31 Commits

Author SHA1 Message Date
DebaucheryLibrarian
be27b325ca Added anal spitroast tag priority. 2026-07-07 04:28:24 +02:00
DebaucheryLibrarian
969c576eb8 1.254.6 2026-07-07 03:40:21 +02:00
DebaucheryLibrarian
66e0076083 Resolving aliased tag media. 2026-07-07 03:40:19 +02:00
DebaucheryLibrarian
1143f0865d Added optional tag alias slug for redirect purposes. 2026-07-07 00:49:21 +02:00
DebaucheryLibrarian
da7e30ff59 1.254.5 2026-07-07 00:45:25 +02:00
DebaucheryLibrarian
242b7d9271 Added rename to tag seed. 2026-07-07 00:45:20 +02:00
DebaucheryLibrarian
b5b87ff82e 1.254.4 2026-07-06 22:29:37 +02:00
DebaucheryLibrarian
99e1efe5c5 Added enhanced and natural boobs tag priorities 2026-07-06 22:29:35 +02:00
DebaucheryLibrarian
6d1f5a9130 Added ass to pussy tags. 2026-07-06 17:20:52 +02:00
DebaucheryLibrarian
e9500f6d0e Removed superfluous media join in biometrics. 2026-07-06 00:26:03 +02:00
DebaucheryLibrarian
db8edf1010 Fixed avatar source in biometrics query. 2026-07-06 00:24:05 +02:00
DebaucheryLibrarian
f538d8b62a Removed superfluous actor_id from biometrics query, should prevent dupes being processed unnecessarily. 2026-07-06 00:20:32 +02:00
DebaucheryLibrarian
e0a89f0b15 Mapped biometric decode error to media ID. 2026-07-06 00:12:27 +02:00
DebaucheryLibrarian
b895ce5a49 1.254.3 2026-07-06 00:09:41 +02:00
DebaucheryLibrarian
b9a8d49063 Biometric time remaining accounting for skipped files. 2026-07-06 00:09:39 +02:00
DebaucheryLibrarian
708c02b410 Tensor dispose optionally chained in case it wasn't set. 2026-07-05 20:57:40 +02:00
DebaucheryLibrarian
c6a3c3aba3 1.254.2 2026-07-05 20:57:03 +02:00
DebaucheryLibrarian
fe2b63878c Catching biometric tensor decode errors. 2026-07-05 20:57:00 +02:00
DebaucheryLibrarian
dd48ef9194 1.254.1 2026-07-05 04:41:38 +02:00
DebaucheryLibrarian
a888acf0b7 Fixed missing slash in biometrics bucket path. 2026-07-05 04:41:36 +02:00
DebaucheryLibrarian
b66ee3095d 1.254.0 2026-07-05 04:02:10 +02:00
DebaucheryLibrarian
525bca255b Added biometrics for avatars. 2026-07-05 04:02:00 +02:00
DebaucheryLibrarian
32a3f876e3 Added abilities seed file to repo. 2026-06-17 00:58:21 +02:00
DebaucheryLibrarian
ccf815b71f 1.253.6 2026-06-17 00:57:48 +02:00
DebaucheryLibrarian
da6b54079f Hashing media in transform in an attempt to improve reliability. Added seed file for user abilities. 2026-06-17 00:57:45 +02:00
DebaucheryLibrarian
43d58ff093 1.253.5 2026-06-09 05:54:11 +02:00
DebaucheryLibrarian
677f72df33 Utilizing allow global match into scene actor association. 2026-06-09 05:54:09 +02:00
DebaucheryLibrarian
18d6832f95 1.253.4 2026-06-09 04:23:16 +02:00
DebaucheryLibrarian
170c42c282 Updated common. 2026-06-09 04:23:11 +02:00
DebaucheryLibrarian
51eafc9a07 1.253.3 2026-06-09 03:19:10 +02:00
DebaucheryLibrarian
01213afd8b Updated common. 2026-06-09 03:19:08 +02:00
13 changed files with 1057 additions and 92 deletions

2
common

Submodule common updated: 1374f90397...ec0812ad9d

View File

@@ -286,6 +286,9 @@ module.exports = {
excludeHostnames: [], excludeHostnames: [],
selectIndex: {}, selectIndex: {},
}, },
biometrics: {
enabled: true,
},
}, },
titleSlugLength: 50, titleSlugLength: 50,
}; };

View File

