Compare commits
52 Commits
2ef1ef80e4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3a2de2e04 | ||
|
|
56b18618be | ||
|
|
a68c8539fd | ||
|
|
a3ae82dcd3 | ||
|
|
c3dad0b523 | ||
|
|
25af9152d7 | ||
|
|
8060be166b | ||
|
|
46fcd1465a | ||
|
|
42027d0856 | ||
|
|
70d4a05aeb | ||
|
|
b2941940e3 | ||
|
|
270cc36e65 | ||
|
|
6fa03081ef | ||
|
|
4a60cadb4b | ||
|
|
d4db2ee1e4 | ||
|
|
76e6e60d69 | ||
|
|
7aa7b9bef1 | ||
|
|
04d1e340f5 | ||
|
|
a5c91ad816 | ||
|
|
35c17d32bb | ||
|
|
bfb7c8b080 | ||
|
|
be27b325ca | ||
|
|
969c576eb8 | ||
|
|
66e0076083 | ||
|
|
1143f0865d | ||
|
|
da7e30ff59 | ||
|
|
242b7d9271 | ||
|
|
b5b87ff82e | ||
|
|
99e1efe5c5 | ||
|
|
6d1f5a9130 | ||
|
|
e9500f6d0e | ||
|
|
db8edf1010 | ||
|
|
f538d8b62a | ||
|
|
e0a89f0b15 | ||
|
|
b895ce5a49 | ||
|
|
b9a8d49063 | ||
|
|
708c02b410 | ||
|
|
c6a3c3aba3 | ||
|
|
fe2b63878c | ||
|
|
dd48ef9194 | ||
|
|
a888acf0b7 | ||
|
|
b66ee3095d | ||
|
|
525bca255b | ||
|
|
32a3f876e3 | ||
|
|
ccf815b71f | ||
|
|
da6b54079f | ||
|
|
43d58ff093 | ||
|
|
677f72df33 | ||
|
|
18d6832f95 | ||
|
|
170c42c282 | ||
|
|
51eafc9a07 | ||
|
|
01213afd8b |
2
common
2
common
Submodule common updated: 1374f90397...ec0812ad9d
@@ -181,6 +181,8 @@ module.exports = {
|
|||||||
'traxxx',
|
'traxxx',
|
||||||
// porn doe
|
// porn doe
|
||||||
'forbondage',
|
'forbondage',
|
||||||
|
// prevent huge influx
|
||||||
|
'manyvids',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
profiles: null,
|
profiles: null,
|
||||||
@@ -286,6 +288,9 @@ module.exports = {
|
|||||||
excludeHostnames: [],
|
excludeHostnames: [],
|
||||||
selectIndex: {},
|
selectIndex: {},
|
||||||
},
|
},
|
||||||
|
biometrics: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
titleSlugLength: 50,
|
titleSlugLength: 50,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
35
migrations/20260704023524_biometrics.js
Normal file
35
migrations/20260704023524_biometrics.js
Normal 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');
|
||||||
|
};
|
||||||
665
package-lock.json
generated
665
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.253.2",
|
"version": "1.254.15",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.253.2",
|
"version": "1.254.15",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.458.0",
|
"@aws-sdk/client-s3": "^3.458.0",
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
"@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",
|
||||||
|
"@rsc-parser/react-client": "^1.1.2",
|
||||||
|
"@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 +28,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 +3813,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 +3834,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 +3848,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 +3861,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 +3877,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 +3887,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 +3908,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 +3923,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 +3948,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",
|
||||||
@@ -4181,6 +4205,11 @@
|
|||||||
"@redis/client": "^1.0.0"
|
"@redis/client": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rsc-parser/react-client": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rsc-parser/react-client/-/react-client-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-IXW7ViicNACwI1vxAQgndtRKZlM9HgGpxSZdqnXn8fCY2VvUkqas03WuHv4LnVlCdC3HGwhbh3r3TBLxRrYSgw=="
|
||||||
|
},
|
||||||
"node_modules/@sinclair/typebox": {
|
"node_modules/@sinclair/typebox": {
|
||||||
"version": "0.27.8",
|
"version": "0.27.8",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||||
@@ -5051,6 +5080,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 +5125,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 +5717,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 +5741,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 +5997,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 +6248,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 +6396,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 +7782,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 +8824,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 +9392,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 +9429,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 +10841,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 +11326,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 +11582,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 +11607,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 +13628,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 +14025,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 +20080,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 +20739,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 +23339,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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.253.2",
|
"version": "1.254.15",
|
||||||
"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,9 @@
|
|||||||
"@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",
|
||||||
|
"@rsc-parser/react-client": "^1.1.2",
|
||||||
|
"@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 +87,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",
|
||||||
|
|||||||
125
seeds/00_tags.js
125
seeds/00_tags.js
@@ -95,6 +95,14 @@ const tags = [
|
|||||||
slug: '69',
|
slug: '69',
|
||||||
group: 'position',
|
group: 'position',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'AI generated',
|
||||||
|
slug: 'ai',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AI enhanced',
|
||||||
|
slug: 'aienhanced',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'airtight',
|
name: 'airtight',
|
||||||
slug: 'airtight',
|
slug: 'airtight',
|
||||||
@@ -114,6 +122,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 +169,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 +331,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 +568,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',
|
||||||
@@ -736,6 +762,11 @@ const tags = [
|
|||||||
slug: 'high-heels',
|
slug: 'high-heels',
|
||||||
group: 'clothing',
|
group: 'clothing',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'hotel',
|
||||||
|
slug: 'hotel',
|
||||||
|
group: 'location',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'humiliation',
|
name: 'humiliation',
|
||||||
slug: 'humiliation',
|
slug: 'humiliation',
|
||||||
@@ -832,13 +863,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 +894,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',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1227,6 +1264,10 @@ const tags = [
|
|||||||
slug: 'toy-dp',
|
slug: 'toy-dp',
|
||||||
description: 'Getting [double penetrated](/tag/dp) with dildos, strap-ons or buttplugs. You can use both ends of a [double dildo](/tag/double-dildo) for a [double dildo DP](/tag/double-dildo-dp).',
|
description: 'Getting [double penetrated](/tag/dp) with dildos, strap-ons or buttplugs. You can use both ends of a [double dildo](/tag/double-dildo) for a [double dildo DP](/tag/double-dildo-dp).',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'finger DP',
|
||||||
|
slug: 'finger-dp',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'transsexual',
|
name: 'transsexual',
|
||||||
slug: 'transsexual',
|
slug: 'transsexual',
|
||||||
@@ -1641,15 +1682,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 +1773,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 +1792,7 @@ const aliases = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'boobjob',
|
name: 'boobjob',
|
||||||
for: 'enhanced-boobs',
|
for: 'fake-tits',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'brown hair',
|
name: 'brown hair',
|
||||||
@@ -2007,11 +2057,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 +2104,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 +2253,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 +3138,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 +3166,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 +3240,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,
|
||||||
|
|||||||
@@ -621,6 +621,11 @@ const networks = [
|
|||||||
url: 'https://mamacitaz.com',
|
url: 'https://mamacitaz.com',
|
||||||
parent: 'porndoe',
|
parent: 'porndoe',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slug: 'manyvids',
|
||||||
|
name: 'ManyVids',
|
||||||
|
url: 'https://www.manyvids.com',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
slug: 'men',
|
slug: 'men',
|
||||||
name: 'Men',
|
name: 'Men',
|
||||||
|
|||||||
@@ -6135,6 +6135,12 @@ const sites = [
|
|||||||
scene: false,
|
scene: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// JAX SLAYHER
|
||||||
|
{
|
||||||
|
slug: 'jaxslayher',
|
||||||
|
name: 'Jax Slayher',
|
||||||
|
url: 'https://jaxslayher.com',
|
||||||
|
},
|
||||||
// JESSE LOADS MONSTER FACIALS
|
// JESSE LOADS MONSTER FACIALS
|
||||||
{
|
{
|
||||||
slug: 'jesseloadsmonsterfacials',
|
slug: 'jesseloadsmonsterfacials',
|
||||||
@@ -7685,6 +7691,15 @@ const sites = [
|
|||||||
delete: true,
|
delete: true,
|
||||||
parent: 'mamacitaz',
|
parent: 'mamacitaz',
|
||||||
},
|
},
|
||||||
|
// MANYVIDS
|
||||||
|
{
|
||||||
|
name: 'nicoledoshi',
|
||||||
|
slug: 'mv.nicoledoshi',
|
||||||
|
url: 'https://nicoledoshi.manyvids.com',
|
||||||
|
hasLogo: false,
|
||||||
|
parent: 'manyvids',
|
||||||
|
delete: true,
|
||||||
|
},
|
||||||
// MARISKA X
|
// MARISKA X
|
||||||
{
|
{
|
||||||
name: 'MariskaX',
|
name: 'MariskaX',
|
||||||
@@ -9471,6 +9486,12 @@ const sites = [
|
|||||||
url: 'https://shesbreedingmaterial.com',
|
url: 'https://shesbreedingmaterial.com',
|
||||||
parent: 'nubiles',
|
parent: 'nubiles',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slug: 'glowingdesire',
|
||||||
|
name: 'Glowing Desire',
|
||||||
|
url: 'https://glowingdesire.com',
|
||||||
|
parent: 'nubiles',
|
||||||
|
},
|
||||||
// PASCALS SUBSLUTS
|
// PASCALS SUBSLUTS
|
||||||
{
|
{
|
||||||
slug: 'pascalssubsluts',
|
slug: 'pascalssubsluts',
|
||||||
@@ -10329,6 +10350,12 @@ const sites = [
|
|||||||
url: 'https://pornplus.com/series/pimpparade',
|
url: 'https://pornplus.com/series/pimpparade',
|
||||||
parent: 'pornplus',
|
parent: 'pornplus',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Penis To Pussy Marketplace',
|
||||||
|
slug: 'p2pmarketplace',
|
||||||
|
url: 'https://pornplus.com/series/p2p-marketplace',
|
||||||
|
parent: 'pornplus',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Porn Pros',
|
name: 'Porn Pros',
|
||||||
slug: 'pornprosplus',
|
slug: 'pornprosplus',
|
||||||
|
|||||||
@@ -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
28
seeds/08_abilities.js
Normal 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');
|
||||||
|
};
|
||||||
58
seeds/09_manyvids.js
Normal file
58
seeds/09_manyvids.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const unprint = require('unprint');
|
||||||
|
const slugify = require('../src/utils/slugify');
|
||||||
|
|
||||||
|
const limit = Number(process.env.MV_LIMIT) || 1000;
|
||||||
|
const sorting = process.env.MV_SORT || 'followers'; // followers, top, mostActive, newest, trending
|
||||||
|
|
||||||
|
async function fetchCreators(page = 1, acc = []) {
|
||||||
|
console.log(`Fetching ${acc.length}/${limit} creators by ${sorting}`);
|
||||||
|
|
||||||
|
// size seems to be capped at 70
|
||||||
|
const res = await unprint.get(`https://api.manyvids.com/search/creators/list?contentPref=1,2,3&sort=${sorting}&size=50&from=${(page - 1) * 50}`);
|
||||||
|
|
||||||
|
if (res.ok && res.data?.creators) {
|
||||||
|
const creators = acc.concat(res.data.creators.map((creator) => ({
|
||||||
|
name: creator.stageName,
|
||||||
|
slug: `mv.${slugify(creator.slug, '')}`,
|
||||||
|
url: `https://www.manyvids.com/Profile/${creator.id}/${creator.slug}/`,
|
||||||
|
emblem: creator.avatar?.url || null,
|
||||||
|
})));
|
||||||
|
|
||||||
|
if (creators.length >= limit) {
|
||||||
|
return creators;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchCreators(page + 1, creators);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.seed = async function (knex) {
|
||||||
|
const channels = await fetchCreators();
|
||||||
|
|
||||||
|
const manyvidsNetwork = await knex('entities')
|
||||||
|
.where('slug', 'manyvids')
|
||||||
|
.where('type', 'network')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!manyvidsNetwork) {
|
||||||
|
throw new Error('ManyVids found, did the network migration run?');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await knex('entities')
|
||||||
|
.insert(channels.map((channel) => ({
|
||||||
|
name: channel.name,
|
||||||
|
slug: channel.slug,
|
||||||
|
url: channel.url,
|
||||||
|
parent_id: manyvidsNetwork.id,
|
||||||
|
has_logo: false,
|
||||||
|
})))
|
||||||
|
.onConflict(['slug', 'type'])
|
||||||
|
.merge(['name', 'url'])
|
||||||
|
.returning(knex.raw('(xmax = 0) as inserted'));
|
||||||
|
|
||||||
|
console.log(`Done! ${result.filter((row) => row.inserted).length}/${channels.length} ManyVids creators newly added as channels`);
|
||||||
|
};
|
||||||
@@ -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
|
||||||
@@ -213,6 +214,7 @@ function curateActor(actor, withDetails = false, isProfile = false) {
|
|||||||
name: actor.name,
|
name: actor.name,
|
||||||
slug: actor.slug,
|
slug: actor.slug,
|
||||||
url: actor.url,
|
url: actor.url,
|
||||||
|
entryId: actor.entry_id,
|
||||||
gender: actor.gender,
|
gender: actor.gender,
|
||||||
orientation: actor.orientation,
|
orientation: actor.orientation,
|
||||||
entityId: actor.entity_id,
|
entityId: actor.entity_id,
|
||||||
@@ -692,7 +694,7 @@ async function scrapeProfiles(actor, sources, entitiesBySlug, existingProfilesBy
|
|||||||
}
|
}
|
||||||
}), Promise.reject(new Error()));
|
}), Promise.reject(new Error()));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
// console.log(error);
|
||||||
|
|
||||||
if (error.code !== 'PROFILE_NOT_AVAILABLE') {
|
if (error.code !== 'PROFILE_NOT_AVAILABLE') {
|
||||||
logger.error(`Failed to fetch profile for '${actor.name}': ${error.message}`);
|
logger.error(`Failed to fetch profile for '${actor.name}': ${error.message}`);
|
||||||
@@ -824,6 +826,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 +920,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 +988,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
213
src/biometrics.js
Normal 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,
|
||||||
|
};
|
||||||
45
src/media.js
45
src/media.js
@@ -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 });
|
|
||||||
let hasherReady = true;
|
|
||||||
hasher.setEncoding('hex');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tempFilePath = path.join(config.media.path, 'temp', `${baseMedia.id}`);
|
const tempFilePath = path.join(config.media.path, 'temp', `${baseMedia.id}`);
|
||||||
const tempFileTarget = fs.createWriteStream(tempFilePath);
|
let tempFileTarget;
|
||||||
const hashStream = new stream.PassThrough();
|
|
||||||
|
const hasher = blake2.createHash('blake2b', { digestLength: 24 });
|
||||||
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
|
|
||||||
hashStream.on('data', (streamChunk) => {
|
const hashStream = new stream.Transform({
|
||||||
|
transform(streamChunk, _encoding, callback) {
|
||||||
size += streamChunk.length;
|
size += streamChunk.length;
|
||||||
|
hasher.update(streamChunk);
|
||||||
if (hasherReady) {
|
this.push(streamChunk);
|
||||||
hasher.write(streamChunk);
|
callback();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
tempFileTarget = fs.createWriteStream(tempFilePath);
|
||||||
|
|
||||||
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}`);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const hitzefrei = require('./hitzefrei');
|
|||||||
const hookuphotshot = require('./hookuphotshot');
|
const hookuphotshot = require('./hookuphotshot');
|
||||||
const hush = require('./hush');
|
const hush = require('./hush');
|
||||||
const inthecrack = require('./inthecrack');
|
const inthecrack = require('./inthecrack');
|
||||||
|
const jaxslayher = require('./jaxslayher');
|
||||||
const julesjordan = require('./julesjordan');
|
const julesjordan = require('./julesjordan');
|
||||||
const karups = require('./karups');
|
const karups = require('./karups');
|
||||||
const kellymadison = require('./kellymadison');
|
const kellymadison = require('./kellymadison');
|
||||||
@@ -33,6 +34,7 @@ const pornbox = require('./pornbox');
|
|||||||
const littlecapricedreams = require('./littlecapricedreams');
|
const littlecapricedreams = require('./littlecapricedreams');
|
||||||
const loveherfilms = require('./loveherfilms');
|
const loveherfilms = require('./loveherfilms');
|
||||||
const bluedonkeymedia = require('./bluedonkeymedia');
|
const bluedonkeymedia = require('./bluedonkeymedia');
|
||||||
|
const manyvids = require('./manyvids');
|
||||||
const mikeadriano = require('./mikeadriano');
|
const mikeadriano = require('./mikeadriano');
|
||||||
const aylo = require('./aylo');
|
const aylo = require('./aylo');
|
||||||
const missax = require('./missax');
|
const missax = require('./missax');
|
||||||
@@ -205,14 +207,6 @@ module.exports = {
|
|||||||
jerkaoke: modelmedia,
|
jerkaoke: modelmedia,
|
||||||
modelmediaasia: modelmedia,
|
modelmediaasia: modelmedia,
|
||||||
delphine: modelmedia,
|
delphine: modelmedia,
|
||||||
// etc
|
|
||||||
'18vr': badoink,
|
|
||||||
theflourishxxx: theflourish,
|
|
||||||
pierrewoodman,
|
|
||||||
exploitedx, // only from known URL that will specify site
|
|
||||||
fullpornnetwork,
|
|
||||||
adultempire,
|
|
||||||
allherluv: missax,
|
|
||||||
// americanpornstar,
|
// americanpornstar,
|
||||||
angelogodshackoriginal,
|
angelogodshackoriginal,
|
||||||
babevr: badoink,
|
babevr: badoink,
|
||||||
@@ -230,6 +224,7 @@ module.exports = {
|
|||||||
hitzefrei,
|
hitzefrei,
|
||||||
hookuphotshot,
|
hookuphotshot,
|
||||||
inthecrack,
|
inthecrack,
|
||||||
|
jaxslayher,
|
||||||
karups,
|
karups,
|
||||||
boyfun: karups,
|
boyfun: karups,
|
||||||
kellymadison,
|
kellymadison,
|
||||||
@@ -281,4 +276,13 @@ module.exports = {
|
|||||||
slayed: vixen,
|
slayed: vixen,
|
||||||
wifey: vixen,
|
wifey: vixen,
|
||||||
vrcosplayx: badoink,
|
vrcosplayx: badoink,
|
||||||
|
// etc
|
||||||
|
'18vr': badoink,
|
||||||
|
theflourishxxx: theflourish,
|
||||||
|
pierrewoodman,
|
||||||
|
exploitedx, // only from known URL that will specify site
|
||||||
|
fullpornnetwork,
|
||||||
|
adultempire,
|
||||||
|
allherluv: missax,
|
||||||
|
manyvids,
|
||||||
};
|
};
|
||||||
|
|||||||
257
src/scrapers/jaxslayher.js
Executable file
257
src/scrapers/jaxslayher.js
Executable file
@@ -0,0 +1,257 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const unprint = require('unprint');
|
||||||
|
|
||||||
|
const slugify = require('../utils/slugify');
|
||||||
|
const { convert } = require('../utils/convert');
|
||||||
|
const tryUrls = require('../utils/try-urls');
|
||||||
|
|
||||||
|
// parse Next/React data tree
|
||||||
|
async function parsePayload(rawText) {
|
||||||
|
const { createFlightResponse, processBinaryChunk } = await import('@rsc-parser/react-client'); // eslint-disable-line import/no-unresolved
|
||||||
|
|
||||||
|
const response = createFlightResponse(false);
|
||||||
|
const bytes = new TextEncoder().encode(rawText);
|
||||||
|
|
||||||
|
processBinaryChunk(response, bytes);
|
||||||
|
|
||||||
|
return response._chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverseData(node, predicate, resultDepth = 3, seen = new WeakSet(), ancestors = []) {
|
||||||
|
if (node === null || typeof node !== 'object') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seen.has(node)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
seen.add(node);
|
||||||
|
|
||||||
|
if (predicate(node)) {
|
||||||
|
if (resultDepth === 0) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = ancestors.length - resultDepth;
|
||||||
|
|
||||||
|
return ancestors[Math.max(index, 0)] ?? node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = Array.isArray(node) ? node : Object.values(node);
|
||||||
|
const nextAncestors = [...ancestors, node];
|
||||||
|
|
||||||
|
let result = null;
|
||||||
|
|
||||||
|
values.some((value) => {
|
||||||
|
result = traverseData(value, predicate, resultDepth, seen, nextAncestors);
|
||||||
|
return result !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPoster(videoId, channel) {
|
||||||
|
if (!videoId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
`${channel.origin}/images/updates/${videoId}-1280.jpg`,
|
||||||
|
`${channel.origin}/images/updates/${videoId}-720.jpg`,
|
||||||
|
`${channel.origin}/images/updates/${videoId}.jpg`, // 502px
|
||||||
|
`${channel.origin}/images/updates/${videoId}-360.jpg`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideo(videoId, path = '/trailers', channel) {
|
||||||
|
if (!videoId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
`${channel.origin}${path}/${videoId}.mp4`,
|
||||||
|
`${channel.origin}${path}/${videoId}-med.mp4`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrapeAll(scenes, channel) {
|
||||||
|
return scenes.map((data) => {
|
||||||
|
const release = {};
|
||||||
|
|
||||||
|
release.url = `${channel.origin}/tour/video/${data.slug}`;
|
||||||
|
release.entryId = data.id; // URL slugs are not unique, some pages on original site unreachable!
|
||||||
|
|
||||||
|
release.attributes = {
|
||||||
|
dataEntryId: data.id, // not used in URL, store as secondary
|
||||||
|
slug: data.slug,
|
||||||
|
};
|
||||||
|
|
||||||
|
release.title = data.title;
|
||||||
|
release.date = new Date(data.release_date);
|
||||||
|
|
||||||
|
release.actors = data.models.map((model) => ({
|
||||||
|
name: model.name,
|
||||||
|
url: `${channel.origin}/tour/model/${slugify(model.slug)}`, // for some reason they are underscored, while URL expects dashes
|
||||||
|
}));
|
||||||
|
|
||||||
|
release.tags = data.tags.map((tag) => tag.tag_name);
|
||||||
|
|
||||||
|
release.poster = getPoster(data.id, channel);
|
||||||
|
|
||||||
|
release.teaser = getVideo(data.id, '/video_thumbs', channel);
|
||||||
|
release.trailer = getVideo(data.id, '/trailers', channel);
|
||||||
|
|
||||||
|
return release;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLatest(channel, page = 1) {
|
||||||
|
// HTML does not contain tags, hence we jump through hoops to fetch the data directly
|
||||||
|
const res = await unprint.get(`${channel.origin}/tour/videos/most-recent/page/${page}.txt`, {
|
||||||
|
headers: {
|
||||||
|
rsc: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const payload = await parsePayload(res.body);
|
||||||
|
const sceneData = traverseData(payload, (item) => item.content_type === 'video');
|
||||||
|
|
||||||
|
if (sceneData) {
|
||||||
|
return scrapeAll(sceneData.map((item) => item.props.item), channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrapeScene(payload, { url, channel, baseRelease }) {
|
||||||
|
const release = {};
|
||||||
|
|
||||||
|
const sceneData = traverseData(payload, (item) => Object.hasOwn(item, 'videoID'), 2);
|
||||||
|
const durationData = traverseData(sceneData, (item) => /\d{2}:\d{2}:\d{2}/.test(item.children), 0);
|
||||||
|
const videoData = traverseData(sceneData, (item) => Object.hasOwn(item, 'videoID'), 0);
|
||||||
|
const dateData = traverseData(sceneData, (item) => item.children?.includes?.('2026'), 0);
|
||||||
|
|
||||||
|
const videoId = videoData?.videoID;
|
||||||
|
|
||||||
|
release.duration = unprint.extractDuration(durationData?.children);
|
||||||
|
|
||||||
|
if (baseRelease?.entryId) {
|
||||||
|
// base release data is much more concise, duration only thing missing
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
|
||||||
|
release.entryId = videoId;
|
||||||
|
|
||||||
|
release.attributes = {
|
||||||
|
dataEntryId: videoId,
|
||||||
|
slug: new URL(url).pathname.match(/\/video\/([\w-]+)/)?.[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
release.title = videoData?.title;
|
||||||
|
release.date = unprint.extractDate(dateData.children, 'MMM DD/YYYY', { match: null });
|
||||||
|
|
||||||
|
release.poster = getPoster(videoId, channel);
|
||||||
|
release.teaser = getVideo(videoId, '/video_thumbs', channel);
|
||||||
|
release.trailer = getVideo(videoId, '/trailers', channel);
|
||||||
|
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchScene(url, channel, baseRelease) {
|
||||||
|
const dataUrl = `${url.replace(/\/*$/, '')}.txt`;
|
||||||
|
|
||||||
|
const res = await unprint.get(dataUrl, {
|
||||||
|
headers: {
|
||||||
|
rsc: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const payload = await parsePayload(res.body);
|
||||||
|
|
||||||
|
return scrapeScene(payload, { url, channel, baseRelease });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBioItem(bioData, key) {
|
||||||
|
if (!bioData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = traverseData(bioData, (item) => typeof item.children === 'string' && item.children.toLowerCase() === `${key}:`, 2);
|
||||||
|
const values = data[1]?.props.children;
|
||||||
|
|
||||||
|
const value = Array.isArray(values)
|
||||||
|
? values.find((itemValue) => (typeof itemValue === 'string' ? itemValue.trim() : itemValue))
|
||||||
|
: values;
|
||||||
|
|
||||||
|
if (value && String(value).toLowerCase() !== 'n/a') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrapeProfile(payload, actorName, channel, url) {
|
||||||
|
const profile = { url };
|
||||||
|
|
||||||
|
const actorData = traverseData(payload, (item) => slugify(item.name) === slugify(actorName), 0);
|
||||||
|
const bioData = traverseData(payload, (item) => item.className === 'description_main', 0);
|
||||||
|
const descriptionData = traverseData(payload, (item) => item.className === 'description_top', 0);
|
||||||
|
const description = traverseData(descriptionData, (item) => item.className === 'info', 0)?.children;
|
||||||
|
|
||||||
|
profile.entryId = actorData?.id;
|
||||||
|
|
||||||
|
profile.birthPlace = getBioItem(bioData, 'born');
|
||||||
|
|
||||||
|
profile.dateOfBirth = getBioItem(bioData, 'birthdate');
|
||||||
|
profile.age = getBioItem(bioData, 'age');
|
||||||
|
|
||||||
|
profile.eyes = getBioItem(bioData, 'eyes');
|
||||||
|
profile.hairColor = getBioItem(bioData, 'hair');
|
||||||
|
profile.weight = convert(getBioItem(bioData, 'weight'), 'lb', 'kg');
|
||||||
|
profile.height = convert(getBioItem(bioData, 'height'), 'cm');
|
||||||
|
profile.measurements = getBioItem(bioData, 'measurements');
|
||||||
|
|
||||||
|
if (!/no bio available/i.test(description)) {
|
||||||
|
profile.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actorData.is_thumbed && actorData.id) {
|
||||||
|
profile.avatar = [
|
||||||
|
`${channel.origin}/images/models/${actorData.id}.jpg`, // 628px
|
||||||
|
`${channel.origin}/images/models/${actorData.id}-516.jpg`,
|
||||||
|
`${channel.origin}/images/models/${actorData.id}-408.jpg`,
|
||||||
|
`${channel.origin}/images/models/${actorData.id}-300.jpg`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchProfile({ name: actorName, url: actorUrl }, entity) {
|
||||||
|
const { res, url } = await tryUrls([
|
||||||
|
actorUrl && `${actorUrl}.txt`,
|
||||||
|
`${entity.url}/tour/model/${slugify(actorName, '-')}.txt`,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const payload = await parsePayload(res.body);
|
||||||
|
|
||||||
|
return scrapeProfile(payload, actorName, entity, url.replace(/\.txt$/, ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
fetchLatest,
|
||||||
|
fetchProfile,
|
||||||
|
fetchScene,
|
||||||
|
};
|
||||||
@@ -5,7 +5,6 @@ const unprint = require('unprint');
|
|||||||
// const { parse } = require('csv-parse/sync');
|
// const { parse } = require('csv-parse/sync');
|
||||||
|
|
||||||
const slugify = require('../utils/slugify');
|
const slugify = require('../utils/slugify');
|
||||||
const http = require('../utils/http');
|
|
||||||
const { feetInchesToCm, femaleFeetUsToEu } = require('../utils/convert');
|
const { feetInchesToCm, femaleFeetUsToEu } = require('../utils/convert');
|
||||||
|
|
||||||
const thumbKeyRegex = /(thumb\d+_url)|(episode_thumb_image_\d+_url)/;
|
const thumbKeyRegex = /(thumb\d+_url)|(episode_thumb_image_\d+_url)/;
|
||||||
@@ -84,14 +83,14 @@ function scrapeSceneApi(data, channel, parameters) {
|
|||||||
|
|
||||||
async function fetchLatestApi(channel, page = 1, { parameters }) {
|
async function fetchLatestApi(channel, page = 1, { parameters }) {
|
||||||
// JSON API doesn't return poster images, CSV API doesn't have pagination. UPDATE: requested and received both, yet to test
|
// JSON API doesn't return poster images, CSV API doesn't have pagination. UPDATE: requested and received both, yet to test
|
||||||
const res = await http.get(`${parameters.apiAddress}/affiliates?site_id=${parameters.siteId}&page=${page}`, {
|
const res = await unprint.get(`${parameters.apiAddress}/affiliates?site_id=${parameters.siteId}&page=${page}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${config.apiKeys[parameters.apiKey]}`,
|
Authorization: `Bearer ${config.apiKeys[parameters.apiKey]}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
return res.body.data.map((data) => scrapeSceneApi(data, channel, parameters));
|
return res.data.data.map((data) => scrapeSceneApi(data, channel, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status;
|
return res.status;
|
||||||
|
|||||||
241
src/scrapers/manyvids.js
Executable file
241
src/scrapers/manyvids.js
Executable file
@@ -0,0 +1,241 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const unprint = require('unprint');
|
||||||
|
|
||||||
|
const slugify = require('../utils/slugify');
|
||||||
|
|
||||||
|
function splitTag(tag) {
|
||||||
|
return tag
|
||||||
|
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||||
|
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // handle sequence of caps
|
||||||
|
.toLowerCase()
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTrailer(videoId) {
|
||||||
|
if (!videoId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await unprint.get(`https://api.manyvids.com/store/video/${videoId}/private`);
|
||||||
|
|
||||||
|
if (res.ok && res.data?.statusCode === 200) {
|
||||||
|
return res.data.data.teaser.filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
function getModelUrl(modelData) {
|
||||||
|
const slug = modelData.slug || modelData.profileUrl?.match(/\/profile\/\d+\/(\w+)/i)?.[1] || null;
|
||||||
|
|
||||||
|
if (slug) {
|
||||||
|
return `https://${slug}.manyvids.com`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function scrapeScene(data, context) {
|
||||||
|
const release = {};
|
||||||
|
|
||||||
|
release.url = data.url
|
||||||
|
? `https://www.manyvids.com${data.url}`
|
||||||
|
: `https://www.manyvids.com/Video/${data.id}/${data.slug || ''}`;
|
||||||
|
|
||||||
|
release.entryId = data.id;
|
||||||
|
|
||||||
|
release.attributes = {
|
||||||
|
price: data.price?.regular,
|
||||||
|
discount: data.price?.discountedPrice,
|
||||||
|
isCustomOrder: data.isCustomOrder,
|
||||||
|
isPremium: data.isPremium,
|
||||||
|
isPrivate: data.isPrivate,
|
||||||
|
isExclusive: data.isExclusive,
|
||||||
|
isStreamOnly: data.isStreamOnly,
|
||||||
|
};
|
||||||
|
|
||||||
|
release.title = data.title;
|
||||||
|
release.description = data.description;
|
||||||
|
|
||||||
|
release.date = new Date(data.launchDate);
|
||||||
|
release.duration = unprint.extractDuration(data.duration || data.videoDuration);
|
||||||
|
|
||||||
|
/* many channels operate more like a studio than a performer, so the channel information doesn't represent a person
|
||||||
|
const model = data.creator || data.model;
|
||||||
|
|
||||||
|
release.actors = [{
|
||||||
|
name: model.stageName || model.displayName,
|
||||||
|
entryId: model.id,
|
||||||
|
avatar: model.avatar?.url || model.avatar,
|
||||||
|
url: getModelUrl(model),
|
||||||
|
}];
|
||||||
|
*/
|
||||||
|
|
||||||
|
release.tags = [
|
||||||
|
data.is3D && '3d',
|
||||||
|
data.isAiGenerated && 'ai',
|
||||||
|
...data.tagList?.map((tag) => splitTag(tag.label)) || [],
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
release.poster = data.thumbnail?.url || data.screenshot || data.thumbnail; // thumbnail URL is full size, thumbnail direct is thumb sized
|
||||||
|
|
||||||
|
if (data.preview?.url) {
|
||||||
|
release.trailer = data.preview?.url;
|
||||||
|
} else if (context.includeTrailers) {
|
||||||
|
release.trailer = await getTrailer(data.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
release.qualities = [data.height].filter(Boolean);
|
||||||
|
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profileIdRegex = /\/profile\/(\d+)/i;
|
||||||
|
|
||||||
|
async function getProfileId(channel) {
|
||||||
|
if (channel.url) {
|
||||||
|
const { hostname, pathname } = new URL(channel.url);
|
||||||
|
|
||||||
|
if (profileIdRegex.test(pathname)) { // full profile URL
|
||||||
|
const urlProfileId = new URL(channel.url).pathname.match(profileIdRegex)?.[1];
|
||||||
|
|
||||||
|
if (urlProfileId) {
|
||||||
|
return urlProfileId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/[\w-]+.manyvids.com/.test(hostname)) { // short profile URL
|
||||||
|
const idRes = await unprint.request(channel.url, {
|
||||||
|
method: 'head',
|
||||||
|
followRedirects: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const profileId = idRes.headers.get('location')?.match(profileIdRegex)?.[1];
|
||||||
|
|
||||||
|
if (profileId) {
|
||||||
|
return profileId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sadly we can't reliably reconstruct short profile URL from the slug
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLatest(channel, page = 1) {
|
||||||
|
const profileId = await getProfileId(channel);
|
||||||
|
|
||||||
|
if (profileId) {
|
||||||
|
const res = await unprint.get(`https://api.manyvids.com/store/videos/${profileId}?sort=newest&limit=100&page=${page}`);
|
||||||
|
|
||||||
|
if (res.ok && res.data?.statusCode === 200) {
|
||||||
|
return Promise.all(res.data.data.map(async (data) => scrapeScene(data, channel)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchScene(url, channel, baseRelease, context) {
|
||||||
|
const videoId = new URL(url).pathname.match(/\/video\/(\d+)/i)?.[1];
|
||||||
|
|
||||||
|
if (!videoId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await unprint.get(`https://api.manyvids.com/store/video/${videoId}`);
|
||||||
|
|
||||||
|
if (res.ok && res.data?.statusCode === 200) {
|
||||||
|
return scrapeScene(res.data.data, {
|
||||||
|
channel,
|
||||||
|
includeTrailers: context.includeTrailers,
|
||||||
|
isDeep: !!baseRelease.entryId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrapeProfile(data) {
|
||||||
|
const profile = {};
|
||||||
|
|
||||||
|
profile.description = [data.bio, data.description].filter(Boolean).join(' | ') || null;
|
||||||
|
|
||||||
|
profile.placeOfResidence = data.location || null;
|
||||||
|
profile.ethnicity = data.ethnicity || null;
|
||||||
|
profile.hairColor = data.hairColor || null;
|
||||||
|
|
||||||
|
profile.gender = data.identification?.toLowerCase() || null;
|
||||||
|
profile.orientation = data.orientation;
|
||||||
|
|
||||||
|
profile.avatar = data.avatar || null;
|
||||||
|
profile.photos = [data.portrait].filter(Boolean);
|
||||||
|
|
||||||
|
profile.socials = [data.socLinkTwitter, data.socLinkInstagram, data.socLinkReddit].filter(Boolean);
|
||||||
|
|
||||||
|
const dateOfBirth = data.dob && unprint.extractDate(data.dob);
|
||||||
|
|
||||||
|
if (dateOfBirth && new Date().getFullYear() - dateOfBirth.getFullYear() > 18) {
|
||||||
|
// date of births that would not be legal are likely bogus
|
||||||
|
profile.dateOfBirth = dateOfBirth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getActorUrl(actor) {
|
||||||
|
if (actor.url) {
|
||||||
|
const idRes = await unprint.request(actor.url, {
|
||||||
|
method: 'head',
|
||||||
|
followRedirects: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const profileId = idRes.headers.get('location')?.match(profileIdRegex)?.[1];
|
||||||
|
|
||||||
|
if (profileId) {
|
||||||
|
return `https://api.manyvids.com/profile/profile/${profileId}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await unprint.get(`https://api.manyvids.com/search/autocomplete?keywords=${slugify(actor.name, '+')}`);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const model = res.data?.models?.find((modelResult) => modelResult.stage_name === actor.name || modelResult.username === actor.name);
|
||||||
|
|
||||||
|
if (model) {
|
||||||
|
const profileId = model.url?.match(/\/profile\/(\d+)/i)?.[1];
|
||||||
|
|
||||||
|
if (profileId) {
|
||||||
|
return `https://api.manyvids.com/profile/profile/${profileId}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchProfile(actor, entity) {
|
||||||
|
const actorUrl = await getActorUrl(actor);
|
||||||
|
|
||||||
|
if (actorUrl) {
|
||||||
|
const res = await unprint.get(actorUrl);
|
||||||
|
|
||||||
|
if (res.ok && res.data?.username) {
|
||||||
|
return scrapeProfile(res.data, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actorUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
fetchLatest,
|
||||||
|
fetchProfile,
|
||||||
|
fetchScene,
|
||||||
|
};
|
||||||
@@ -26,9 +26,11 @@ async function getPhotos(albumUrl) {
|
|||||||
selectAll: '.photo-thumb',
|
selectAll: '.photo-thumb',
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok
|
if (res.ok) {
|
||||||
? res.context.map(({ query }) => unprint.prefixUrl(query.element('source').srcset))
|
return res.context.map(({ query }) => query.sourceSet('source, picture img')).filter(Boolean);
|
||||||
: [];
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrapeAll(scenes, entity) {
|
function scrapeAll(scenes, entity) {
|
||||||
@@ -37,7 +39,7 @@ function scrapeAll(scenes, entity) {
|
|||||||
|
|
||||||
release.title = query.content('.title a');
|
release.title = query.content('.title a');
|
||||||
release.url = stripQuery(unprint.prefixUrl(query.url('.title a'), entity.url));
|
release.url = stripQuery(unprint.prefixUrl(query.url('.title a'), entity.url));
|
||||||
release.entryId = Number(new URL(release.url).pathname.match(/\/watch\/(\d+)/)[1]);
|
release.entryId = Number(new URL(release.url).pathname.match(/\/(?:watch|stream)\/(\d+)/)[1]);
|
||||||
|
|
||||||
release.date = query.date('.date', 'MMM D, YYYY');
|
release.date = query.date('.date', 'MMM D, YYYY');
|
||||||
|
|
||||||
@@ -51,7 +53,8 @@ function scrapeAll(scenes, entity) {
|
|||||||
release.actors = query.content('.models', { trim: false })?.trim().split(/\s{2,}/);
|
release.actors = query.content('.models', { trim: false })?.trim().split(/\s{2,}/);
|
||||||
}
|
}
|
||||||
|
|
||||||
const poster = query.sourceSet('img', 'data-srcset')?.[0];
|
const poster = query.sourceSet('img', 'data-srcset')?.[0]
|
||||||
|
|| query.sourceSet('img', 'data-src')?.[0];
|
||||||
|
|
||||||
release.poster = poster && [
|
release.poster = poster && [
|
||||||
poster.replace('_640', '_1280'),
|
poster.replace('_640', '_1280'),
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const hush = require('./hush');
|
|||||||
const innofsin = require('./innofsin');
|
const innofsin = require('./innofsin');
|
||||||
const insex = require('./insex');
|
const insex = require('./insex');
|
||||||
const inthecrack = require('./inthecrack');
|
const inthecrack = require('./inthecrack');
|
||||||
|
const jaxslayher = require('./jaxslayher');
|
||||||
const jesseloadsmonsterfacials = require('./jesseloadsmonsterfacials');
|
const jesseloadsmonsterfacials = require('./jesseloadsmonsterfacials');
|
||||||
const julesjordan = require('./julesjordan');
|
const julesjordan = require('./julesjordan');
|
||||||
const karups = require('./karups');
|
const karups = require('./karups');
|
||||||
@@ -43,6 +44,7 @@ const pornbox = require('./pornbox');
|
|||||||
const littlecapricedreams = require('./littlecapricedreams');
|
const littlecapricedreams = require('./littlecapricedreams');
|
||||||
const loveherfilms = require('./loveherfilms');
|
const loveherfilms = require('./loveherfilms');
|
||||||
const bluedonkeymedia = require('./bluedonkeymedia');
|
const bluedonkeymedia = require('./bluedonkeymedia');
|
||||||
|
const manyvids = require('./manyvids');
|
||||||
const mikeadriano = require('./mikeadriano');
|
const mikeadriano = require('./mikeadriano');
|
||||||
const aylo = require('./aylo');
|
const aylo = require('./aylo');
|
||||||
const missax = require('./missax');
|
const missax = require('./missax');
|
||||||
@@ -145,9 +147,11 @@ module.exports = {
|
|||||||
julesjordan,
|
julesjordan,
|
||||||
karups,
|
karups,
|
||||||
// etc.
|
// etc.
|
||||||
|
jaxslayher,
|
||||||
littlecapricedreams,
|
littlecapricedreams,
|
||||||
loveherfilms,
|
loveherfilms,
|
||||||
mamacitaz: porndoe,
|
mamacitaz: porndoe,
|
||||||
|
manyvids,
|
||||||
mariskax,
|
mariskax,
|
||||||
mikeadriano,
|
mikeadriano,
|
||||||
missax,
|
missax,
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ const dateRegex = /\d{4}-\d{2}-\d{2}T/;
|
|||||||
function scrapeLatest(scenes, fullData, channel, parameters) {
|
function scrapeLatest(scenes, fullData, channel, parameters) {
|
||||||
return scenes.map(({ query }) => {
|
return scenes.map(({ query }) => {
|
||||||
const release = {};
|
const release = {};
|
||||||
|
const origin = new URL(parameters.latest || channel.url).origin;
|
||||||
|
|
||||||
release.url = query.url('[href*="/video"]', { origin: new URL(parameters.latest || channel.url).origin });
|
release.url = query.url('a[href*="/video"]', { origin })
|
||||||
release.title = query.content('a[href*="/video"] strong');
|
|| query.url('nuxtlink[to*="/video"]', { attribute: 'to', origin });
|
||||||
|
|
||||||
|
release.title = query.content('a[href*="/video"] strong, nuxtlink[to*="/video"] strong');
|
||||||
|
|
||||||
release.entryId = release.url
|
release.entryId = release.url
|
||||||
? new URL(release.url).pathname.split('/').at(-1)
|
? new URL(release.url).pathname.split('/').at(-1)
|
||||||
|
|||||||
31
src/tools/biometrics.js
Normal file
31
src/tools/biometrics.js
Normal 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();
|
||||||
@@ -265,6 +265,7 @@ const actors = [
|
|||||||
{ entity: 'tokyohot', name: 'Mai Kawana', url: 'https://my.tokyo-hot.com/cast/2099/', fields: ['avatar', 'birthPlace', 'height', 'cup', 'bust', 'waist', 'hip', 'hairStyle', 'shoeSize', 'bloodType'] },
|
{ entity: 'tokyohot', name: 'Mai Kawana', url: 'https://my.tokyo-hot.com/cast/2099/', fields: ['avatar', 'birthPlace', 'height', 'cup', 'bust', 'waist', 'hip', 'hairStyle', 'shoeSize', 'bloodType'] },
|
||||||
{ entity: 'wakeupnfuck', name: 'Abby Lee Brazil', fields: ['avatar', 'nationality'] },
|
{ entity: 'wakeupnfuck', name: 'Abby Lee Brazil', fields: ['avatar', 'nationality'] },
|
||||||
{ entity: 'darkkotv', name: 'Aidra Fox', fields: ['avatar', 'description', 'dateOfBirth', 'birthPlace', 'ethnicity', 'height', 'weight', 'measurements', 'naturalBoobs', 'hasTattoos', 'hasPiercings'] },
|
{ entity: 'darkkotv', name: 'Aidra Fox', fields: ['avatar', 'description', 'dateOfBirth', 'birthPlace', 'ethnicity', 'height', 'weight', 'measurements', 'naturalBoobs', 'hasTattoos', 'hasPiercings'] },
|
||||||
|
{ entity: 'jaxslayher', name: 'Eliza Ibarra', fields: ['avatar', 'description', 'eyes', 'height', 'weight', 'birthPlace', 'measurements', 'hairColor'] },
|
||||||
];
|
];
|
||||||
|
|
||||||
const actorScrapers = scrapers.actors;
|
const actorScrapers = scrapers.actors;
|
||||||
|
|||||||
Reference in New Issue
Block a user