Merge branch 'main' of https://huggingface.co/spaces/devusman/test
Browse files- .gitignore +2 -1
- index.html +2 -1
- package-lock.json +396 -5
- package.json +5 -1
- server.js +85 -91
.gitignore
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
-
/node_modules
|
|
|
|
|
|
| 1 |
+
/node_modules
|
| 2 |
+
.env
|
index.html
CHANGED
|
@@ -278,7 +278,8 @@
|
|
| 278 |
const logSection = document.getElementById('log-section');
|
| 279 |
const logContainer = document.getElementById('log-container');
|
| 280 |
|
| 281 |
-
const API_BASE_URL = 'http://localhost:7860';
|
|
|
|
| 282 |
let pollInterval;
|
| 283 |
let lastLoggedMessage = ''; // NEW: Track last message to avoid duplicates
|
| 284 |
|
|
|
|
| 278 |
const logSection = document.getElementById('log-section');
|
| 279 |
const logContainer = document.getElementById('log-container');
|
| 280 |
|
| 281 |
+
// const API_BASE_URL = 'http://localhost:7860';
|
| 282 |
+
const API_BASE_URL = 'https://devusman-studocu-testing.hf.space';
|
| 283 |
let pollInterval;
|
| 284 |
let lastLoggedMessage = ''; // NEW: Track last message to avoid duplicates
|
| 285 |
|
package-lock.json
CHANGED
|
@@ -8,13 +8,17 @@
|
|
| 8 |
"name": "puppeteer-api",
|
| 9 |
"version": "1.0.0",
|
| 10 |
"dependencies": {
|
|
|
|
| 11 |
"axios": "^1.11.0",
|
| 12 |
"cluster": "^0.7.7",
|
| 13 |
"cors": "^2.8.5",
|
|
|
|
| 14 |
"express": "^5.1.0",
|
| 15 |
"puppeteer": "^24.16.2",
|
| 16 |
"puppeteer-extra": "^3.3.6",
|
| 17 |
-
"puppeteer-extra-plugin-
|
|
|
|
|
|
|
| 18 |
},
|
| 19 |
"devDependencies": {
|
| 20 |
"nodemon": "^3.1.10",
|
|
@@ -22,6 +26,15 @@
|
|
| 22 |
"typescript": "^5.4.0"
|
| 23 |
}
|
| 24 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
"node_modules/@babel/code-frame": {
|
| 26 |
"version": "7.27.1",
|
| 27 |
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
|
@@ -72,6 +85,12 @@
|
|
| 72 |
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
|
| 73 |
"license": "MIT"
|
| 74 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
"node_modules/@types/debug": {
|
| 76 |
"version": "4.1.12",
|
| 77 |
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
|
@@ -92,7 +111,6 @@
|
|
| 92 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
|
| 93 |
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
|
| 94 |
"license": "MIT",
|
| 95 |
-
"optional": true,
|
| 96 |
"dependencies": {
|
| 97 |
"undici-types": "~7.10.0"
|
| 98 |
}
|
|
@@ -295,6 +313,26 @@
|
|
| 295 |
}
|
| 296 |
}
|
| 297 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
"node_modules/basic-ftp": {
|
| 299 |
"version": "5.0.5",
|
| 300 |
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
|
@@ -304,6 +342,16 @@
|
|
| 304 |
"node": ">=10.0.0"
|
| 305 |
}
|
| 306 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
"node_modules/binary-extensions": {
|
| 308 |
"version": "2.3.0",
|
| 309 |
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
|
@@ -360,6 +408,30 @@
|
|
| 360 |
"node": ">=8"
|
| 361 |
}
|
| 362 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
"node_modules/buffer-crc32": {
|
| 364 |
"version": "0.2.13",
|
| 365 |
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
@@ -441,6 +513,24 @@
|
|
| 441 |
"fsevents": "~2.3.2"
|
| 442 |
}
|
| 443 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 |
"node_modules/chromium-bidi": {
|
| 445 |
"version": "7.3.1",
|
| 446 |
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-7.3.1.tgz",
|
|
@@ -711,6 +801,18 @@
|
|
| 711 |
"integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==",
|
| 712 |
"license": "BSD-3-Clause"
|
| 713 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 714 |
"node_modules/dunder-proto": {
|
| 715 |
"version": "1.0.1",
|
| 716 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
@@ -890,6 +992,18 @@
|
|
| 890 |
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 891 |
"license": "MIT"
|
| 892 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 893 |
"node_modules/escodegen": {
|
| 894 |
"version": "2.1.0",
|
| 895 |
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
|
|
@@ -1330,6 +1444,17 @@
|
|
| 1330 |
"node": ">= 14"
|
| 1331 |
}
|
| 1332 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1333 |
"node_modules/glob": {
|
| 1334 |
"version": "7.2.3",
|
| 1335 |
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
@@ -1494,6 +1619,26 @@
|
|
| 1494 |
"node": ">=0.10.0"
|
| 1495 |
}
|
| 1496 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1497 |
"node_modules/ignore-by-default": {
|
| 1498 |
"version": "1.0.1",
|
| 1499 |
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
|
@@ -1577,6 +1722,21 @@
|
|
| 1577 |
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
| 1578 |
"license": "MIT"
|
| 1579 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1580 |
"node_modules/is-extendable": {
|
| 1581 |
"version": "0.1.1",
|
| 1582 |
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
|
@@ -1646,6 +1806,18 @@
|
|
| 1646 |
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 1647 |
"license": "MIT"
|
| 1648 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1649 |
"node_modules/isexe": {
|
| 1650 |
"version": "2.0.0",
|
| 1651 |
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
|
@@ -1719,6 +1891,16 @@
|
|
| 1719 |
"node": ">=0.10.0"
|
| 1720 |
}
|
| 1721 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1722 |
"node_modules/lines-and-columns": {
|
| 1723 |
"version": "1.2.4",
|
| 1724 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
@@ -1758,6 +1940,12 @@
|
|
| 1758 |
"integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
|
| 1759 |
"dev": true
|
| 1760 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1761 |
"node_modules/math-intrinsics": {
|
| 1762 |
"version": "1.1.0",
|
| 1763 |
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
@@ -1884,6 +2072,13 @@
|
|
| 1884 |
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 1885 |
"license": "MIT"
|
| 1886 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1887 |
"node_modules/negotiator": {
|
| 1888 |
"version": "1.0.0",
|
| 1889 |
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
|
@@ -1915,6 +2110,26 @@
|
|
| 1915 |
"dev": true,
|
| 1916 |
"license": "MIT"
|
| 1917 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1918 |
"node_modules/nodemon": {
|
| 1919 |
"version": "3.1.10",
|
| 1920 |
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
|
@@ -2307,6 +2522,32 @@
|
|
| 2307 |
}
|
| 2308 |
}
|
| 2309 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2310 |
"node_modules/puppeteer-extra-plugin-stealth": {
|
| 2311 |
"version": "2.11.2",
|
| 2312 |
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz",
|
|
@@ -2387,6 +2628,20 @@
|
|
| 2387 |
}
|
| 2388 |
}
|
| 2389 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2390 |
"node_modules/qs": {
|
| 2391 |
"version": "6.14.0",
|
| 2392 |
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
|
@@ -2439,6 +2694,74 @@
|
|
| 2439 |
"node": ">=8.10.0"
|
| 2440 |
}
|
| 2441 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2442 |
"node_modules/require-directory": {
|
| 2443 |
"version": "2.1.1",
|
| 2444 |
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
|
@@ -2714,6 +3037,20 @@
|
|
| 2714 |
"node": ">=10"
|
| 2715 |
}
|
| 2716 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2717 |
"node_modules/smart-buffer": {
|
| 2718 |
"version": "4.2.0",
|
| 2719 |
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
|
@@ -2906,7 +3243,6 @@
|
|
| 2906 |
"version": "2.3.8",
|
| 2907 |
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
| 2908 |
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
| 2909 |
-
"dev": true,
|
| 2910 |
"license": "MIT"
|
| 2911 |
},
|
| 2912 |
"node_modules/to-regex-range": {
|
|
@@ -2941,6 +3277,21 @@
|
|
| 2941 |
"nodetouch": "bin/nodetouch.js"
|
| 2942 |
}
|
| 2943 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2944 |
"node_modules/tsc-watch": {
|
| 2945 |
"version": "6.3.1",
|
| 2946 |
"resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.3.1.tgz",
|
|
@@ -3009,6 +3360,16 @@
|
|
| 3009 |
"node": ">=14.17"
|
| 3010 |
}
|
| 3011 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3012 |
"node_modules/undefsafe": {
|
| 3013 |
"version": "2.0.5",
|
| 3014 |
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
|
@@ -3020,8 +3381,7 @@
|
|
| 3020 |
"version": "7.10.0",
|
| 3021 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
| 3022 |
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
| 3023 |
-
"license": "MIT"
|
| 3024 |
-
"optional": true
|
| 3025 |
},
|
| 3026 |
"node_modules/uni-global": {
|
| 3027 |
"version": "1.0.0",
|
|
@@ -3050,6 +3410,12 @@
|
|
| 3050 |
"node": ">= 0.8"
|
| 3051 |
}
|
| 3052 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3053 |
"node_modules/vary": {
|
| 3054 |
"version": "1.1.2",
|
| 3055 |
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
@@ -3059,6 +3425,22 @@
|
|
| 3059 |
"node": ">= 0.8"
|
| 3060 |
}
|
| 3061 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3062 |
"node_modules/which": {
|
| 3063 |
"version": "2.0.2",
|
| 3064 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
@@ -3119,6 +3501,15 @@
|
|
| 3119 |
}
|
| 3120 |
}
|
| 3121 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3122 |
"node_modules/y18n": {
|
| 3123 |
"version": "5.0.8",
|
| 3124 |
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
|
|
|
| 8 |
"name": "puppeteer-api",
|
| 9 |
"version": "1.0.0",
|
| 10 |
"dependencies": {
|
| 11 |
+
"@2captcha/captcha-solver": "^1.3.0",
|
| 12 |
"axios": "^1.11.0",
|
| 13 |
"cluster": "^0.7.7",
|
| 14 |
"cors": "^2.8.5",
|
| 15 |
+
"dotenv": "^17.2.2",
|
| 16 |
"express": "^5.1.0",
|
| 17 |
"puppeteer": "^24.16.2",
|
| 18 |
"puppeteer-extra": "^3.3.6",
|
| 19 |
+
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
| 20 |
+
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
| 21 |
+
"puppeteer-real-browser": "^1.4.4"
|
| 22 |
},
|
| 23 |
"devDependencies": {
|
| 24 |
"nodemon": "^3.1.10",
|
|
|
|
| 26 |
"typescript": "^5.4.0"
|
| 27 |
}
|
| 28 |
},
|
| 29 |
+
"node_modules/@2captcha/captcha-solver": {
|
| 30 |
+
"version": "1.3.0",
|
| 31 |
+
"resolved": "https://registry.npmjs.org/@2captcha/captcha-solver/-/captcha-solver-1.3.0.tgz",
|
| 32 |
+
"integrity": "sha512-Rgyr0kv3EAvBtogQOoe2ns1aXo4ZEoi/QW/96Bkvjk5RdEVPzi+C/X8X2HphQo6FanKQ1UqO7Q7T874IYUJ8eg==",
|
| 33 |
+
"license": "MIT",
|
| 34 |
+
"dependencies": {
|
| 35 |
+
"node-fetch": "^2.6.1"
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
"node_modules/@babel/code-frame": {
|
| 39 |
"version": "7.27.1",
|
| 40 |
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
|
|
|
| 85 |
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
|
| 86 |
"license": "MIT"
|
| 87 |
},
|
| 88 |
+
"node_modules/@types/bezier-js": {
|
| 89 |
+
"version": "4.1.3",
|
| 90 |
+
"resolved": "https://registry.npmjs.org/@types/bezier-js/-/bezier-js-4.1.3.tgz",
|
| 91 |
+
"integrity": "sha512-FNVVCu5mx/rJCWBxLTcL7oOajmGtWtBTDjq6DSUWUI12GeePivrZZXz+UgE0D6VYsLEjvExRO03z4hVtu3pTEQ==",
|
| 92 |
+
"license": "MIT"
|
| 93 |
+
},
|
| 94 |
"node_modules/@types/debug": {
|
| 95 |
"version": "4.1.12",
|
| 96 |
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
|
|
|
| 111 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
|
| 112 |
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
|
| 113 |
"license": "MIT",
|
|
|
|
| 114 |
"dependencies": {
|
| 115 |
"undici-types": "~7.10.0"
|
| 116 |
}
|
|
|
|
| 313 |
}
|
| 314 |
}
|
| 315 |
},
|
| 316 |
+
"node_modules/base64-js": {
|
| 317 |
+
"version": "1.5.1",
|
| 318 |
+
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
| 319 |
+
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
| 320 |
+
"funding": [
|
| 321 |
+
{
|
| 322 |
+
"type": "github",
|
| 323 |
+
"url": "https://github.com/sponsors/feross"
|
| 324 |
+
},
|
| 325 |
+
{
|
| 326 |
+
"type": "patreon",
|
| 327 |
+
"url": "https://www.patreon.com/feross"
|
| 328 |
+
},
|
| 329 |
+
{
|
| 330 |
+
"type": "consulting",
|
| 331 |
+
"url": "https://feross.org/support"
|
| 332 |
+
}
|
| 333 |
+
],
|
| 334 |
+
"license": "MIT"
|
| 335 |
+
},
|
| 336 |
"node_modules/basic-ftp": {
|
| 337 |
"version": "5.0.5",
|
| 338 |
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
|
|
|
| 342 |
"node": ">=10.0.0"
|
| 343 |
}
|
| 344 |
},
|
| 345 |
+
"node_modules/bezier-js": {
|
| 346 |
+
"version": "6.1.4",
|
| 347 |
+
"resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-6.1.4.tgz",
|
| 348 |
+
"integrity": "sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==",
|
| 349 |
+
"license": "MIT",
|
| 350 |
+
"funding": {
|
| 351 |
+
"type": "individual",
|
| 352 |
+
"url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md"
|
| 353 |
+
}
|
| 354 |
+
},
|
| 355 |
"node_modules/binary-extensions": {
|
| 356 |
"version": "2.3.0",
|
| 357 |
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
|
|
|
| 408 |
"node": ">=8"
|
| 409 |
}
|
| 410 |
},
|
| 411 |
+
"node_modules/buffer": {
|
| 412 |
+
"version": "5.7.1",
|
| 413 |
+
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
| 414 |
+
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
| 415 |
+
"funding": [
|
| 416 |
+
{
|
| 417 |
+
"type": "github",
|
| 418 |
+
"url": "https://github.com/sponsors/feross"
|
| 419 |
+
},
|
| 420 |
+
{
|
| 421 |
+
"type": "patreon",
|
| 422 |
+
"url": "https://www.patreon.com/feross"
|
| 423 |
+
},
|
| 424 |
+
{
|
| 425 |
+
"type": "consulting",
|
| 426 |
+
"url": "https://feross.org/support"
|
| 427 |
+
}
|
| 428 |
+
],
|
| 429 |
+
"license": "MIT",
|
| 430 |
+
"dependencies": {
|
| 431 |
+
"base64-js": "^1.3.1",
|
| 432 |
+
"ieee754": "^1.1.13"
|
| 433 |
+
}
|
| 434 |
+
},
|
| 435 |
"node_modules/buffer-crc32": {
|
| 436 |
"version": "0.2.13",
|
| 437 |
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
|
|
| 513 |
"fsevents": "~2.3.2"
|
| 514 |
}
|
| 515 |
},
|
| 516 |
+
"node_modules/chrome-launcher": {
|
| 517 |
+
"version": "1.2.0",
|
| 518 |
+
"resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.0.tgz",
|
| 519 |
+
"integrity": "sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==",
|
| 520 |
+
"license": "Apache-2.0",
|
| 521 |
+
"dependencies": {
|
| 522 |
+
"@types/node": "*",
|
| 523 |
+
"escape-string-regexp": "^4.0.0",
|
| 524 |
+
"is-wsl": "^2.2.0",
|
| 525 |
+
"lighthouse-logger": "^2.0.1"
|
| 526 |
+
},
|
| 527 |
+
"bin": {
|
| 528 |
+
"print-chrome-path": "bin/print-chrome-path.cjs"
|
| 529 |
+
},
|
| 530 |
+
"engines": {
|
| 531 |
+
"node": ">=12.13.0"
|
| 532 |
+
}
|
| 533 |
+
},
|
| 534 |
"node_modules/chromium-bidi": {
|
| 535 |
"version": "7.3.1",
|
| 536 |
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-7.3.1.tgz",
|
|
|
|
| 801 |
"integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==",
|
| 802 |
"license": "BSD-3-Clause"
|
| 803 |
},
|
| 804 |
+
"node_modules/dotenv": {
|
| 805 |
+
"version": "17.2.2",
|
| 806 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz",
|
| 807 |
+
"integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==",
|
| 808 |
+
"license": "BSD-2-Clause",
|
| 809 |
+
"engines": {
|
| 810 |
+
"node": ">=12"
|
| 811 |
+
},
|
| 812 |
+
"funding": {
|
| 813 |
+
"url": "https://dotenvx.com"
|
| 814 |
+
}
|
| 815 |
+
},
|
| 816 |
"node_modules/dunder-proto": {
|
| 817 |
"version": "1.0.1",
|
| 818 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
|
|
| 992 |
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 993 |
"license": "MIT"
|
| 994 |
},
|
| 995 |
+
"node_modules/escape-string-regexp": {
|
| 996 |
+
"version": "4.0.0",
|
| 997 |
+
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
| 998 |
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
| 999 |
+
"license": "MIT",
|
| 1000 |
+
"engines": {
|
| 1001 |
+
"node": ">=10"
|
| 1002 |
+
},
|
| 1003 |
+
"funding": {
|
| 1004 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1005 |
+
}
|
| 1006 |
+
},
|
| 1007 |
"node_modules/escodegen": {
|
| 1008 |
"version": "2.1.0",
|
| 1009 |
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
|
|
|
|
| 1444 |
"node": ">= 14"
|
| 1445 |
}
|
| 1446 |
},
|
| 1447 |
+
"node_modules/ghost-cursor": {
|
| 1448 |
+
"version": "1.4.1",
|
| 1449 |
+
"resolved": "https://registry.npmjs.org/ghost-cursor/-/ghost-cursor-1.4.1.tgz",
|
| 1450 |
+
"integrity": "sha512-K8A8/Co/Jbdqee694qrNsGWBG51DVK5UF2gGKEoZBDx9F1WmoD2SzUoDHWoY7O+TY84s1VrWwwfkVKxI2FoV2Q==",
|
| 1451 |
+
"license": "ISC",
|
| 1452 |
+
"dependencies": {
|
| 1453 |
+
"@types/bezier-js": "4",
|
| 1454 |
+
"bezier-js": "^6.1.3",
|
| 1455 |
+
"debug": "^4.3.4"
|
| 1456 |
+
}
|
| 1457 |
+
},
|
| 1458 |
"node_modules/glob": {
|
| 1459 |
"version": "7.2.3",
|
| 1460 |
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
|
|
| 1619 |
"node": ">=0.10.0"
|
| 1620 |
}
|
| 1621 |
},
|
| 1622 |
+
"node_modules/ieee754": {
|
| 1623 |
+
"version": "1.2.1",
|
| 1624 |
+
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
| 1625 |
+
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
| 1626 |
+
"funding": [
|
| 1627 |
+
{
|
| 1628 |
+
"type": "github",
|
| 1629 |
+
"url": "https://github.com/sponsors/feross"
|
| 1630 |
+
},
|
| 1631 |
+
{
|
| 1632 |
+
"type": "patreon",
|
| 1633 |
+
"url": "https://www.patreon.com/feross"
|
| 1634 |
+
},
|
| 1635 |
+
{
|
| 1636 |
+
"type": "consulting",
|
| 1637 |
+
"url": "https://feross.org/support"
|
| 1638 |
+
}
|
| 1639 |
+
],
|
| 1640 |
+
"license": "BSD-3-Clause"
|
| 1641 |
+
},
|
| 1642 |
"node_modules/ignore-by-default": {
|
| 1643 |
"version": "1.0.1",
|
| 1644 |
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
|
|
|
| 1722 |
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
| 1723 |
"license": "MIT"
|
| 1724 |
},
|
| 1725 |
+
"node_modules/is-docker": {
|
| 1726 |
+
"version": "2.2.1",
|
| 1727 |
+
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
| 1728 |
+
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
|
| 1729 |
+
"license": "MIT",
|
| 1730 |
+
"bin": {
|
| 1731 |
+
"is-docker": "cli.js"
|
| 1732 |
+
},
|
| 1733 |
+
"engines": {
|
| 1734 |
+
"node": ">=8"
|
| 1735 |
+
},
|
| 1736 |
+
"funding": {
|
| 1737 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1738 |
+
}
|
| 1739 |
+
},
|
| 1740 |
"node_modules/is-extendable": {
|
| 1741 |
"version": "0.1.1",
|
| 1742 |
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
|
|
|
| 1806 |
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 1807 |
"license": "MIT"
|
| 1808 |
},
|
| 1809 |
+
"node_modules/is-wsl": {
|
| 1810 |
+
"version": "2.2.0",
|
| 1811 |
+
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
| 1812 |
+
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
| 1813 |
+
"license": "MIT",
|
| 1814 |
+
"dependencies": {
|
| 1815 |
+
"is-docker": "^2.0.0"
|
| 1816 |
+
},
|
| 1817 |
+
"engines": {
|
| 1818 |
+
"node": ">=8"
|
| 1819 |
+
}
|
| 1820 |
+
},
|
| 1821 |
"node_modules/isexe": {
|
| 1822 |
"version": "2.0.0",
|
| 1823 |
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
|
|
|
| 1891 |
"node": ">=0.10.0"
|
| 1892 |
}
|
| 1893 |
},
|
| 1894 |
+
"node_modules/lighthouse-logger": {
|
| 1895 |
+
"version": "2.0.2",
|
| 1896 |
+
"resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz",
|
| 1897 |
+
"integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==",
|
| 1898 |
+
"license": "Apache-2.0",
|
| 1899 |
+
"dependencies": {
|
| 1900 |
+
"debug": "^4.4.1",
|
| 1901 |
+
"marky": "^1.2.2"
|
| 1902 |
+
}
|
| 1903 |
+
},
|
| 1904 |
"node_modules/lines-and-columns": {
|
| 1905 |
"version": "1.2.4",
|
| 1906 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
|
|
| 1940 |
"integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
|
| 1941 |
"dev": true
|
| 1942 |
},
|
| 1943 |
+
"node_modules/marky": {
|
| 1944 |
+
"version": "1.3.0",
|
| 1945 |
+
"resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz",
|
| 1946 |
+
"integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==",
|
| 1947 |
+
"license": "Apache-2.0"
|
| 1948 |
+
},
|
| 1949 |
"node_modules/math-intrinsics": {
|
| 1950 |
"version": "1.1.0",
|
| 1951 |
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
|
|
| 2072 |
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 2073 |
"license": "MIT"
|
| 2074 |
},
|
| 2075 |
+
"node_modules/nan": {
|
| 2076 |
+
"version": "2.23.0",
|
| 2077 |
+
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
|
| 2078 |
+
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
|
| 2079 |
+
"license": "MIT",
|
| 2080 |
+
"optional": true
|
| 2081 |
+
},
|
| 2082 |
"node_modules/negotiator": {
|
| 2083 |
"version": "1.0.0",
|
| 2084 |
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
|
|
|
| 2110 |
"dev": true,
|
| 2111 |
"license": "MIT"
|
| 2112 |
},
|
| 2113 |
+
"node_modules/node-fetch": {
|
| 2114 |
+
"version": "2.7.0",
|
| 2115 |
+
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
| 2116 |
+
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
| 2117 |
+
"license": "MIT",
|
| 2118 |
+
"dependencies": {
|
| 2119 |
+
"whatwg-url": "^5.0.0"
|
| 2120 |
+
},
|
| 2121 |
+
"engines": {
|
| 2122 |
+
"node": "4.x || >=6.0.0"
|
| 2123 |
+
},
|
| 2124 |
+
"peerDependencies": {
|
| 2125 |
+
"encoding": "^0.1.0"
|
| 2126 |
+
},
|
| 2127 |
+
"peerDependenciesMeta": {
|
| 2128 |
+
"encoding": {
|
| 2129 |
+
"optional": true
|
| 2130 |
+
}
|
| 2131 |
+
}
|
| 2132 |
+
},
|
| 2133 |
"node_modules/nodemon": {
|
| 2134 |
"version": "3.1.10",
|
| 2135 |
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
|
|
|
| 2522 |
}
|
| 2523 |
}
|
| 2524 |
},
|
| 2525 |
+
"node_modules/puppeteer-extra-plugin-recaptcha": {
|
| 2526 |
+
"version": "3.6.8",
|
| 2527 |
+
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-recaptcha/-/puppeteer-extra-plugin-recaptcha-3.6.8.tgz",
|
| 2528 |
+
"integrity": "sha512-AY2HG1ZFlSi4xs+Hy84LtRJ95DIfnbjR3Az64dJGVW8gr/hBAGEWRlXTMzea7YOmxO3Nc8Ak3CcUgjgp1gIu1w==",
|
| 2529 |
+
"license": "MIT",
|
| 2530 |
+
"dependencies": {
|
| 2531 |
+
"debug": "^4.1.1",
|
| 2532 |
+
"merge-deep": "^3.0.2",
|
| 2533 |
+
"puppeteer-extra-plugin": "^3.2.3"
|
| 2534 |
+
},
|
| 2535 |
+
"engines": {
|
| 2536 |
+
"node": ">=9.11.2"
|
| 2537 |
+
},
|
| 2538 |
+
"peerDependencies": {
|
| 2539 |
+
"playwright-extra": "*",
|
| 2540 |
+
"puppeteer-extra": "*"
|
| 2541 |
+
},
|
| 2542 |
+
"peerDependenciesMeta": {
|
| 2543 |
+
"playwright-extra": {
|
| 2544 |
+
"optional": true
|
| 2545 |
+
},
|
| 2546 |
+
"puppeteer-extra": {
|
| 2547 |
+
"optional": true
|
| 2548 |
+
}
|
| 2549 |
+
}
|
| 2550 |
+
},
|
| 2551 |
"node_modules/puppeteer-extra-plugin-stealth": {
|
| 2552 |
"version": "2.11.2",
|
| 2553 |
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz",
|
|
|
|
| 2628 |
}
|
| 2629 |
}
|
| 2630 |
},
|
| 2631 |
+
"node_modules/puppeteer-real-browser": {
|
| 2632 |
+
"version": "1.4.4",
|
| 2633 |
+
"resolved": "https://registry.npmjs.org/puppeteer-real-browser/-/puppeteer-real-browser-1.4.4.tgz",
|
| 2634 |
+
"integrity": "sha512-1CYGlL1Y0SdxP55byi9WQ8dtLkyIYBmRpGr+D+cB6uZDrW17+ZxWMnc7CDZfNuNuJaF15DWW5zvChS/ikymdmg==",
|
| 2635 |
+
"license": "ISC",
|
| 2636 |
+
"dependencies": {
|
| 2637 |
+
"chrome-launcher": "^1.1.2",
|
| 2638 |
+
"ghost-cursor": "^1.3.0",
|
| 2639 |
+
"puppeteer-extra": "^3.3.6",
|
| 2640 |
+
"rebrowser-puppeteer-core": "^23.3.1",
|
| 2641 |
+
"tree-kill": "^1.2.2",
|
| 2642 |
+
"xvfb": "^0.4.0"
|
| 2643 |
+
}
|
| 2644 |
+
},
|
| 2645 |
"node_modules/qs": {
|
| 2646 |
"version": "6.14.0",
|
| 2647 |
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
|
|
|
| 2694 |
"node": ">=8.10.0"
|
| 2695 |
}
|
| 2696 |
},
|
| 2697 |
+
"node_modules/rebrowser-puppeteer-core": {
|
| 2698 |
+
"version": "23.10.3",
|
| 2699 |
+
"resolved": "https://registry.npmjs.org/rebrowser-puppeteer-core/-/rebrowser-puppeteer-core-23.10.3.tgz",
|
| 2700 |
+
"integrity": "sha512-oWwuFg3XoZUkAt6Te4zTU6sQeS39I9tctjdSEiDPa76MF47R0IfLX8VQhyRwwzMySqD5L1wambYcWyEAN0b9AA==",
|
| 2701 |
+
"license": "Apache-2.0",
|
| 2702 |
+
"dependencies": {
|
| 2703 |
+
"@puppeteer/browsers": "2.6.1",
|
| 2704 |
+
"chromium-bidi": "0.8.0",
|
| 2705 |
+
"debug": "^4.4.0",
|
| 2706 |
+
"devtools-protocol": "0.0.1367902",
|
| 2707 |
+
"typed-query-selector": "^2.12.0",
|
| 2708 |
+
"ws": "^8.18.0"
|
| 2709 |
+
},
|
| 2710 |
+
"engines": {
|
| 2711 |
+
"node": ">=18"
|
| 2712 |
+
}
|
| 2713 |
+
},
|
| 2714 |
+
"node_modules/rebrowser-puppeteer-core/node_modules/@puppeteer/browsers": {
|
| 2715 |
+
"version": "2.6.1",
|
| 2716 |
+
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz",
|
| 2717 |
+
"integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==",
|
| 2718 |
+
"license": "Apache-2.0",
|
| 2719 |
+
"dependencies": {
|
| 2720 |
+
"debug": "^4.4.0",
|
| 2721 |
+
"extract-zip": "^2.0.1",
|
| 2722 |
+
"progress": "^2.0.3",
|
| 2723 |
+
"proxy-agent": "^6.5.0",
|
| 2724 |
+
"semver": "^7.6.3",
|
| 2725 |
+
"tar-fs": "^3.0.6",
|
| 2726 |
+
"unbzip2-stream": "^1.4.3",
|
| 2727 |
+
"yargs": "^17.7.2"
|
| 2728 |
+
},
|
| 2729 |
+
"bin": {
|
| 2730 |
+
"browsers": "lib/cjs/main-cli.js"
|
| 2731 |
+
},
|
| 2732 |
+
"engines": {
|
| 2733 |
+
"node": ">=18"
|
| 2734 |
+
}
|
| 2735 |
+
},
|
| 2736 |
+
"node_modules/rebrowser-puppeteer-core/node_modules/chromium-bidi": {
|
| 2737 |
+
"version": "0.8.0",
|
| 2738 |
+
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.8.0.tgz",
|
| 2739 |
+
"integrity": "sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==",
|
| 2740 |
+
"license": "Apache-2.0",
|
| 2741 |
+
"dependencies": {
|
| 2742 |
+
"mitt": "3.0.1",
|
| 2743 |
+
"urlpattern-polyfill": "10.0.0",
|
| 2744 |
+
"zod": "3.23.8"
|
| 2745 |
+
},
|
| 2746 |
+
"peerDependencies": {
|
| 2747 |
+
"devtools-protocol": "*"
|
| 2748 |
+
}
|
| 2749 |
+
},
|
| 2750 |
+
"node_modules/rebrowser-puppeteer-core/node_modules/devtools-protocol": {
|
| 2751 |
+
"version": "0.0.1367902",
|
| 2752 |
+
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz",
|
| 2753 |
+
"integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==",
|
| 2754 |
+
"license": "BSD-3-Clause"
|
| 2755 |
+
},
|
| 2756 |
+
"node_modules/rebrowser-puppeteer-core/node_modules/zod": {
|
| 2757 |
+
"version": "3.23.8",
|
| 2758 |
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
| 2759 |
+
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
| 2760 |
+
"license": "MIT",
|
| 2761 |
+
"funding": {
|
| 2762 |
+
"url": "https://github.com/sponsors/colinhacks"
|
| 2763 |
+
}
|
| 2764 |
+
},
|
| 2765 |
"node_modules/require-directory": {
|
| 2766 |
"version": "2.1.1",
|
| 2767 |
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
|
|
|
| 3037 |
"node": ">=10"
|
| 3038 |
}
|
| 3039 |
},
|
| 3040 |
+
"node_modules/sleep": {
|
| 3041 |
+
"version": "6.1.0",
|
| 3042 |
+
"resolved": "https://registry.npmjs.org/sleep/-/sleep-6.1.0.tgz",
|
| 3043 |
+
"integrity": "sha512-Z1x4JjJxsru75Tqn8F4tnOFeEu3HjtITTsumYUiuz54sGKdISgLCek9AUlXlVVrkhltRFhNUsJDJE76SFHTDIQ==",
|
| 3044 |
+
"hasInstallScript": true,
|
| 3045 |
+
"license": "MIT",
|
| 3046 |
+
"optional": true,
|
| 3047 |
+
"dependencies": {
|
| 3048 |
+
"nan": "^2.13.2"
|
| 3049 |
+
},
|
| 3050 |
+
"engines": {
|
| 3051 |
+
"node": ">=0.8.0"
|
| 3052 |
+
}
|
| 3053 |
+
},
|
| 3054 |
"node_modules/smart-buffer": {
|
| 3055 |
"version": "4.2.0",
|
| 3056 |
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
|
|
|
| 3243 |
"version": "2.3.8",
|
| 3244 |
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
| 3245 |
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
|
|
|
| 3246 |
"license": "MIT"
|
| 3247 |
},
|
| 3248 |
"node_modules/to-regex-range": {
|
|
|
|
| 3277 |
"nodetouch": "bin/nodetouch.js"
|
| 3278 |
}
|
| 3279 |
},
|
| 3280 |
+
"node_modules/tr46": {
|
| 3281 |
+
"version": "0.0.3",
|
| 3282 |
+
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
| 3283 |
+
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
| 3284 |
+
"license": "MIT"
|
| 3285 |
+
},
|
| 3286 |
+
"node_modules/tree-kill": {
|
| 3287 |
+
"version": "1.2.2",
|
| 3288 |
+
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
| 3289 |
+
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
| 3290 |
+
"license": "MIT",
|
| 3291 |
+
"bin": {
|
| 3292 |
+
"tree-kill": "cli.js"
|
| 3293 |
+
}
|
| 3294 |
+
},
|
| 3295 |
"node_modules/tsc-watch": {
|
| 3296 |
"version": "6.3.1",
|
| 3297 |
"resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.3.1.tgz",
|
|
|
|
| 3360 |
"node": ">=14.17"
|
| 3361 |
}
|
| 3362 |
},
|
| 3363 |
+
"node_modules/unbzip2-stream": {
|
| 3364 |
+
"version": "1.4.3",
|
| 3365 |
+
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
|
| 3366 |
+
"integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
|
| 3367 |
+
"license": "MIT",
|
| 3368 |
+
"dependencies": {
|
| 3369 |
+
"buffer": "^5.2.1",
|
| 3370 |
+
"through": "^2.3.8"
|
| 3371 |
+
}
|
| 3372 |
+
},
|
| 3373 |
"node_modules/undefsafe": {
|
| 3374 |
"version": "2.0.5",
|
| 3375 |
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
|
|
|
| 3381 |
"version": "7.10.0",
|
| 3382 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
| 3383 |
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
| 3384 |
+
"license": "MIT"
|
|
|
|
| 3385 |
},
|
| 3386 |
"node_modules/uni-global": {
|
| 3387 |
"version": "1.0.0",
|
|
|
|
| 3410 |
"node": ">= 0.8"
|
| 3411 |
}
|
| 3412 |
},
|
| 3413 |
+
"node_modules/urlpattern-polyfill": {
|
| 3414 |
+
"version": "10.0.0",
|
| 3415 |
+
"resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz",
|
| 3416 |
+
"integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==",
|
| 3417 |
+
"license": "MIT"
|
| 3418 |
+
},
|
| 3419 |
"node_modules/vary": {
|
| 3420 |
"version": "1.1.2",
|
| 3421 |
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
|
|
| 3425 |
"node": ">= 0.8"
|
| 3426 |
}
|
| 3427 |
},
|
| 3428 |
+
"node_modules/webidl-conversions": {
|
| 3429 |
+
"version": "3.0.1",
|
| 3430 |
+
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
| 3431 |
+
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
| 3432 |
+
"license": "BSD-2-Clause"
|
| 3433 |
+
},
|
| 3434 |
+
"node_modules/whatwg-url": {
|
| 3435 |
+
"version": "5.0.0",
|
| 3436 |
+
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
| 3437 |
+
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
| 3438 |
+
"license": "MIT",
|
| 3439 |
+
"dependencies": {
|
| 3440 |
+
"tr46": "~0.0.3",
|
| 3441 |
+
"webidl-conversions": "^3.0.0"
|
| 3442 |
+
}
|
| 3443 |
+
},
|
| 3444 |
"node_modules/which": {
|
| 3445 |
"version": "2.0.2",
|
| 3446 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
|
|
| 3501 |
}
|
| 3502 |
}
|
| 3503 |
},
|
| 3504 |
+
"node_modules/xvfb": {
|
| 3505 |
+
"version": "0.4.0",
|
| 3506 |
+
"resolved": "https://registry.npmjs.org/xvfb/-/xvfb-0.4.0.tgz",
|
| 3507 |
+
"integrity": "sha512-g55AbjcBL4Bztfn7kiUrR0ne8mMUsFODDJ+HFGf5OuHJqKKccpExX2Qgn7VF2eImw1eoh6+riXHser1J4agrFA==",
|
| 3508 |
+
"license": "MIT",
|
| 3509 |
+
"optionalDependencies": {
|
| 3510 |
+
"sleep": "6.1.0"
|
| 3511 |
+
}
|
| 3512 |
+
},
|
| 3513 |
"node_modules/y18n": {
|
| 3514 |
"version": "5.0.8",
|
| 3515 |
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
package.json
CHANGED
|
@@ -4,13 +4,17 @@
|
|
| 4 |
"main": "server.js",
|
| 5 |
"type": "commonjs",
|
| 6 |
"dependencies": {
|
|
|
|
| 7 |
"axios": "^1.11.0",
|
| 8 |
"cluster": "^0.7.7",
|
| 9 |
"cors": "^2.8.5",
|
|
|
|
| 10 |
"express": "^5.1.0",
|
| 11 |
"puppeteer": "^24.16.2",
|
| 12 |
"puppeteer-extra": "^3.3.6",
|
| 13 |
-
"puppeteer-extra-plugin-
|
|
|
|
|
|
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
"nodemon": "^3.1.10",
|
|
|
|
| 4 |
"main": "server.js",
|
| 5 |
"type": "commonjs",
|
| 6 |
"dependencies": {
|
| 7 |
+
"@2captcha/captcha-solver": "^1.3.0",
|
| 8 |
"axios": "^1.11.0",
|
| 9 |
"cluster": "^0.7.7",
|
| 10 |
"cors": "^2.8.5",
|
| 11 |
+
"dotenv": "^17.2.2",
|
| 12 |
"express": "^5.1.0",
|
| 13 |
"puppeteer": "^24.16.2",
|
| 14 |
"puppeteer-extra": "^3.3.6",
|
| 15 |
+
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
| 16 |
+
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
| 17 |
+
"puppeteer-real-browser": "^1.4.4"
|
| 18 |
},
|
| 19 |
"devDependencies": {
|
| 20 |
"nodemon": "^3.1.10",
|
server.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
| 1 |
const express = require('express');
|
| 2 |
-
const puppeteerExtra = require('puppeteer-extra');
|
| 3 |
-
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
|
|
|
|
| 4 |
const cors = require('cors');
|
| 5 |
const { EventEmitter } = require('events');
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
const app = express();
|
| 10 |
const port = 7860;
|
|
@@ -12,7 +22,7 @@ const port = 7860;
|
|
| 12 |
app.use(cors());
|
| 13 |
app.use(express.json());
|
| 14 |
|
| 15 |
-
// --- Progress Tracking and Job Storage ---
|
| 16 |
const progressTrackers = new Map();
|
| 17 |
const downloadJobs = new Map();
|
| 18 |
|
|
@@ -155,7 +165,6 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 155 |
progressTracker?.updateProgress(5, 'bypassing', 'Setting up cookie bypass...');
|
| 156 |
|
| 157 |
console.log("πͺ Starting comprehensive cookie and restriction bypass...");
|
| 158 |
-
// Step 1: Set cookies before page load
|
| 159 |
const preCookies = [
|
| 160 |
{ name: 'cookieConsent', value: 'accepted', domain: '.studocu.com' },
|
| 161 |
{ name: 'cookie_consent', value: 'true', domain: '.studocu.com' },
|
|
@@ -174,10 +183,9 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 174 |
}
|
| 175 |
}
|
| 176 |
|
| 177 |
-
// Step 2: Inject CSS to hide cookie banners immediately (
|
| 178 |
await page.addStyleTag({
|
| 179 |
content: `
|
| 180 |
-
/* Hide all possible cookie banners */
|
| 181 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i], [aria-label*="cookie" i],
|
| 182 |
.gdpr-banner, .gdpr-popup, .gdpr-modal, .consent-banner, .consent-popup, .consent-modal, .privacy-banner, .privacy-popup, .privacy-modal,
|
| 183 |
.cookie-law, .cookie-policy, .cookie-compliance, .onetrust-banner-sdk, #onetrust-consent-sdk, .cmp-banner, .cmp-popup, .cmp-modal,
|
|
@@ -189,26 +197,22 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 189 |
z-index: -9999 !important;
|
| 190 |
pointer-events: none !important;
|
| 191 |
}
|
| 192 |
-
/* Remove blur and premium overlays
|
| 193 |
-
[class*="blur" i], [class*="premium" i], [class*="paywall" i], [class*="sample-preview-blur" i]
|
| 194 |
-
display: none !important;
|
| 195 |
filter: none !important;
|
| 196 |
backdrop-filter: none !important;
|
| 197 |
opacity: 1 !important;
|
| 198 |
visibility: visible !important;
|
| 199 |
}
|
| 200 |
-
/* Ensure document content is visible */
|
| 201 |
.document-content, .page-content, [data-page] {
|
| 202 |
filter: none !important;
|
| 203 |
opacity: 1 !important;
|
| 204 |
visibility: visible !important;
|
| 205 |
pointer-events: auto !important;
|
| 206 |
}
|
| 207 |
-
/* Remove fixed overlays */
|
| 208 |
.fixed-overlay, .sticky-overlay, .content-overlay {
|
| 209 |
display: none !important;
|
| 210 |
}
|
| 211 |
-
/* Restore scrolling */
|
| 212 |
html, body {
|
| 213 |
overflow: auto !important;
|
| 214 |
position: static !important;
|
|
@@ -220,24 +224,20 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 220 |
`
|
| 221 |
});
|
| 222 |
|
| 223 |
-
// Step 3: Inject JavaScript to handle dynamic cookie banners (Unchanged)
|
| 224 |
await page.evaluateOnNewDocument(() => {
|
| 225 |
-
// Override common cookie consent functions
|
| 226 |
window.cookieConsent = { accepted: true };
|
| 227 |
window.gtag = () => { };
|
| 228 |
window.ga = () => { };
|
| 229 |
window.dataLayer = [];
|
| 230 |
|
| 231 |
-
// Mutation observer to catch dynamically added cookie banners
|
| 232 |
const observer = new MutationObserver((mutations) => {
|
| 233 |
mutations.forEach((mutation) => {
|
| 234 |
mutation.addedNodes.forEach((node) => {
|
| 235 |
-
if (node.nodeType === 1) {
|
| 236 |
const element = node;
|
| 237 |
const text = element.textContent || '';
|
| 238 |
const className = element.className || '';
|
| 239 |
const id = element.id || '';
|
| 240 |
-
// Check if this looks like a cookie banner
|
| 241 |
if (
|
| 242 |
text.toLowerCase().includes('cookie') ||
|
| 243 |
text.toLowerCase().includes('consent') ||
|
|
@@ -257,7 +257,6 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 257 |
});
|
| 258 |
observer.observe(document.body, { childList: true, subtree: true });
|
| 259 |
|
| 260 |
-
// Set up periodic cleanup
|
| 261 |
setInterval(() => {
|
| 262 |
const cookieElements = document.querySelectorAll(`
|
| 263 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i],
|
|
@@ -265,7 +264,6 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 265 |
.cmp-banner, .cc-banner
|
| 266 |
`);
|
| 267 |
cookieElements.forEach(el => el.remove());
|
| 268 |
-
// Restore body scroll
|
| 269 |
document.body.style.overflow = 'auto';
|
| 270 |
document.documentElement.style.overflow = 'auto';
|
| 271 |
}, 1000);
|
|
@@ -374,13 +372,11 @@ const applyPrintStyles = async (page, progressTracker) => {
|
|
| 374 |
style.id = "print-style-extension";
|
| 375 |
style.innerHTML = `
|
| 376 |
@page {
|
| 377 |
-
/* Set page size to A4 and remove default margins */
|
| 378 |
size: A4 portrait;
|
| 379 |
margin: 0mm;
|
| 380 |
}
|
| 381 |
@media print {
|
| 382 |
html, body {
|
| 383 |
-
/* Ensure the body takes the full width and has no extra padding/margin */
|
| 384 |
width: 210mm !important;
|
| 385 |
height: auto !important;
|
| 386 |
margin: 0 !important;
|
|
@@ -391,7 +387,6 @@ const applyPrintStyles = async (page, progressTracker) => {
|
|
| 391 |
display: flex;
|
| 392 |
justify-content: center;
|
| 393 |
}
|
| 394 |
-
/* Remove all unwanted elements like headers, footers, sidebars, etc. */
|
| 395 |
header, footer, nav, aside, .no-print, .ads, .sidebar, .premium-banner,
|
| 396 |
[class*="Header"], [class*="Footer"], [class*="Sidebar"], [id*="Header"],
|
| 397 |
.ViewerToolbar, .Layout_info-bar-wrapper__He0Ho, .Sidebar_sidebar-scrollable__kqeBZ,
|
|
@@ -401,17 +396,11 @@ const applyPrintStyles = async (page, progressTracker) => {
|
|
| 401 |
.Layout_sidebar-wrapper__unavM, .Layout_is-open__9DQr4 {
|
| 402 |
display: none !important;
|
| 403 |
}
|
| 404 |
-
/* Force all elements to have a transparent background and no shadow */
|
| 405 |
* {
|
| 406 |
box-shadow: none !important;
|
| 407 |
background: transparent !important;
|
| 408 |
color: inherit !important;
|
| 409 |
}
|
| 410 |
-
/*
|
| 411 |
-
* KEY FIX: Target the main document container.
|
| 412 |
-
* Force it to be a block element, remove any transforms or max-widths,
|
| 413 |
-
* and center it perfectly within the page.
|
| 414 |
-
*/
|
| 415 |
.Viewer_document-wrapper__JPBWQ, .Viewer_document-wrapper__LXzoQ,
|
| 416 |
.Viewer_document-wrapper__XsO4j, .page-content, .document-viewer, #page-container {
|
| 417 |
position: static !important;
|
|
@@ -420,10 +409,9 @@ const applyPrintStyles = async (page, progressTracker) => {
|
|
| 420 |
max-width: none !important;
|
| 421 |
margin: 0 auto !important; /* Center horizontally */
|
| 422 |
padding: 0 !important;
|
| 423 |
-
box-sizing: border-box;
|
| 424 |
transform: none !important;
|
| 425 |
}
|
| 426 |
-
/* Ensure individual pages and images within the document use the full width */
|
| 427 |
[data-page], .page, .document-page, img {
|
| 428 |
page-break-after: always !important;
|
| 429 |
page-break-inside: avoid !important;
|
|
@@ -445,12 +433,18 @@ const applyPrintStyles = async (page, progressTracker) => {
|
|
| 445 |
|
| 446 |
const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
| 447 |
let browser;
|
|
|
|
| 448 |
try {
|
| 449 |
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 450 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
console.log("π Launching browser with enhanced stealth configuration...");
|
| 452 |
-
browser = await puppeteerExtra.launch({
|
| 453 |
headless: true,
|
|
|
|
| 454 |
args: [
|
| 455 |
'--no-sandbox',
|
| 456 |
'--disable-setuid-sandbox',
|
|
@@ -490,10 +484,9 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 490 |
|
| 491 |
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 492 |
|
| 493 |
-
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
| 494 |
-
await page.setViewport({ width:
|
| 495 |
|
| 496 |
-
// NOTE: Stealth plugin handles most of this, but keeping for extra safety
|
| 497 |
await page.evaluateOnNewDocument(() => {
|
| 498 |
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 499 |
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
|
@@ -512,10 +505,9 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 512 |
};
|
| 513 |
});
|
| 514 |
|
| 515 |
-
// Set up cookie and content bypass
|
| 516 |
await bypassCookiesAndRestrictions(page, progressTracker);
|
| 517 |
|
| 518 |
-
// Block unnecessary resources (UPDATED:
|
| 519 |
await page.setRequestInterception(true);
|
| 520 |
page.on('request', (req) => {
|
| 521 |
const resourceType = req.resourceType();
|
|
@@ -535,7 +527,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 535 |
if (
|
| 536 |
['image', 'media', 'font', 'stylesheet'].includes(resourceType) && // Block non-essential images/media/fonts/styles early if not core
|
| 537 |
!reqUrl.includes('document') && !reqUrl.includes('page') && !reqUrl.includes('studocu') || // Allow core document images
|
| 538 |
-
resourceType === 'script' && !reqUrl.includes('studocu')
|
| 539 |
reqUrl.includes('doubleclick') ||
|
| 540 |
reqUrl.includes('googletagmanager') ||
|
| 541 |
reqUrl.includes('facebook.com') ||
|
|
@@ -554,21 +546,18 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 554 |
}
|
| 555 |
});
|
| 556 |
|
| 557 |
-
// Login if credentials provided
|
| 558 |
if (options.email && options.password) {
|
| 559 |
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 560 |
|
| 561 |
console.log("π Logging in to StuDocu...");
|
| 562 |
-
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded', timeout:
|
| 563 |
-
|
| 564 |
-
await handleCloudflareChallenge(page, progressTracker);
|
| 565 |
-
await page.waitForSelector('#email', { timeout: 15000 });
|
| 566 |
await page.type('#email', options.email);
|
| 567 |
await page.type('#password', options.password);
|
| 568 |
await page.click('button[type="submit"]');
|
| 569 |
try {
|
| 570 |
-
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout:
|
| 571 |
-
await page.waitForSelector('.user-profile, [data-testid="user-menu"]', { timeout:
|
| 572 |
console.log("β
Login successful.");
|
| 573 |
progressTracker?.updateProgress(18, 'authenticated', 'Login successful');
|
| 574 |
} catch (e) {
|
|
@@ -589,25 +578,21 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 589 |
attempts++;
|
| 590 |
progressTracker?.updateProgress(30 + (attempts * 5), 'navigating', `Navigation attempt ${attempts}/${maxAttempts}`);
|
| 591 |
console.log(`Navigation attempt ${attempts}/${maxAttempts}`);
|
| 592 |
-
await page.goto(url, { waitUntil: 'domcontentloaded', timeout:
|
| 593 |
navigationSuccess = true;
|
| 594 |
} catch (e) {
|
| 595 |
console.log(`Navigation attempt ${attempts} failed:`, e.message);
|
| 596 |
if (attempts >= maxAttempts) throw e;
|
| 597 |
-
await new Promise(resolve => setTimeout(resolve,
|
| 598 |
}
|
| 599 |
}
|
| 600 |
|
| 601 |
-
// NEW: Handle Cloudflare after navigation
|
| 602 |
-
await handleCloudflareChallenge(page, progressTracker);
|
| 603 |
-
|
| 604 |
progressTracker?.updateProgress(40, 'loading', 'Page loaded, waiting for content...');
|
| 605 |
-
await new Promise(resolve => setTimeout(resolve,
|
| 606 |
|
| 607 |
// Apply content unblurring
|
| 608 |
await unblurContent(page, progressTracker);
|
| 609 |
|
| 610 |
-
// Wait for document content
|
| 611 |
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 612 |
console.log("β³ Waiting for document content to load...");
|
| 613 |
|
|
@@ -618,7 +603,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 618 |
let contentFound = false;
|
| 619 |
for (const selector of contentSelectors) {
|
| 620 |
try {
|
| 621 |
-
await page.waitForSelector(selector, { timeout:
|
| 622 |
console.log(`β
Found content with selector: ${selector}`);
|
| 623 |
contentFound = true;
|
| 624 |
break;
|
|
@@ -631,7 +616,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 631 |
console.log("β οΈ No specific content selector found, proceeding with page content...");
|
| 632 |
}
|
| 633 |
|
| 634 |
-
// Enhanced scrolling to load all content (Optimized: Increased scroll distance, reduced delays)
|
| 635 |
progressTracker?.updateProgress(50, 'scrolling', 'Loading all document pages...');
|
| 636 |
console.log("π Loading all document pages with enhanced slow scroll...");
|
| 637 |
|
|
@@ -640,28 +624,26 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 640 |
let scrollHeight = document.body.scrollHeight;
|
| 641 |
while (true) {
|
| 642 |
let totalHeight = 0;
|
| 643 |
-
const distance = 600;
|
| 644 |
while (totalHeight < scrollHeight) {
|
| 645 |
window.scrollBy(0, distance);
|
| 646 |
totalHeight += distance;
|
| 647 |
-
await delay(
|
| 648 |
}
|
| 649 |
-
await delay(
|
| 650 |
const newHeight = document.body.scrollHeight;
|
| 651 |
if (newHeight === scrollHeight) break;
|
| 652 |
scrollHeight = newHeight;
|
| 653 |
}
|
| 654 |
window.scrollTo({ top: 0, behavior: "smooth" });
|
| 655 |
-
await delay(
|
| 656 |
});
|
| 657 |
|
| 658 |
-
// Re-apply unblur after loading new content
|
| 659 |
await unblurContent(page, progressTracker);
|
| 660 |
|
| 661 |
// New: Fetch clear images for blurred pages
|
| 662 |
await fetchClearImages(page, progressTracker);
|
| 663 |
|
| 664 |
-
// Wait for all images to load (Optimized: Reduced per-image timeout, parallel wait)
|
| 665 |
progressTracker?.updateProgress(75, 'loading_images', 'Loading images...');
|
| 666 |
console.log("πΌοΈ Waiting for all images to load...");
|
| 667 |
|
|
@@ -672,15 +654,14 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 672 |
return new Promise((resolve) => {
|
| 673 |
img.addEventListener('load', resolve);
|
| 674 |
img.addEventListener('error', resolve);
|
| 675 |
-
setTimeout(resolve,
|
| 676 |
});
|
| 677 |
}));
|
| 678 |
});
|
| 679 |
|
| 680 |
-
await new Promise(resolve => setTimeout(resolve,
|
| 681 |
progressTracker?.updateProgress(80, 'finalizing', 'Preparing document for PDF generation...');
|
| 682 |
|
| 683 |
-
// Set exact height
|
| 684 |
await page.evaluate(() => {
|
| 685 |
const getDocumentHeight = () => Math.max(
|
| 686 |
document.body.scrollHeight, document.body.offsetHeight,
|
|
@@ -692,7 +673,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 692 |
document.body.style.overflow = 'hidden !important';
|
| 693 |
});
|
| 694 |
|
| 695 |
-
// Content verification (Unchanged, as it's quick)
|
| 696 |
const contentCheck = await page.evaluate(() => {
|
| 697 |
const textContent = document.body.textContent || '';
|
| 698 |
const images = document.querySelectorAll('img');
|
|
@@ -719,7 +699,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 719 |
console.warn("β οΈ Warning: Limited document content detected.");
|
| 720 |
}
|
| 721 |
|
| 722 |
-
// Apply print styles and generate PDF
|
| 723 |
await applyPrintStyles(page, progressTracker);
|
| 724 |
await page.emulateMediaType('print');
|
| 725 |
|
|
@@ -728,9 +707,9 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 728 |
|
| 729 |
const pdfBuffer = await page.pdf({
|
| 730 |
printBackground: true,
|
| 731 |
-
preferCSSPageSize: true,
|
| 732 |
displayHeaderFooter: false,
|
| 733 |
-
timeout:
|
| 734 |
scale: 1,
|
| 735 |
omitBackground: false
|
| 736 |
});
|
|
@@ -752,10 +731,19 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 752 |
console.log("Error closing browser:", e.message);
|
| 753 |
}
|
| 754 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 755 |
}
|
| 756 |
};
|
| 757 |
|
| 758 |
-
// --- API Routes ---
|
| 759 |
app.post('/api/request-download', (req, res) => {
|
| 760 |
const { url, email, password } = req.body;
|
| 761 |
if (!url || !url.includes('studocu.com')) {
|
|
@@ -766,25 +754,30 @@ app.post('/api/request-download', (req, res) => {
|
|
| 766 |
const progressTracker = new ProgressTracker(sessionId);
|
| 767 |
|
| 768 |
progressTrackers.set(sessionId, progressTracker);
|
| 769 |
-
downloadJobs.set(sessionId, { status: 'processing' });
|
| 770 |
|
| 771 |
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 772 |
|
| 773 |
-
// Respond to the client immediately with the session ID
|
| 774 |
res.json({ sessionId });
|
| 775 |
|
| 776 |
-
// --- Start the PDF generation in the background ---
|
| 777 |
studocuDownloader(url, { email, password }, progressTracker)
|
| 778 |
.then(pdfBuffer => {
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
progressTrackers.delete(sessionId); // Clean up live tracker
|
| 782 |
})
|
| 783 |
.catch(error => {
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
progressTrackers.delete(sessionId); // Clean up live tracker
|
| 787 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 788 |
});
|
| 789 |
|
| 790 |
app.get('/api/progress/:sessionId', (req, res) => {
|
|
@@ -792,7 +785,6 @@ app.get('/api/progress/:sessionId', (req, res) => {
|
|
| 792 |
const tracker = progressTrackers.get(sessionId);
|
| 793 |
|
| 794 |
if (tracker) {
|
| 795 |
-
// Job is in progress, return live data
|
| 796 |
return res.json({
|
| 797 |
sessionId,
|
| 798 |
progress: tracker.progress,
|
|
@@ -804,7 +796,6 @@ app.get('/api/progress/:sessionId', (req, res) => {
|
|
| 804 |
|
| 805 |
const job = downloadJobs.get(sessionId);
|
| 806 |
if (job) {
|
| 807 |
-
// Job is finished, return final state
|
| 808 |
if (job.status === 'completed') {
|
| 809 |
return res.json({ sessionId, progress: 100, status: 'completed', message: 'PDF generated successfully!' });
|
| 810 |
}
|
|
@@ -825,7 +816,12 @@ app.get('/api/download/:sessionId', (req, res) => {
|
|
| 825 |
}
|
| 826 |
|
| 827 |
if (job.status === 'processing') {
|
| 828 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 829 |
}
|
| 830 |
|
| 831 |
if (job.status === 'error') {
|
|
@@ -836,14 +832,13 @@ app.get('/api/download/:sessionId', (req, res) => {
|
|
| 836 |
res.setHeader('Content-Type', 'application/pdf');
|
| 837 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 838 |
res.send(job.buffer);
|
| 839 |
-
//
|
| 840 |
-
// downloadJobs.delete(sessionId);
|
| 841 |
} else {
|
| 842 |
res.status(500).json({ error: 'An unknown error occurred.' });
|
| 843 |
}
|
| 844 |
});
|
| 845 |
|
| 846 |
-
// --- Health and Info Endpoints
|
| 847 |
app.get('/health', (req, res) => {
|
| 848 |
res.json({
|
| 849 |
status: 'healthy',
|
|
@@ -855,17 +850,16 @@ app.get('/health', (req, res) => {
|
|
| 855 |
|
| 856 |
app.get('/', (req, res) => {
|
| 857 |
res.json({
|
| 858 |
-
message: 'π Enhanced StuDocu Downloader API v5.
|
| 859 |
-
version: '5.
|
| 860 |
features: [
|
|
|
|
| 861 |
'πͺ Advanced cookie banner bypass',
|
| 862 |
'π Premium content unblurring',
|
| 863 |
'π Login support for full access',
|
| 864 |
'π Real-time progress tracking via polling',
|
| 865 |
'π Clean PDF generation with print styles',
|
| 866 |
-
'π΅οΈ Enhanced stealth to evade bot detection'
|
| 867 |
-
'βοΈ Automatic Cloudflare challenge handling',
|
| 868 |
-
'π§ Human-like behavior simulation'
|
| 869 |
],
|
| 870 |
endpoints: {
|
| 871 |
request: 'POST /api/request-download (body: {url, filename?, email?, password?})',
|
|
@@ -887,6 +881,6 @@ process.on('SIGINT', () => {
|
|
| 887 |
});
|
| 888 |
|
| 889 |
app.listen(port, () => {
|
| 890 |
-
console.log(`π Enhanced StuDocu Downloader v5.
|
| 891 |
-
console.log(`β¨ Features: Real-time progress tracking, enhanced stealth,
|
| 892 |
});
|
|
|
|
| 1 |
const express = require('express');
|
| 2 |
+
const puppeteerExtra = require('puppeteer-extra');
|
| 3 |
+
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
|
| 4 |
+
const RecaptchaPlugin = require('puppeteer-extra-plugin-recaptcha');
|
| 5 |
const cors = require('cors');
|
| 6 |
const { EventEmitter } = require('events');
|
| 7 |
+
const os = require('os');
|
| 8 |
+
const fs = require('fs').promises;
|
| 9 |
+
const path = require('path');
|
| 10 |
+
|
| 11 |
+
puppeteerExtra.use(
|
| 12 |
+
RecaptchaPlugin({
|
| 13 |
+
provider: { id: '2captcha', token: process.env.TWOCAPTCHA_API_KEY || 'YOUR_2CAPTCHA_API_KEY' },
|
| 14 |
+
throwOnError: false
|
| 15 |
+
})
|
| 16 |
+
);
|
| 17 |
+
puppeteerExtra.use(StealthPlugin());
|
| 18 |
|
| 19 |
const app = express();
|
| 20 |
const port = 7860;
|
|
|
|
| 22 |
app.use(cors());
|
| 23 |
app.use(express.json());
|
| 24 |
|
| 25 |
+
// --- Progress Tracking and Job Storage ---
|
| 26 |
const progressTrackers = new Map();
|
| 27 |
const downloadJobs = new Map();
|
| 28 |
|
|
|
|
| 165 |
progressTracker?.updateProgress(5, 'bypassing', 'Setting up cookie bypass...');
|
| 166 |
|
| 167 |
console.log("πͺ Starting comprehensive cookie and restriction bypass...");
|
|
|
|
| 168 |
const preCookies = [
|
| 169 |
{ name: 'cookieConsent', value: 'accepted', domain: '.studocu.com' },
|
| 170 |
{ name: 'cookie_consent', value: 'true', domain: '.studocu.com' },
|
|
|
|
| 183 |
}
|
| 184 |
}
|
| 185 |
|
| 186 |
+
// Step 2: Inject CSS to hide cookie banners immediately (Unchanged)
|
| 187 |
await page.addStyleTag({
|
| 188 |
content: `
|
|
|
|
| 189 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i], [aria-label*="cookie" i],
|
| 190 |
.gdpr-banner, .gdpr-popup, .gdpr-modal, .consent-banner, .consent-popup, .consent-modal, .privacy-banner, .privacy-popup, .privacy-modal,
|
| 191 |
.cookie-law, .cookie-policy, .cookie-compliance, .onetrust-banner-sdk, #onetrust-consent-sdk, .cmp-banner, .cmp-popup, .cmp-modal,
|
|
|
|
| 197 |
z-index: -9999 !important;
|
| 198 |
pointer-events: none !important;
|
| 199 |
}
|
| 200 |
+
/* Remove blur and premium overlays */
|
| 201 |
+
[class*="blur" i], [class*="premium" i], [class*="paywall" i], [class*="sample-preview-blur" i] {
|
|
|
|
| 202 |
filter: none !important;
|
| 203 |
backdrop-filter: none !important;
|
| 204 |
opacity: 1 !important;
|
| 205 |
visibility: visible !important;
|
| 206 |
}
|
|
|
|
| 207 |
.document-content, .page-content, [data-page] {
|
| 208 |
filter: none !important;
|
| 209 |
opacity: 1 !important;
|
| 210 |
visibility: visible !important;
|
| 211 |
pointer-events: auto !important;
|
| 212 |
}
|
|
|
|
| 213 |
.fixed-overlay, .sticky-overlay, .content-overlay {
|
| 214 |
display: none !important;
|
| 215 |
}
|
|
|
|
| 216 |
html, body {
|
| 217 |
overflow: auto !important;
|
| 218 |
position: static !important;
|
|
|
|
| 224 |
`
|
| 225 |
});
|
| 226 |
|
|
|
|
| 227 |
await page.evaluateOnNewDocument(() => {
|
|
|
|
| 228 |
window.cookieConsent = { accepted: true };
|
| 229 |
window.gtag = () => { };
|
| 230 |
window.ga = () => { };
|
| 231 |
window.dataLayer = [];
|
| 232 |
|
|
|
|
| 233 |
const observer = new MutationObserver((mutations) => {
|
| 234 |
mutations.forEach((mutation) => {
|
| 235 |
mutation.addedNodes.forEach((node) => {
|
| 236 |
+
if (node.nodeType === 1) {
|
| 237 |
const element = node;
|
| 238 |
const text = element.textContent || '';
|
| 239 |
const className = element.className || '';
|
| 240 |
const id = element.id || '';
|
|
|
|
| 241 |
if (
|
| 242 |
text.toLowerCase().includes('cookie') ||
|
| 243 |
text.toLowerCase().includes('consent') ||
|
|
|
|
| 257 |
});
|
| 258 |
observer.observe(document.body, { childList: true, subtree: true });
|
| 259 |
|
|
|
|
| 260 |
setInterval(() => {
|
| 261 |
const cookieElements = document.querySelectorAll(`
|
| 262 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i],
|
|
|
|
| 264 |
.cmp-banner, .cc-banner
|
| 265 |
`);
|
| 266 |
cookieElements.forEach(el => el.remove());
|
|
|
|
| 267 |
document.body.style.overflow = 'auto';
|
| 268 |
document.documentElement.style.overflow = 'auto';
|
| 269 |
}, 1000);
|
|
|
|
| 372 |
style.id = "print-style-extension";
|
| 373 |
style.innerHTML = `
|
| 374 |
@page {
|
|
|
|
| 375 |
size: A4 portrait;
|
| 376 |
margin: 0mm;
|
| 377 |
}
|
| 378 |
@media print {
|
| 379 |
html, body {
|
|
|
|
| 380 |
width: 210mm !important;
|
| 381 |
height: auto !important;
|
| 382 |
margin: 0 !important;
|
|
|
|
| 387 |
display: flex;
|
| 388 |
justify-content: center;
|
| 389 |
}
|
|
|
|
| 390 |
header, footer, nav, aside, .no-print, .ads, .sidebar, .premium-banner,
|
| 391 |
[class*="Header"], [class*="Footer"], [class*="Sidebar"], [id*="Header"],
|
| 392 |
.ViewerToolbar, .Layout_info-bar-wrapper__He0Ho, .Sidebar_sidebar-scrollable__kqeBZ,
|
|
|
|
| 396 |
.Layout_sidebar-wrapper__unavM, .Layout_is-open__9DQr4 {
|
| 397 |
display: none !important;
|
| 398 |
}
|
|
|
|
| 399 |
* {
|
| 400 |
box-shadow: none !important;
|
| 401 |
background: transparent !important;
|
| 402 |
color: inherit !important;
|
| 403 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
.Viewer_document-wrapper__JPBWQ, .Viewer_document-wrapper__LXzoQ,
|
| 405 |
.Viewer_document-wrapper__XsO4j, .page-content, .document-viewer, #page-container {
|
| 406 |
position: static !important;
|
|
|
|
| 409 |
max-width: none !important;
|
| 410 |
margin: 0 auto !important; /* Center horizontally */
|
| 411 |
padding: 0 !important;
|
| 412 |
+
box-sizing: border-box;
|
| 413 |
transform: none !important;
|
| 414 |
}
|
|
|
|
| 415 |
[data-page], .page, .document-page, img {
|
| 416 |
page-break-after: always !important;
|
| 417 |
page-break-inside: avoid !important;
|
|
|
|
| 433 |
|
| 434 |
const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
| 435 |
let browser;
|
| 436 |
+
let userDataDir = null;
|
| 437 |
try {
|
| 438 |
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 439 |
|
| 440 |
+
const tempDir = os.tmpdir();
|
| 441 |
+
userDataDir = await fs.mkdtemp(path.join(tempDir, 'puppeteer-'));
|
| 442 |
+
console.log(`π Created temporary user data directory: ${userDataDir}`);
|
| 443 |
+
|
| 444 |
console.log("π Launching browser with enhanced stealth configuration...");
|
| 445 |
+
browser = await puppeteerExtra.launch({
|
| 446 |
headless: true,
|
| 447 |
+
userDataDir: userDataDir,
|
| 448 |
args: [
|
| 449 |
'--no-sandbox',
|
| 450 |
'--disable-setuid-sandbox',
|
|
|
|
| 484 |
|
| 485 |
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 486 |
|
| 487 |
+
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36');
|
| 488 |
+
await page.setViewport({ width: 794, height: 1122 }); // A4 size in pixels at 96 DPI
|
| 489 |
|
|
|
|
| 490 |
await page.evaluateOnNewDocument(() => {
|
| 491 |
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 492 |
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
|
|
|
| 505 |
};
|
| 506 |
});
|
| 507 |
|
|
|
|
| 508 |
await bypassCookiesAndRestrictions(page, progressTracker);
|
| 509 |
|
| 510 |
+
// Block unnecessary resources (UPDATED: Block more aggressively, including scripts, fonts, and stylesheets if not critical)
|
| 511 |
await page.setRequestInterception(true);
|
| 512 |
page.on('request', (req) => {
|
| 513 |
const resourceType = req.resourceType();
|
|
|
|
| 527 |
if (
|
| 528 |
['image', 'media', 'font', 'stylesheet'].includes(resourceType) && // Block non-essential images/media/fonts/styles early if not core
|
| 529 |
!reqUrl.includes('document') && !reqUrl.includes('page') && !reqUrl.includes('studocu') || // Allow core document images
|
| 530 |
+
resourceType === 'script' && !reqUrl.includes('studocu') || // Block third-party scripts
|
| 531 |
reqUrl.includes('doubleclick') ||
|
| 532 |
reqUrl.includes('googletagmanager') ||
|
| 533 |
reqUrl.includes('facebook.com') ||
|
|
|
|
| 546 |
}
|
| 547 |
});
|
| 548 |
|
|
|
|
| 549 |
if (options.email && options.password) {
|
| 550 |
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 551 |
|
| 552 |
console.log("π Logging in to StuDocu...");
|
| 553 |
+
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded', timeout: 60000 }); // Reduced timeout from 120000
|
| 554 |
+
await page.waitForSelector('#email', { timeout: 10000 }); // Reduced from 15000
|
|
|
|
|
|
|
| 555 |
await page.type('#email', options.email);
|
| 556 |
await page.type('#password', options.password);
|
| 557 |
await page.click('button[type="submit"]');
|
| 558 |
try {
|
| 559 |
+
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 }); // Reduced from 30000
|
| 560 |
+
await page.waitForSelector('.user-profile, [data-testid="user-menu"]', { timeout: 5000 }); // Reduced from 10000
|
| 561 |
console.log("β
Login successful.");
|
| 562 |
progressTracker?.updateProgress(18, 'authenticated', 'Login successful');
|
| 563 |
} catch (e) {
|
|
|
|
| 578 |
attempts++;
|
| 579 |
progressTracker?.updateProgress(30 + (attempts * 5), 'navigating', `Navigation attempt ${attempts}/${maxAttempts}`);
|
| 580 |
console.log(`Navigation attempt ${attempts}/${maxAttempts}`);
|
| 581 |
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 }); // Reduced timeout from 150000
|
| 582 |
navigationSuccess = true;
|
| 583 |
} catch (e) {
|
| 584 |
console.log(`Navigation attempt ${attempts} failed:`, e.message);
|
| 585 |
if (attempts >= maxAttempts) throw e;
|
| 586 |
+
await new Promise(resolve => setTimeout(resolve, 5000)); // Reduced retry delay from 15000 to 5000ms
|
| 587 |
}
|
| 588 |
}
|
| 589 |
|
|
|
|
|
|
|
|
|
|
| 590 |
progressTracker?.updateProgress(40, 'loading', 'Page loaded, waiting for content...');
|
| 591 |
+
await new Promise(resolve => setTimeout(resolve, 2000)); // Reduced from 5000ms
|
| 592 |
|
| 593 |
// Apply content unblurring
|
| 594 |
await unblurContent(page, progressTracker);
|
| 595 |
|
|
|
|
| 596 |
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 597 |
console.log("β³ Waiting for document content to load...");
|
| 598 |
|
|
|
|
| 603 |
let contentFound = false;
|
| 604 |
for (const selector of contentSelectors) {
|
| 605 |
try {
|
| 606 |
+
await page.waitForSelector(selector, { timeout: 10000 }); // Reduced from 20000
|
| 607 |
console.log(`β
Found content with selector: ${selector}`);
|
| 608 |
contentFound = true;
|
| 609 |
break;
|
|
|
|
| 616 |
console.log("β οΈ No specific content selector found, proceeding with page content...");
|
| 617 |
}
|
| 618 |
|
|
|
|
| 619 |
progressTracker?.updateProgress(50, 'scrolling', 'Loading all document pages...');
|
| 620 |
console.log("π Loading all document pages with enhanced slow scroll...");
|
| 621 |
|
|
|
|
| 624 |
let scrollHeight = document.body.scrollHeight;
|
| 625 |
while (true) {
|
| 626 |
let totalHeight = 0;
|
| 627 |
+
const distance = 600;
|
| 628 |
while (totalHeight < scrollHeight) {
|
| 629 |
window.scrollBy(0, distance);
|
| 630 |
totalHeight += distance;
|
| 631 |
+
await delay(200); // Reduced from 500ms
|
| 632 |
}
|
| 633 |
+
await delay(1000); // Reduced from 2000ms
|
| 634 |
const newHeight = document.body.scrollHeight;
|
| 635 |
if (newHeight === scrollHeight) break;
|
| 636 |
scrollHeight = newHeight;
|
| 637 |
}
|
| 638 |
window.scrollTo({ top: 0, behavior: "smooth" });
|
| 639 |
+
await delay(500); // Reduced from 1000ms
|
| 640 |
});
|
| 641 |
|
|
|
|
| 642 |
await unblurContent(page, progressTracker);
|
| 643 |
|
| 644 |
// New: Fetch clear images for blurred pages
|
| 645 |
await fetchClearImages(page, progressTracker);
|
| 646 |
|
|
|
|
| 647 |
progressTracker?.updateProgress(75, 'loading_images', 'Loading images...');
|
| 648 |
console.log("πΌοΈ Waiting for all images to load...");
|
| 649 |
|
|
|
|
| 654 |
return new Promise((resolve) => {
|
| 655 |
img.addEventListener('load', resolve);
|
| 656 |
img.addEventListener('error', resolve);
|
| 657 |
+
setTimeout(resolve, 5000); // Reduced from 15000ms
|
| 658 |
});
|
| 659 |
}));
|
| 660 |
});
|
| 661 |
|
| 662 |
+
await new Promise(resolve => setTimeout(resolve, 2000)); // Reduced from 5000ms
|
| 663 |
progressTracker?.updateProgress(80, 'finalizing', 'Preparing document for PDF generation...');
|
| 664 |
|
|
|
|
| 665 |
await page.evaluate(() => {
|
| 666 |
const getDocumentHeight = () => Math.max(
|
| 667 |
document.body.scrollHeight, document.body.offsetHeight,
|
|
|
|
| 673 |
document.body.style.overflow = 'hidden !important';
|
| 674 |
});
|
| 675 |
|
|
|
|
| 676 |
const contentCheck = await page.evaluate(() => {
|
| 677 |
const textContent = document.body.textContent || '';
|
| 678 |
const images = document.querySelectorAll('img');
|
|
|
|
| 699 |
console.warn("β οΈ Warning: Limited document content detected.");
|
| 700 |
}
|
| 701 |
|
|
|
|
| 702 |
await applyPrintStyles(page, progressTracker);
|
| 703 |
await page.emulateMediaType('print');
|
| 704 |
|
|
|
|
| 707 |
|
| 708 |
const pdfBuffer = await page.pdf({
|
| 709 |
printBackground: true,
|
| 710 |
+
preferCSSPageSize: true,
|
| 711 |
displayHeaderFooter: false,
|
| 712 |
+
timeout: 60000, // Reduced from 180000
|
| 713 |
scale: 1,
|
| 714 |
omitBackground: false
|
| 715 |
});
|
|
|
|
| 731 |
console.log("Error closing browser:", e.message);
|
| 732 |
}
|
| 733 |
}
|
| 734 |
+
if (userDataDir) {
|
| 735 |
+
console.log(`ποΈ Cleaning up temporary directory: ${userDataDir}`);
|
| 736 |
+
try {
|
| 737 |
+
await fs.rm(userDataDir, { recursive: true, force: true });
|
| 738 |
+
console.log("β
Temporary directory cleaned up.");
|
| 739 |
+
} catch (e) {
|
| 740 |
+
console.error(`β Failed to clean up temporary directory ${userDataDir}:`, e.message);
|
| 741 |
+
}
|
| 742 |
+
}
|
| 743 |
}
|
| 744 |
};
|
| 745 |
|
| 746 |
+
// --- API Routes ---
|
| 747 |
app.post('/api/request-download', (req, res) => {
|
| 748 |
const { url, email, password } = req.body;
|
| 749 |
if (!url || !url.includes('studocu.com')) {
|
|
|
|
| 754 |
const progressTracker = new ProgressTracker(sessionId);
|
| 755 |
|
| 756 |
progressTrackers.set(sessionId, progressTracker);
|
| 757 |
+
downloadJobs.set(sessionId, { status: 'processing', createdAt: Date.now() }); // MODIFIED: Added createdAt
|
| 758 |
|
| 759 |
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 760 |
|
|
|
|
| 761 |
res.json({ sessionId });
|
| 762 |
|
|
|
|
| 763 |
studocuDownloader(url, { email, password }, progressTracker)
|
| 764 |
.then(pdfBuffer => {
|
| 765 |
+
downloadJobs.set(sessionId, { status: 'completed', buffer: pdfBuffer, createdAt: Date.now() });
|
| 766 |
+
progressTrackers.delete(sessionId);
|
|
|
|
| 767 |
})
|
| 768 |
.catch(error => {
|
| 769 |
+
downloadJobs.set(sessionId, { status: 'error', message: error.message, createdAt: Date.now() });
|
| 770 |
+
progressTrackers.delete(sessionId);
|
|
|
|
| 771 |
});
|
| 772 |
+
|
| 773 |
+
// NEW: Timeout for job cleanup
|
| 774 |
+
setTimeout(() => {
|
| 775 |
+
if (downloadJobs.has(sessionId) && downloadJobs.get(sessionId).status === 'processing') {
|
| 776 |
+
downloadJobs.set(sessionId, { status: 'error', message: 'Job timed out after 5 minutes', createdAt: Date.now() });
|
| 777 |
+
progressTrackers.delete(sessionId);
|
| 778 |
+
console.log(`π Session ${sessionId} timed out`);
|
| 779 |
+
}
|
| 780 |
+
}, 300000); // 5 minutes
|
| 781 |
});
|
| 782 |
|
| 783 |
app.get('/api/progress/:sessionId', (req, res) => {
|
|
|
|
| 785 |
const tracker = progressTrackers.get(sessionId);
|
| 786 |
|
| 787 |
if (tracker) {
|
|
|
|
| 788 |
return res.json({
|
| 789 |
sessionId,
|
| 790 |
progress: tracker.progress,
|
|
|
|
| 796 |
|
| 797 |
const job = downloadJobs.get(sessionId);
|
| 798 |
if (job) {
|
|
|
|
| 799 |
if (job.status === 'completed') {
|
| 800 |
return res.json({ sessionId, progress: 100, status: 'completed', message: 'PDF generated successfully!' });
|
| 801 |
}
|
|
|
|
| 816 |
}
|
| 817 |
|
| 818 |
if (job.status === 'processing') {
|
| 819 |
+
const elapsed = Date.now() - job.createdAt;
|
| 820 |
+
if (elapsed > 300000) { // 5 minutes
|
| 821 |
+
downloadJobs.set(sessionId, { status: 'error', message: 'Download timed out after 5 minutes' });
|
| 822 |
+
return res.status(500).json({ error: 'Download timed out after 5 minutes.' });
|
| 823 |
+
}
|
| 824 |
+
return res.status(400).json({ error: 'Download is still processing. Please try again in a few seconds.' });
|
| 825 |
}
|
| 826 |
|
| 827 |
if (job.status === 'error') {
|
|
|
|
| 832 |
res.setHeader('Content-Type', 'application/pdf');
|
| 833 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 834 |
res.send(job.buffer);
|
| 835 |
+
downloadJobs.delete(sessionId); // MODIFIED: Clean up after successful download
|
|
|
|
| 836 |
} else {
|
| 837 |
res.status(500).json({ error: 'An unknown error occurred.' });
|
| 838 |
}
|
| 839 |
});
|
| 840 |
|
| 841 |
+
// --- Health and Info Endpoints ---
|
| 842 |
app.get('/health', (req, res) => {
|
| 843 |
res.json({
|
| 844 |
status: 'healthy',
|
|
|
|
| 850 |
|
| 851 |
app.get('/', (req, res) => {
|
| 852 |
res.json({
|
| 853 |
+
message: 'π Enhanced StuDocu Downloader API v5.2 - Real-time Progress Tracking with Stealth',
|
| 854 |
+
version: '5.2.0',
|
| 855 |
features: [
|
| 856 |
+
'π‘οΈ Cloudflare CAPTCHA bypass with 2Captcha',
|
| 857 |
'πͺ Advanced cookie banner bypass',
|
| 858 |
'π Premium content unblurring',
|
| 859 |
'π Login support for full access',
|
| 860 |
'π Real-time progress tracking via polling',
|
| 861 |
'π Clean PDF generation with print styles',
|
| 862 |
+
'π΅οΈ Enhanced stealth to evade bot detection'
|
|
|
|
|
|
|
| 863 |
],
|
| 864 |
endpoints: {
|
| 865 |
request: 'POST /api/request-download (body: {url, filename?, email?, password?})',
|
|
|
|
| 881 |
});
|
| 882 |
|
| 883 |
app.listen(port, () => {
|
| 884 |
+
console.log(`π Enhanced StuDocu Downloader v5.2.0 running on http://localhost:${port}`);
|
| 885 |
+
console.log(`β¨ Features: Real-time progress tracking, enhanced stealth, and user feedback`);
|
| 886 |
});
|