@@ -76,7 +76,7 @@ exports.up = async (knex) => {
}); });
await knex.schema.createTable('media', (table) => { await knex.schema.createTable('media', (table) => {
table.text('id', 21) table.string('id', 21)
.primary(); .primary();
table.text('path'); table.text('path');

View File

@@ -0,0 +1,35 @@
exports.up = async function(knex) {
await knex.schema.createTable('media_biometrics', (table) => {
table.increments('id');
table.string('media_id', 21)
.references('id')
.inTable('media')
.notNullable()
.onDelete('cascade');
table.integer('width');
table.integer('height');
table.integer('face_index')
.notNullable()
.defaultTo(0);
table.json('biometrics');
table.specificType('embedding', 'vector(1024)');
table.datetime('updated_at')
.notNullable()
.defaultTo(knex.fn.now());
table.datetime('created_at')
.notNullable()
.defaultTo(knex.fn.now());
table.unique(['media_id', 'face_index']);
});
};
exports.down = async function(knex) {
await knex.schema.dropTable('media_biometrics');
};

659
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.253.2", "version": "1.254.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "traxxx", "name": "traxxx",
"version": "1.253.2", "version": "1.254.6",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.458.0", "@aws-sdk/client-s3": "^3.458.0",
@@ -17,6 +17,8 @@
"@graphile-contrib/pg-order-by-related": "^1.0.0", "@graphile-contrib/pg-order-by-related": "^1.0.0",
"@graphile-contrib/pg-simplify-inflector": "^6.1.0", "@graphile-contrib/pg-simplify-inflector": "^6.1.0",
"@graphile/pg-aggregates": "^0.1.1", "@graphile/pg-aggregates": "^0.1.1",
"@tensorflow/tfjs-node": "^4.22.0",
"@vladmandic/human": "^3.3.6",
"acorn": "^8.11.2", "acorn": "^8.11.2",
"array-equal": "^1.0.2", "array-equal": "^1.0.2",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
@@ -25,7 +27,6 @@
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"bottleneck": "^2.19.5", "bottleneck": "^2.19.5",
"canvas": "^2.11.2",
"casual": "^1.6.2", "casual": "^1.6.2",
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"cli-confirm": "^1.0.1", "cli-confirm": "^1.0.1",
@@ -3811,6 +3812,8 @@
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"detect-libc": "^2.0.0", "detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0",
@@ -3830,6 +3833,8 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"agent-base": "6", "agent-base": "6",
"debug": "4" "debug": "4"
@@ -3842,6 +3847,8 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"yallist": "^4.0.0" "yallist": "^4.0.0"
}, },
@@ -3853,6 +3860,8 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"semver": "^6.0.0" "semver": "^6.0.0"
}, },
@@ -3867,6 +3876,8 @@
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"optional": true,
"peer": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
@@ -3875,6 +3886,8 @@
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },
@@ -3894,6 +3907,8 @@
"version": "7.5.4", "version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
}, },
@@ -3907,17 +3922,23 @@
"node_modules/@mapbox/node-pre-gyp/node_modules/tr46": { "node_modules/@mapbox/node-pre-gyp/node_modules/tr46": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"optional": true,
"peer": true
}, },
"node_modules/@mapbox/node-pre-gyp/node_modules/webidl-conversions": { "node_modules/@mapbox/node-pre-gyp/node_modules/webidl-conversions": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"optional": true,
"peer": true
}, },
"node_modules/@mapbox/node-pre-gyp/node_modules/whatwg-url": { "node_modules/@mapbox/node-pre-gyp/node_modules/whatwg-url": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"tr46": "~0.0.3", "tr46": "~0.0.3",
"webidl-conversions": "^3.0.0" "webidl-conversions": "^3.0.0"
@@ -3926,7 +3947,9 @@
"node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"optional": true,
"peer": true
}, },
"node_modules/@nicolo-ribaudo/chokidar-2": { "node_modules/@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents.3", "version": "2.1.8-no-fsevents.3",
@@ -5051,6 +5074,27 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
}, },
"node_modules/@tensorflow/tfjs": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.22.0.tgz",
"integrity": "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg==",
"dependencies": {
"@tensorflow/tfjs-backend-cpu": "4.22.0",
"@tensorflow/tfjs-backend-webgl": "4.22.0",
"@tensorflow/tfjs-converter": "4.22.0",
"@tensorflow/tfjs-core": "4.22.0",
"@tensorflow/tfjs-data": "4.22.0",
"@tensorflow/tfjs-layers": "4.22.0",
"argparse": "^1.0.10",
"chalk": "^4.1.0",
"core-js": "3.29.1",
"regenerator-runtime": "^0.13.5",
"yargs": "^16.0.3"
},
"bin": {
"tfjs-custom-module": "dist/tools/custom_module/cli.js"
}
},
"node_modules/@tensorflow/tfjs-core": { "node_modules/@tensorflow/tfjs-core": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.7.0.tgz", "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.7.0.tgz",
@@ -5075,6 +5119,493 @@
"node": "4.x || >=6.0.0" "node": "4.x || >=6.0.0"
} }
}, },
"node_modules/@tensorflow/tfjs-node": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-4.22.0.tgz",
"integrity": "sha512-uHrXeUlfgkMxTZqHkESSV7zSdKdV0LlsBeblqkuKU9nnfxB1pC6DtoyYVaLxznzZy7WQSegjcohxxCjAf6Dc7w==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "1.0.9",
"@tensorflow/tfjs": "4.22.0",
"adm-zip": "^0.5.2",
"google-protobuf": "^3.9.2",
"https-proxy-agent": "^2.2.1",
"progress": "^2.0.0",
"rimraf": "^2.6.2",
"tar": "^6.2.1"
},
"engines": {
"node": ">=8.11.0"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz",
"integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==",
"dependencies": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/minipass": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^5.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/@mapbox/node-pre-gyp/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/@tensorflow/tfjs-node/node_modules/https-proxy-agent": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dependencies": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
},
"engines": {
"node": ">= 4.5.0"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/https-proxy-agent/node_modules/agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"dependencies": {
"es6-promisify": "^5.0.0"
},
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/https-proxy-agent/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/@tensorflow/tfjs-node/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/@tensorflow/tfjs-node/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/@tensorflow/tfjs-node/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-cpu": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.22.0.tgz",
"integrity": "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==",
"dependencies": {
"@types/seedrandom": "^2.4.28",
"seedrandom": "^3.0.5"
},
"engines": {
"yarn": ">= 1.3.2"
},
"peerDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-webgl": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.22.0.tgz",
"integrity": "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg==",
"dependencies": {
"@tensorflow/tfjs-backend-cpu": "4.22.0",
"@types/offscreencanvas": "~2019.3.0",
"@types/seedrandom": "^2.4.28",
"seedrandom": "^3.0.5"
},
"engines": {
"yarn": ">= 1.3.2"
},
"peerDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-converter": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.22.0.tgz",
"integrity": "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ==",
"peerDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-core": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.22.0.tgz",
"integrity": "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A==",
"dependencies": {
"@types/long": "^4.0.1",
"@types/offscreencanvas": "~2019.7.0",
"@types/seedrandom": "^2.4.28",
"@webgpu/types": "0.1.38",
"long": "4.0.0",
"node-fetch": "~2.6.1",
"seedrandom": "^3.0.5"
},
"engines": {
"yarn": ">= 1.3.2"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": {
"version": "2019.7.3",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-data": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.22.0.tgz",
"integrity": "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w==",
"dependencies": {
"@types/node-fetch": "^2.1.2",
"node-fetch": "~2.6.1",
"string_decoder": "^1.3.0"
},
"peerDependencies": {
"@tensorflow/tfjs-core": "4.22.0",
"seedrandom": "^3.0.5"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-layers": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.22.0.tgz",
"integrity": "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA==",
"peerDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/@types/seedrandom": {
"version": "2.4.34",
"resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz",
"integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A=="
},
"node_modules/@tensorflow/tfjs/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/@tensorflow/tfjs/node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dependencies": {
"sprintf-js": "~1.0.2"
}
},
"node_modules/@tensorflow/tfjs/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@tensorflow/tfjs/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/@tensorflow/tfjs/node_modules/core-js": {
"version": "3.29.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz",
"integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/@tensorflow/tfjs/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/@tensorflow/tfjs/node_modules/node-fetch": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
"integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/@tensorflow/tfjs/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/@tensorflow/tfjs/node_modules/seedrandom": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
},
"node_modules/@tensorflow/tfjs/node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
},
"node_modules/@tensorflow/tfjs/node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@tensorflow/tfjs/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/@tensorflow/tfjs/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/@tensorflow/tfjs/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/@tensorflow/tfjs/node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/@tensorflow/tfjs/node_modules/yargs": {
"version": "16.2.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
"integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@tokenizer/token": { "node_modules/@tokenizer/token": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
@@ -5180,6 +5711,11 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
},
"node_modules/@types/minimist": { "node_modules/@types/minimist": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
@@ -5199,6 +5735,15 @@
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
}, },
"node_modules/@types/node-fetch": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz",
"integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.4"
}
},
"node_modules/@types/normalize-package-data": { "node_modules/@types/normalize-package-data": {
"version": "2.4.4", "version": "2.4.4",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
@@ -5446,6 +5991,15 @@
"is-function": "^1.0.1" "is-function": "^1.0.1"
} }
}, },
"node_modules/@vladmandic/human": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/@vladmandic/human/-/human-3.3.6.tgz",
"integrity": "sha512-Nr5mPfq1gQ+uKeXY5uM3Fj8UxvF5CEh2s6txM5wRThqaW0mF9huooOuZSrT8hhGho0hGaXLFdAwfD/2+teCv6A==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.5.25", "version": "3.5.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
@@ -5688,6 +6242,11 @@
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"node_modules/@webgpu/types": {
"version": "0.1.38",
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.38.tgz",
"integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA=="
},
"node_modules/@webpack-cli/configtest": { "node_modules/@webpack-cli/configtest": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
@@ -5831,6 +6390,14 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/adm-zip": {
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
"integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==",
"engines": {
"node": ">=12.0"
}
},
"node_modules/aes-decrypter": { "node_modules/aes-decrypter": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-4.0.1.tgz", "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-4.0.1.tgz",
@@ -7209,6 +7776,8 @@
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
"hasInstallScript": true, "hasInstallScript": true,
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0", "@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.17.0", "nan": "^2.17.0",
@@ -8249,6 +8818,8 @@
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"mimic-response": "^2.0.0" "mimic-response": "^2.0.0"
}, },
@@ -8815,13 +9386,14 @@
} }
}, },
"node_modules/es-set-tostringtag": { "node_modules/es-set-tostringtag": {
"version": "2.0.2", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dependencies": { "dependencies": {
"get-intrinsic": "^1.2.2", "es-errors": "^1.3.0",
"has-tostringtag": "^1.0.0", "get-intrinsic": "^1.2.6",
"hasown": "^2.0.0" "has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -8851,6 +9423,19 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"node_modules/es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
"dependencies": {
"es6-promise": "^4.0.3"
}
},
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -10250,13 +10835,15 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.0", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"mime-types": "^2.1.12" "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.4",
"mime-types": "^2.1.35"
}, },
"engines": { "engines": {
"node": ">= 6" "node": ">= 6"
@@ -10733,6 +11320,11 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/google-protobuf": {
"version": "3.21.4",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz",
"integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ=="
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -10984,11 +11576,11 @@
} }
}, },
"node_modules/has-tostringtag": { "node_modules/has-tostringtag": {
"version": "1.0.0", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dependencies": { "dependencies": {
"has-symbols": "^1.0.2" "has-symbols": "^1.0.3"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -11009,9 +11601,9 @@
"dev": true "dev": true
}, },
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.2", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"dependencies": { "dependencies": {
"function-bind": "^1.1.2" "function-bind": "^1.1.2"
}, },
@@ -13030,6 +13622,11 @@
"node": ">= 12.0.0" "node": ">= 12.0.0"
} }
}, },
"node_modules/long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"node_modules/longjohn": { "node_modules/longjohn": {
"version": "0.2.12", "version": "0.2.12",
"resolved": "https://registry.npmjs.org/longjohn/-/longjohn-0.2.12.tgz", "resolved": "https://registry.npmjs.org/longjohn/-/longjohn-0.2.12.tgz",
@@ -13422,6 +14019,8 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"optional": true,
"peer": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
}, },
@@ -19475,12 +20074,16 @@
"type": "consulting", "type": "consulting",
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
] ],
"optional": true,
"peer": true
}, },
"node_modules/simple-get": { "node_modules/simple-get": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"decompress-response": "^4.2.0", "decompress-response": "^4.2.0",
"once": "^1.3.1", "once": "^1.3.1",
@@ -20130,9 +20733,10 @@
} }
}, },
"node_modules/tar": { "node_modules/tar": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dependencies": { "dependencies": {
"chownr": "^2.0.0", "chownr": "^2.0.0",
"fs-minipass": "^2.0.0", "fs-minipass": "^2.0.0",
@@ -22729,7 +23333,6 @@
"version": "20.2.9", "version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "traxxx", "name": "traxxx",
"version": "1.253.2", "version": "1.254.6",
"description": "All the latest porn releases in one place", "description": "All the latest porn releases in one place",
"main": "src/app.js", "main": "src/app.js",
"scripts": { "scripts": {
@@ -76,6 +76,8 @@
"@graphile-contrib/pg-order-by-related": "^1.0.0", "@graphile-contrib/pg-order-by-related": "^1.0.0",
"@graphile-contrib/pg-simplify-inflector": "^6.1.0", "@graphile-contrib/pg-simplify-inflector": "^6.1.0",
"@graphile/pg-aggregates": "^0.1.1", "@graphile/pg-aggregates": "^0.1.1",
"@tensorflow/tfjs-node": "^4.22.0",
"@vladmandic/human": "^3.3.6",
"acorn": "^8.11.2", "acorn": "^8.11.2",
"array-equal": "^1.0.2", "array-equal": "^1.0.2",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
@@ -84,7 +86,6 @@
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"bottleneck": "^2.19.5", "bottleneck": "^2.19.5",
"canvas": "^2.11.2",
"casual": "^1.6.2", "casual": "^1.6.2",
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"cli-confirm": "^1.0.1", "cli-confirm": "^1.0.1",

View File

@@ -114,6 +114,7 @@ const tags = [
{ {
name: 'anal', name: 'anal',
slug: 'anal', slug: 'anal',
rename: 'ass-fucking',
description: 'Getting your asshole fucked. Generally considered naughtier, you may or may not find it a pleasurable alternative to vaginal sex.', description: 'Getting your asshole fucked. Generally considered naughtier, you may or may not find it a pleasurable alternative to vaginal sex.',
group: 'penetration', group: 'penetration',
}, },
@@ -160,8 +161,19 @@ const tags = [
name: 'ass to other girl\'s mouth', name: 'ass to other girl\'s mouth',
slug: 'atogm', slug: 'atogm',
description: '[Ass to mouth](/tag/atm) with a cock that has been in someone else\'s ass. ATOGM may also be the gay variation "ass to other guy\'s mouth".', description: '[Ass to mouth](/tag/atm) with a cock that has been in someone else\'s ass. ATOGM may also be the gay variation "ass to other guy\'s mouth".',
implies: ['atm'],
group: 'oral', group: 'oral',
}, },
{
name: 'ass to pussy',
slug: 'atp',
description: 'Getting your pussy fucked right after it\'s been in your ass, or [someone else\'s](/tag/atogp).',
},
{
name: 'ass to other girl\'s pussy',
slug: 'atogp',
description: 'Getting your pussy fucked right after it\'s been in someone else\'s ass.',
},
{ {
name: 'ass eating', name: 'ass eating',
slug: 'ass-eating', slug: 'ass-eating',
@@ -311,6 +323,10 @@ const tags = [
name: 'bondage', name: 'bondage',
slug: 'bondage', slug: 'bondage',
}, },
{
name: 'bimbo',
slug: 'bimbo',
},
{ {
name: 'braces', name: 'braces',
slug: 'braces', slug: 'braces',
@@ -544,8 +560,10 @@ const tags = [
slug: 'electric-shock', slug: 'electric-shock',
}, },
{ {
name: 'enhanced boobs', name: 'fake tits',
slug: 'enhanced-boobs', slug: 'fake-tits',
description: 'Tasteful enhancements or bimbo bolt-ons, you don\'t mind a bit of plastic to get the boobs you\'ve always wanted.',
rename: 'enhanced-boobs',
}, },
{ {
name: 'enhanced butt', name: 'enhanced butt',
@@ -832,13 +850,18 @@ const tags = [
name: 'MFM threesome', name: 'MFM threesome',
slug: 'mfm', slug: 'mfm',
implies: ['threesome'], implies: ['threesome'],
description: 'Two men fucking one woman, but not eachother. Typically involves a \'spitroast\', where one guy gets a blowjob and the other fucks her pussy or ass.', description: 'Two men fucking one woman, but not eachother. Typically involves a [spitroast](/tag/spitroast), where one guy gets a blowjob and the other fucks her pussy or ass.',
group: 'group', group: 'group',
}, },
{ {
name: 'spitroast', name: 'spitroast',
slug: 'spitroast', slug: 'spitroast',
}, },
{
name: 'anal spitroast',
slug: 'anal-spitroast',
implies: ['anal', 'spitroast'],
},
{ {
name: 'military', name: 'military',
slug: 'military', slug: 'military',
@@ -858,8 +881,9 @@ const tags = [
group: 'position', group: 'position',
}, },
{ {
name: 'natural boobs', name: 'natural tits',
slug: 'natural-boobs', slug: 'natural-tits',
rename: 'natural-boobs',
group: 'body', group: 'body',
}, },
{ {
@@ -1641,15 +1665,18 @@ const aliases = [
name: 'big butts', name: 'big butts',
for: 'big-butt', for: 'big-butt',
}, },
{
name: 'natural boobs',
for: 'natural-tits',
slug: 'natural-boobs',
},
{ {
name: 'big natural tits', name: 'big natural tits',
for: 'big-boobs', for: 'big-boobs',
implies: ['natural-boobs'],
}, },
{ {
name: 'big natural boobs', name: 'big natural boobs',
for: 'big-boobs', for: 'big-boobs',
implies: ['natural-boobs'],
}, },
{ {
name: 'big tits', name: 'big tits',
@@ -1729,12 +1756,18 @@ const aliases = [
}, },
{ {
name: 'fake boobs', name: 'fake boobs',
for: 'enhanced-boobs', for: 'fake-tits',
secondary: true,
},
{
name: 'enhanced boobs',
for: 'fake-tits',
slug: 'enhanced-boobs',
secondary: true, secondary: true,
}, },
{ {
name: 'implants', name: 'implants',
for: 'enhanced-boobs', for: 'fake-tits',
}, },
{ {
name: 'boob job', name: 'boob job',
@@ -1742,7 +1775,7 @@ const aliases = [
}, },
{ {
name: 'boobjob', name: 'boobjob',
for: 'enhanced-boobs', for: 'fake-tits',
}, },
{ {
name: 'brown hair', name: 'brown hair',
@@ -2007,11 +2040,11 @@ const aliases = [
}, },
{ {
name: 'enhanced', name: 'enhanced',
for: 'enhanced-boobs', for: 'fake-tits',
}, },
{ {
name: 'enhanced tits', name: 'enhanced tits',
for: 'enhanced-boobs', for: 'fake-tits',
}, },
{ {
name: 'fake butt', name: 'fake butt',
@@ -2054,11 +2087,6 @@ const aliases = [
name: 'facial - multiple', name: 'facial - multiple',
for: 'facial', for: 'facial',
}, },
{
name: 'fake tits',
for: 'enhanced-boobs',
secondary: true,
},
{ {
name: 'feet sex', name: 'feet sex',
for: 'foot-sex', for: 'foot-sex',
@@ -2208,11 +2236,7 @@ const aliases = [
}, },
{ {
name: 'natural', name: 'natural',
for: 'natural-boobs', for: 'natural-tits',
},
{
name: 'natural tits',
for: 'natural-boobs',
}, },
{ {
name: 'natural butt', name: 'natural butt',
@@ -3097,17 +3121,19 @@ const aliases = [
]; ];
const priorities = [ // higher index is higher priority const priorities = [ // higher index is higher priority
['double-dildo', 'double-dildo-blowjob', 'double-dildo-kiss', 'double-dildo-anal', 'double-dildo-dp'], ['blonde', 'brunette', 'black-hair', 'redhead'],
['toys', 'toy-anal', 'toy-dp', 'piss-drinking'],
['family'],
['blowjob', 'deepthroat', 'oil'],
['asian', 'black', 'latina', 'white', 'interracial'], ['asian', 'black', 'latina', 'white', 'interracial'],
['fake-tits', 'natural-tits'],
['bts'], ['bts'],
['blowjob', 'deepthroat', 'oil'],
['toys', 'toy-anal', 'toy-dp', 'piss-drinking'],
['double-dildo', 'double-dildo-blowjob', 'double-dildo-kiss', 'double-dildo-anal', 'double-dildo-dp'],
['family'],
['facefucking', 'gaping', 'atm', 'atogm', 'pussy-to-mouth', 'ass-eating'], ['facefucking', 'gaping', 'atm', 'atogm', 'pussy-to-mouth', 'ass-eating'],
['facial', 'swallowing', 'creampie', 'anal-creampie', 'oral-creampie', 'cum-in-mouth', 'throatpie'], ['facial', 'swallowing', 'creampie', 'anal-creampie', 'oral-creampie', 'cum-in-mouth', 'throatpie'],
['lesbian', 'rough', 'milf', 'male-focus', 'bdsm', 'oil'], ['lesbian', 'rough', 'milf', 'male-focus', 'bdsm', 'oil'],
['threesome', 'mfm', 'mff', 'trainbang', 'pissing'], ['threesome', 'mfm', 'mff', 'trainbang', 'pissing'],
['anal', 'bukkake', 'spitroast'], ['anal', 'bukkake', 'spitroast', 'anal-spitroast'],
['dp', 'dap', 'triple-penetration', 'tap', 'dvp', 'tvp', 'airtight'], ['dp', 'dap', 'triple-penetration', 'tap', 'dvp', 'tvp', 'airtight'],
['blowbang', 'orgy'], ['blowbang', 'orgy'],
['gangbang'], ['gangbang'],
@@ -3123,6 +3149,33 @@ const priorities = [ // higher index is higher priority
exports.seed = (knex) => Promise.resolve() exports.seed = (knex) => Promise.resolve()
.then(async () => upsert('tags_groups', groups, 'slug', knex)) .then(async () => upsert('tags_groups', groups, 'slug', knex))
.then(async () => { .then(async () => {
await Promise.all(tags.map((async (tag) => {
if (tag.rename) {
await knex('tags')
.where((builder) => {
builder
.where('slug', tag.slug)
.orWhere('name', tag.name);
})
.whereNotNull('alias_for')
.delete();
await knex('tags')
.where('slug', tag.rename)
.whereNull('alias_for')
.update({
name: tag.name,
slug: tag.slug,
});
}
if (tag.delete) {
await knex('tags')
.where('slug', tag.rename)
.delete();
}
})));
const groupEntries = await knex('tags_groups').select('*'); const groupEntries = await knex('tags_groups').select('*');
const groupsMap = groupEntries.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); const groupsMap = groupEntries.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {});
@@ -3170,6 +3223,7 @@ exports.seed = (knex) => Promise.resolve()
return { return {
name: alias.name, name: alias.name,
slug: alias.slug, // only used if redirect is required
alias_for: tagsMap[alias.for], alias_for: tagsMap[alias.for],
implied_tag_ids: alias.implies?.map((slug) => tagsMap[slug]), implied_tag_ids: alias.implies?.map((slug) => tagsMap[slug]),
secondary: !!alias.secondary, secondary: !!alias.secondary,

View File

@@ -1144,7 +1144,7 @@ exports.seed = (knex) => Promise.resolve()
entity_id: entitiesBySlug[media.entitySlug]?.id, entity_id: entitiesBySlug[media.entitySlug]?.id,
})), 'path', knex); })), 'path', knex);
const tagIdsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag.id }), {}); const tagIdsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag.alias_for || tag.id }), {});
const mediaIdsByPath = inserted.concat(updated).reduce((acc, item) => ({ ...acc, [item.path]: item.id }), {}); const mediaIdsByPath = inserted.concat(updated).reduce((acc, item) => ({ ...acc, [item.path]: item.id }), {});
const tagMediaBySlug = tagMedia.reduce((acc, tagPhoto) => ({ const tagMediaBySlug = tagMedia.reduce((acc, tagPhoto) => ({

28
seeds/08_abilities.js Normal file
View File

@@ -0,0 +1,28 @@
exports.seed = async (knex) => {
await knex('users_roles')
.update('abilities', JSON.stringify([
{ subject: 'scene', action: 'create' },
{ subject: 'scene', action: 'update' },
{ subject: 'scene', action: 'delete' },
{ subject: 'actor', action: 'create' },
{ subject: 'actor', action: 'update' },
{ subject: 'actor', action: 'delete' },
{ subject: 'actor', action: 'merge' },
{ subject: 'sync' },
{ subject: 'plainUrls' },
]))
.where('role', 'admin');
await knex('users_roles')
.update('abilities', JSON.stringify([
{ subject: 'scene', action: 'create' },
{ subject: 'scene', action: 'update' },
{ subject: 'scene', action: 'delete' },
{ subject: 'actor', action: 'create' },
{ subject: 'actor', action: 'update' },
{ subject: 'actor', action: 'delete' },
{ subject: 'actor', action: 'merge' },
{ subject: 'plainUrls' },
]))
.where('role', 'editor');
};

View File

@@ -31,6 +31,7 @@ const { associateAvatars, flushOrphanedMedia } = require('./media');
const { fetchEntitiesBySlug } = require('./entities'); const { fetchEntitiesBySlug } = require('./entities');
const { deleteScenes } = require('./releases'); const { deleteScenes } = require('./releases');
const { updateActorSearch } = require('./update-search'); const { updateActorSearch } = require('./update-search');
const { setBiometrics } = require('./biometrics');
const actorsCommon = import('../common/actors.mjs'); // eslint-disable-line import/extensions, import/no-relative-packages const actorsCommon = import('../common/actors.mjs'); // eslint-disable-line import/extensions, import/no-relative-packages
const geoCommon = import('../common/geo.mjs'); // eslint-disable-line import/extensions, import/no-relative-packages const geoCommon = import('../common/geo.mjs'); // eslint-disable-line import/extensions, import/no-relative-packages
@@ -824,6 +825,11 @@ async function storeProfiles(profiles) {
await upsertProfiles(profilesWithAvatarIds); await upsertProfiles(profilesWithAvatarIds);
await interpolateProfiles(actorIds); await interpolateProfiles(actorIds);
setBiometrics(Array.from(new Set(profiles.map((profile) => profile.actorId)))).catch((error) => {
// biometrics are courtesy, don't await
logger.error(`Biometrics failed: ${error.message}`);
});
} }
async function scrapeActors(argNames) { async function scrapeActors(argNames) {
@@ -913,7 +919,7 @@ async function getOrCreateActors(baseActors, batchId) {
.whereRaw(` .whereRaw(`
actors.slug = base_actors.slug actors.slug = base_actors.slug
AND actors.entity_id IS NULL AND actors.entity_id IS NULL
AND NOT base_actors.collision_likely AND (NOT base_actors.collision_likely OR actors.allow_global_match)
`) `)
.orWhereRaw(` .orWhereRaw(`
actors.slug = base_actors.slug actors.slug = base_actors.slug
@@ -981,7 +987,7 @@ async function getOrCreateActors(baseActors, batchId) {
await storeProfiles(newActorProfiles); await storeProfiles(newActorProfiles);
if (Array.isArray(newActors)) { if (Array.isArray(newActors) && newActors.length > 0) {
await updateActorSearch(newActors.map((actor) => actor.id)); await updateActorSearch(newActors.map((actor) => actor.id));
return newActors.concat(existingActors); return newActors.concat(existingActors);
} }

213
src/biometrics.js Normal file
View File

@@ -0,0 +1,213 @@
'use strict';
const config = require('config');
const path = require('path');
const fsPromises = require('fs').promises;
const Human = require('@vladmandic/human').default;
const knex = require('./knex');
const logger = require('./logger')(__filename);
const humanConfig = {
modelBasePath: `file://${path.join(__dirname, '../node_modules/@vladmandic/human/models')}/`,
backend: 'tensorflow', // uses tfjs-node under the hood
face: {
enabled: true,
detector: { rotation: false },
mesh: { enabled: false },
iris: { enabled: false },
description: { enabled: true },
emotion: { enabled: true },
},
body: { enabled: false },
hand: { enabled: false },
gesture: { enabled: false },
};
let humanInstance = null;
const nsPerSec = 1e9;
const remainingTimeWindow = 30; // number of recent entries used to estimate time remaining
async function getHumanInstance() {
if (!humanInstance) {
humanInstance = new Human(humanConfig);
await humanInstance.load();
await humanInstance.warmup();
}
return humanInstance;
}
async function getImageBuffer(mediaEntry) {
if (mediaEntry.is_s3) {
if (!config.s3.enabled) {
logger.verbose(`Skipping biometrics for ${mediaEntry.media_id}, S3 not enabled`);
return null;
}
const res = await fetch(`https://${config.s3.bucket}/${mediaEntry.path}`);
if (res.ok) {
return Buffer.from(await res.arrayBuffer());
}
return null;
}
try {
const buffer = await fsPromises.readFile(path.join(config.media.path, mediaEntry.path));
return buffer;
} catch (_error) {
return null;
}
}
async function getBiometrics(avatarEntry) {
if (!avatarEntry) {
return null;
}
const human = await getHumanInstance();
const buffer = await getImageBuffer(avatarEntry);
if (!buffer) {
return null;
}
let tensor;
try {
tensor = human.tf.node.decodeImage(buffer, 3);
const result = await human.detect(tensor);
return result;
} catch (error) {
logger.error(`Failed to decode or detect ${avatarEntry.media_id}: ${error.message}`);
return null;
} finally {
tensor?.dispose();
}
}
function getAvatarSource(actorIds, includePhotos) {
if (includePhotos) {
return knex('actors_avatars')
.select('media_id')
.modify((builder) => {
if (actorIds) {
builder.whereIn('actor_id', actorIds);
}
});
}
return knex('actors')
.select('avatar_media_id as media_id')
.whereNotNull('avatar_media_id')
.modify((builder) => {
if (actorIds) {
builder.whereIn('id', actorIds);
}
});
}
async function setBiometrics(actorIds, shouldUpdate = false, includePhotos = false) {
if (!config.media.biometrics.enabled) {
return;
}
const startTime = process.hrtime();
const recentDurationsMs = [];
const avatarSource = getAvatarSource(actorIds, includePhotos);
const avatarEntries = await knex('media')
.select(
'media.id as media_id',
'media.path',
'media.is_s3',
knex.raw('coalesce(json_agg(media_biometrics.biometrics) filter (where media_biometrics.id is not null), \'[]\') as biometrics'),
)
.leftJoin('media_biometrics', 'media_biometrics.media_id', 'media.id')
.whereIn('media.id', avatarSource)
.groupBy('media.id', 'media.path', 'media.is_s3');
await avatarEntries.reduce(async (chain, avatarEntry, avatarIndex) => {
await chain;
if (avatarEntry.biometrics.length > 0 && !shouldUpdate) {
logger.verbose(`Skipping biometrics for ${avatarEntry.media_id}, already present`);
return;
}
const entryStartTime = process.hrtime();
const biometrics = await getBiometrics(avatarEntry);
if (!biometrics) {
return;
}
const curatedBiometrics = biometrics.face.map((face) => ({
biometrics: {
age: face.age,
gender: face.gender,
genderScore: face.genderScore,
emotion: face.emotion,
box: face.box,
score: face.score,
...Object.fromEntries(Object.entries(face.annotations).map(([key, annotation]) => [key, annotation.filter(Boolean)[0] || null])),
},
embedding: face.embedding,
}));
if (curatedBiometrics.length === 0) {
return;
}
if (shouldUpdate) {
await knex('media_biometrics')
.where('media_id', avatarEntry.media_id)
.delete();
}
await knex('media_biometrics')
.insert(curatedBiometrics.map((face, index) => ({
media_id: avatarEntry.media_id,
face_index: index,
width: biometrics.width,
height: biometrics.height,
biometrics: JSON.stringify(face.biometrics),
embedding: knex.raw('?::vector', [JSON.stringify(face.embedding)]),
updated_at: knex.raw('now()'),
})))
.onConflict(['media_id', 'face_index'])
.ignore();
const [entrySec, entryNs] = process.hrtime(entryStartTime);
const entryMs = (entrySec * nsPerSec + entryNs) / 1e6;
recentDurationsMs.push(entryMs);
if (recentDurationsMs.length > remainingTimeWindow) {
recentDurationsMs.shift();
}
const avgMsPerEntry = recentDurationsMs.reduce((sum, ms) => sum + ms, 0) / recentDurationsMs.length;
const remainingMs = avgMsPerEntry * (avatarEntries.length - (avatarIndex + 1));
const remainingMinutes = (remainingMs / 1000 / 60).toFixed(1);
logger.info(`Set biometrics for ${avatarEntry.media_id} (${avatarIndex + 1}/${avatarEntries.length}, ~${remainingMinutes}m remaining)`);
}, Promise.resolve());
const diffTime = process.hrtime(startTime);
const totalMs = (diffTime[0] * nsPerSec + diffTime[1]) / 1e6;
logger.info(`Biometrics done in ${(totalMs / 1000).toFixed(1)}s for ${actorIds?.length || 'all'} actors and ${avatarEntries.length} avatars`);
}
module.exports = {
setBiometrics,
};

View File

@@ -28,7 +28,6 @@ const chunk = require('./utils/chunk');
const { get } = require('./utils/qu'); const { get } = require('./utils/qu');
const { fetchEntityReleaseIds } = require('./entity-releases'); const { fetchEntityReleaseIds } = require('./entity-releases');
// const pipeline = util.promisify(stream.pipeline);
const streamQueue = taskQueue(); const streamQueue = taskQueue();
const s3 = new S3Client({ const s3 = new S3Client({
@@ -516,16 +515,6 @@ async function storeImageFile(media, hashDir, hashSubDir, filename, filedir, fil
writeLazy(image, lazypath, info), writeLazy(image, lazypath, info),
]); ]);
/*
if (isProcessed) {
// file already stored, remove temporary file
await fsPromises.unlink(media.file.path);
} else {
// image not processed, simply move temporary file to final location
await fsPromises.rename(media.file.path, path.join(config.media.path, filepath));
}
*/
await fsPromises.unlink(media.file.path); await fsPromises.unlink(media.file.path);
if (config.s3.enabled) { if (config.s3.enabled) {
@@ -731,31 +720,31 @@ async function fetchSource(source, baseMedia) {
} }
async function attempt(attempts = 1) { async function attempt(attempts = 1) {
const hasher = new blake2.Hash('blake2b', { digestLength: 24 }); const tempFilePath = path.join(config.media.path, 'temp', `${baseMedia.id}`);
let hasherReady = true; let tempFileTarget;
hasher.setEncoding('hex');
const hasher = blake2.createHash('blake2b', { digestLength: 24 });
let size = 0;
const hashStream = new stream.Transform({
transform(streamChunk, _encoding, callback) {
size += streamChunk.length;
hasher.update(streamChunk);
this.push(streamChunk);
callback();
},
});
try { try {
const tempFilePath = path.join(config.media.path, 'temp', `${baseMedia.id}`); tempFileTarget = fs.createWriteStream(tempFilePath);
const tempFileTarget = fs.createWriteStream(tempFilePath);
const hashStream = new stream.PassThrough();
let size = 0;
hashStream.on('data', (streamChunk) => {
size += streamChunk.length;
if (hasherReady) {
hasher.write(streamChunk);
}
});
const { mimetype } = source.stream const { mimetype } = source.stream
? await streamQueue.push('fetchStreamSource', { source, tempFileTarget, hashStream }) ? await streamQueue.push('fetchStreamSource', { source, tempFileTarget, hashStream })
: await fetchHttpSource(source, tempFileTarget, hashStream); : await fetchHttpSource(source, tempFileTarget, hashStream);
hasher.end(); const hash = hasher.digest('hex');
const hash = hasher.read();
const [type, subtype] = mimetype.split('/'); const [type, subtype] = mimetype.split('/');
const extension = mime.getExtension(mimetype); const extension = mime.getExtension(mimetype);
@@ -778,8 +767,10 @@ async function fetchSource(source, baseMedia) {
}, },
}; };
} catch (error) { } catch (error) {
hasherReady = false; // hasherReady = false;
hasher.end(); // hasher.end();
hashStream.destroy();
tempFileTarget?.destroy();
if (error.code !== 'VERIFY_TYPE') { if (error.code !== 'VERIFY_TYPE') {
logger.warn(`Failed attempt ${attempts}/${maxAttempts} to fetch ${source.src}: ${error.message}`); logger.warn(`Failed attempt ${attempts}/${maxAttempts} to fetch ${source.src}: ${error.message}`);

31
src/tools/biometrics.js Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const knex = require('../knex');
const { setBiometrics } = require('../biometrics');
const argv = yargs(hideBin(process.argv))
.option('actor-ids', {
alias: 'actors',
type: 'array',
describe: 'List of actor IDs to derive biometrics for',
})
.option('update', {
alias: 'force',
type: 'boolean',
describe: 'Re-compute biometrics when already present',
})
.option('photos', {
type: 'boolean',
describe: 'Include secondary actor photos',
})
.argv;
async function init() {
await setBiometrics(argv.actorIds, argv.update, argv.photos);
knex.destroy();
}
init();