added captacha verification bypass in this
Browse files- .gitignore +2 -1
- index.html +2 -1
- package-lock.json +369 -5
- package.json +4 -1
- server.js +142 -83
.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,14 +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-recaptcha": "^3.6.8",
|
| 18 |
-
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
|
|
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
"nodemon": "^3.1.10",
|
|
@@ -23,6 +26,15 @@
|
|
| 23 |
"typescript": "^5.4.0"
|
| 24 |
}
|
| 25 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
"node_modules/@babel/code-frame": {
|
| 27 |
"version": "7.27.1",
|
| 28 |
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
|
@@ -73,6 +85,12 @@
|
|
| 73 |
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
|
| 74 |
"license": "MIT"
|
| 75 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
"node_modules/@types/debug": {
|
| 77 |
"version": "4.1.12",
|
| 78 |
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
|
@@ -93,7 +111,6 @@
|
|
| 93 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
|
| 94 |
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
|
| 95 |
"license": "MIT",
|
| 96 |
-
"optional": true,
|
| 97 |
"dependencies": {
|
| 98 |
"undici-types": "~7.10.0"
|
| 99 |
}
|
|
@@ -296,6 +313,26 @@
|
|
| 296 |
}
|
| 297 |
}
|
| 298 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
"node_modules/basic-ftp": {
|
| 300 |
"version": "5.0.5",
|
| 301 |
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
|
@@ -305,6 +342,16 @@
|
|
| 305 |
"node": ">=10.0.0"
|
| 306 |
}
|
| 307 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
"node_modules/binary-extensions": {
|
| 309 |
"version": "2.3.0",
|
| 310 |
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
|
@@ -361,6 +408,30 @@
|
|
| 361 |
"node": ">=8"
|
| 362 |
}
|
| 363 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
"node_modules/buffer-crc32": {
|
| 365 |
"version": "0.2.13",
|
| 366 |
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
@@ -442,6 +513,24 @@
|
|
| 442 |
"fsevents": "~2.3.2"
|
| 443 |
}
|
| 444 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
"node_modules/chromium-bidi": {
|
| 446 |
"version": "7.3.1",
|
| 447 |
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-7.3.1.tgz",
|
|
@@ -712,6 +801,18 @@
|
|
| 712 |
"integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==",
|
| 713 |
"license": "BSD-3-Clause"
|
| 714 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 715 |
"node_modules/dunder-proto": {
|
| 716 |
"version": "1.0.1",
|
| 717 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
@@ -891,6 +992,18 @@
|
|
| 891 |
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 892 |
"license": "MIT"
|
| 893 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 894 |
"node_modules/escodegen": {
|
| 895 |
"version": "2.1.0",
|
| 896 |
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
|
|
@@ -1331,6 +1444,17 @@
|
|
| 1331 |
"node": ">= 14"
|
| 1332 |
}
|
| 1333 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1334 |
"node_modules/glob": {
|
| 1335 |
"version": "7.2.3",
|
| 1336 |
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
@@ -1495,6 +1619,26 @@
|
|
| 1495 |
"node": ">=0.10.0"
|
| 1496 |
}
|
| 1497 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1498 |
"node_modules/ignore-by-default": {
|
| 1499 |
"version": "1.0.1",
|
| 1500 |
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
|
@@ -1578,6 +1722,21 @@
|
|
| 1578 |
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
| 1579 |
"license": "MIT"
|
| 1580 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1581 |
"node_modules/is-extendable": {
|
| 1582 |
"version": "0.1.1",
|
| 1583 |
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
|
@@ -1647,6 +1806,18 @@
|
|
| 1647 |
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 1648 |
"license": "MIT"
|
| 1649 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1650 |
"node_modules/isexe": {
|
| 1651 |
"version": "2.0.0",
|
| 1652 |
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
|
@@ -1720,6 +1891,16 @@
|
|
| 1720 |
"node": ">=0.10.0"
|
| 1721 |
}
|
| 1722 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1723 |
"node_modules/lines-and-columns": {
|
| 1724 |
"version": "1.2.4",
|
| 1725 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
@@ -1759,6 +1940,12 @@
|
|
| 1759 |
"integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
|
| 1760 |
"dev": true
|
| 1761 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1762 |
"node_modules/math-intrinsics": {
|
| 1763 |
"version": "1.1.0",
|
| 1764 |
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
@@ -1885,6 +2072,13 @@
|
|
| 1885 |
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 1886 |
"license": "MIT"
|
| 1887 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1888 |
"node_modules/negotiator": {
|
| 1889 |
"version": "1.0.0",
|
| 1890 |
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
|
@@ -1916,6 +2110,26 @@
|
|
| 1916 |
"dev": true,
|
| 1917 |
"license": "MIT"
|
| 1918 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1919 |
"node_modules/nodemon": {
|
| 1920 |
"version": "3.1.10",
|
| 1921 |
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
|
@@ -2414,6 +2628,20 @@
|
|
| 2414 |
}
|
| 2415 |
}
|
| 2416 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2417 |
"node_modules/qs": {
|
| 2418 |
"version": "6.14.0",
|
| 2419 |
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
|
@@ -2466,6 +2694,74 @@
|
|
| 2466 |
"node": ">=8.10.0"
|
| 2467 |
}
|
| 2468 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2469 |
"node_modules/require-directory": {
|
| 2470 |
"version": "2.1.1",
|
| 2471 |
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
|
@@ -2741,6 +3037,20 @@
|
|
| 2741 |
"node": ">=10"
|
| 2742 |
}
|
| 2743 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2744 |
"node_modules/smart-buffer": {
|
| 2745 |
"version": "4.2.0",
|
| 2746 |
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
|
@@ -2933,7 +3243,6 @@
|
|
| 2933 |
"version": "2.3.8",
|
| 2934 |
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
| 2935 |
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
| 2936 |
-
"dev": true,
|
| 2937 |
"license": "MIT"
|
| 2938 |
},
|
| 2939 |
"node_modules/to-regex-range": {
|
|
@@ -2968,6 +3277,21 @@
|
|
| 2968 |
"nodetouch": "bin/nodetouch.js"
|
| 2969 |
}
|
| 2970 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2971 |
"node_modules/tsc-watch": {
|
| 2972 |
"version": "6.3.1",
|
| 2973 |
"resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.3.1.tgz",
|
|
@@ -3036,6 +3360,16 @@
|
|
| 3036 |
"node": ">=14.17"
|
| 3037 |
}
|
| 3038 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3039 |
"node_modules/undefsafe": {
|
| 3040 |
"version": "2.0.5",
|
| 3041 |
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
|
@@ -3047,8 +3381,7 @@
|
|
| 3047 |
"version": "7.10.0",
|
| 3048 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
| 3049 |
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
| 3050 |
-
"license": "MIT"
|
| 3051 |
-
"optional": true
|
| 3052 |
},
|
| 3053 |
"node_modules/uni-global": {
|
| 3054 |
"version": "1.0.0",
|
|
@@ -3077,6 +3410,12 @@
|
|
| 3077 |
"node": ">= 0.8"
|
| 3078 |
}
|
| 3079 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3080 |
"node_modules/vary": {
|
| 3081 |
"version": "1.1.2",
|
| 3082 |
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
@@ -3086,6 +3425,22 @@
|
|
| 3086 |
"node": ">= 0.8"
|
| 3087 |
}
|
| 3088 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3089 |
"node_modules/which": {
|
| 3090 |
"version": "2.0.2",
|
| 3091 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
@@ -3146,6 +3501,15 @@
|
|
| 3146 |
}
|
| 3147 |
}
|
| 3148 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3149 |
"node_modules/y18n": {
|
| 3150 |
"version": "5.0.8",
|
| 3151 |
"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",
|
|
|
|
| 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,14 +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-recaptcha": "^3.6.8",
|
| 14 |
-
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
|
|
|
| 15 |
},
|
| 16 |
"devDependencies": {
|
| 17 |
"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,7 +1,6 @@
|
|
| 1 |
const express = require('express');
|
| 2 |
const puppeteerExtra = require('puppeteer-extra');
|
| 3 |
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
|
| 4 |
-
// NEW: Add the recaptcha plugin to help solve Cloudflare and other challenges
|
| 5 |
const RecaptchaPlugin = require('puppeteer-extra-plugin-recaptcha');
|
| 6 |
const cors = require('cors');
|
| 7 |
const { EventEmitter } = require('events');
|
|
@@ -9,24 +8,21 @@ const os = require('os');
|
|
| 9 |
const fs = require('fs').promises;
|
| 10 |
const path = require('path');
|
| 11 |
|
| 12 |
-
// --- NEW: Configuration for the Solver ---
|
| 13 |
-
// You can optionally provide a 2Captcha API key to solve more complex captchas,
|
| 14 |
-
// but it's often not needed for the initial Cloudflare JS challenge.
|
| 15 |
puppeteerExtra.use(
|
| 16 |
RecaptchaPlugin({
|
| 17 |
-
provider: { id: '2captcha', token: '
|
|
|
|
| 18 |
})
|
| 19 |
);
|
| 20 |
puppeteerExtra.use(StealthPlugin());
|
| 21 |
|
| 22 |
-
|
| 23 |
const app = express();
|
| 24 |
const port = 7860;
|
| 25 |
|
| 26 |
app.use(cors());
|
| 27 |
app.use(express.json());
|
| 28 |
|
| 29 |
-
// --- Progress Tracking and Job Storage
|
| 30 |
const progressTrackers = new Map();
|
| 31 |
const downloadJobs = new Map();
|
| 32 |
|
|
@@ -55,13 +51,75 @@ class ProgressTracker extends EventEmitter {
|
|
| 55 |
}
|
| 56 |
}
|
| 57 |
|
| 58 |
-
// ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
| 60 |
-
// This function remains largely the same but is now called *after* passing Cloudflare.
|
| 61 |
progressTracker?.updateProgress(5, 'bypassing', 'Setting up cookie bypass...');
|
| 62 |
-
|
| 63 |
console.log("πͺ Starting comprehensive cookie and restriction bypass...");
|
| 64 |
-
// Step 1: Set cookies before page load
|
| 65 |
const preCookies = [
|
| 66 |
{ name: 'cookieConsent', value: 'accepted', domain: '.studocu.com' },
|
| 67 |
{ name: 'cookie_consent', value: 'true', domain: '.studocu.com' },
|
|
@@ -80,10 +138,8 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 80 |
}
|
| 81 |
}
|
| 82 |
|
| 83 |
-
// Step 2: Inject CSS to hide cookie banners immediately
|
| 84 |
await page.addStyleTag({
|
| 85 |
content: `
|
| 86 |
-
/* Hide all possible cookie banners */
|
| 87 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i], [aria-label*="cookie" i],
|
| 88 |
.gdpr-banner, .gdpr-popup, .gdpr-modal, .consent-banner, .consent-popup, .consent-modal, .privacy-banner, .privacy-popup, .privacy-modal,
|
| 89 |
.cookie-law, .cookie-policy, .cookie-compliance, .onetrust-banner-sdk, #onetrust-consent-sdk, .cmp-banner, .cmp-popup, .cmp-modal,
|
|
@@ -95,25 +151,21 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 95 |
z-index: -9999 !important;
|
| 96 |
pointer-events: none !important;
|
| 97 |
}
|
| 98 |
-
/* Remove blur and premium overlays */
|
| 99 |
[class*="blur" i], [class*="premium" i], [class*="paywall" i], [class*="sample-preview-blur" i] {
|
| 100 |
filter: none !important;
|
| 101 |
backdrop-filter: none !important;
|
| 102 |
opacity: 1 !important;
|
| 103 |
visibility: visible !important;
|
| 104 |
}
|
| 105 |
-
/* Ensure document content is visible */
|
| 106 |
.document-content, .page-content, [data-page] {
|
| 107 |
filter: none !important;
|
| 108 |
opacity: 1 !important;
|
| 109 |
visibility: visible !important;
|
| 110 |
pointer-events: auto !important;
|
| 111 |
}
|
| 112 |
-
/* Remove fixed overlays */
|
| 113 |
.fixed-overlay, .sticky-overlay, .content-overlay {
|
| 114 |
display: none !important;
|
| 115 |
}
|
| 116 |
-
/* Restore scrolling */
|
| 117 |
html, body {
|
| 118 |
overflow: auto !important;
|
| 119 |
position: static !important;
|
|
@@ -121,24 +173,20 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 121 |
`
|
| 122 |
});
|
| 123 |
|
| 124 |
-
// Step 3: Inject JavaScript to handle dynamic cookie banners
|
| 125 |
await page.evaluateOnNewDocument(() => {
|
| 126 |
-
// Override common cookie consent functions
|
| 127 |
window.cookieConsent = { accepted: true };
|
| 128 |
window.gtag = () => { };
|
| 129 |
window.ga = () => { };
|
| 130 |
window.dataLayer = [];
|
| 131 |
|
| 132 |
-
// Mutation observer to catch dynamically added cookie banners
|
| 133 |
const observer = new MutationObserver((mutations) => {
|
| 134 |
mutations.forEach((mutation) => {
|
| 135 |
mutation.addedNodes.forEach((node) => {
|
| 136 |
-
if (node.nodeType === 1) {
|
| 137 |
const element = node;
|
| 138 |
const text = element.textContent || '';
|
| 139 |
const className = element.className || '';
|
| 140 |
const id = element.id || '';
|
| 141 |
-
// Check if this looks like a cookie banner
|
| 142 |
if (
|
| 143 |
text.toLowerCase().includes('cookie') ||
|
| 144 |
text.toLowerCase().includes('consent') ||
|
|
@@ -158,7 +206,6 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 158 |
});
|
| 159 |
observer.observe(document.body, { childList: true, subtree: true });
|
| 160 |
|
| 161 |
-
// Set up periodic cleanup
|
| 162 |
setInterval(() => {
|
| 163 |
const cookieElements = document.querySelectorAll(`
|
| 164 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i],
|
|
@@ -166,16 +213,15 @@ const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
| 166 |
.cmp-banner, .cc-banner
|
| 167 |
`);
|
| 168 |
cookieElements.forEach(el => el.remove());
|
| 169 |
-
// Restore body scroll
|
| 170 |
document.body.style.overflow = 'auto';
|
| 171 |
document.documentElement.style.overflow = 'auto';
|
| 172 |
}, 1000);
|
| 173 |
});
|
|
|
|
| 174 |
progressTracker?.updateProgress(10, 'bypassing', 'Cookie bypass configured successfully');
|
| 175 |
return true;
|
| 176 |
};
|
| 177 |
|
| 178 |
-
// --- Other functions (unblurContent, applyPrintStyles) are unchanged ---
|
| 179 |
const unblurContent = async (page, progressTracker) => {
|
| 180 |
progressTracker?.updateProgress(15, 'unblurring', 'Removing content restrictions...');
|
| 181 |
|
|
@@ -301,13 +347,9 @@ const applyPrintStyles = async (page, progressTracker) => {
|
|
| 301 |
progressTracker?.updateProgress(88, 'styling', 'Print styles applied successfully');
|
| 302 |
};
|
| 303 |
|
| 304 |
-
|
| 305 |
const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
| 306 |
let browser;
|
| 307 |
let userDataDir = null;
|
| 308 |
-
// NEW: Easy flag for debugging. Set to true to see the browser window.
|
| 309 |
-
const isDebugging = false;
|
| 310 |
-
|
| 311 |
try {
|
| 312 |
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 313 |
|
|
@@ -317,29 +359,47 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 317 |
|
| 318 |
console.log("π Launching browser with enhanced stealth configuration...");
|
| 319 |
browser = await puppeteerExtra.launch({
|
| 320 |
-
headless:
|
| 321 |
userDataDir: userDataDir,
|
| 322 |
args: [
|
| 323 |
'--no-sandbox',
|
| 324 |
'--disable-setuid-sandbox',
|
| 325 |
-
'--disable-infobars',
|
| 326 |
'--disable-dev-shm-usage',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
'--disable-blink-features=AutomationControlled',
|
| 328 |
-
'--
|
|
|
|
| 329 |
],
|
| 330 |
ignoreHTTPSErrors: true,
|
|
|
|
| 331 |
});
|
| 332 |
|
| 333 |
const page = await browser.newPage();
|
|
|
|
| 334 |
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 335 |
|
| 336 |
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');
|
| 337 |
-
await page.setViewport({ width:
|
| 338 |
|
| 339 |
-
|
| 340 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
|
| 342 |
-
// Request interception logic is unchanged
|
| 343 |
await page.setRequestInterception(true);
|
| 344 |
page.on('request', (req) => {
|
| 345 |
const resourceType = req.resourceType();
|
|
@@ -349,6 +409,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 349 |
req.continue();
|
| 350 |
return;
|
| 351 |
}
|
|
|
|
| 352 |
if (
|
| 353 |
['image', 'media', 'font', 'stylesheet'].includes(resourceType) &&
|
| 354 |
!reqUrl.includes('document') && !reqUrl.includes('page') && !reqUrl.includes('studocu') ||
|
|
@@ -371,48 +432,30 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 371 |
}
|
| 372 |
});
|
| 373 |
|
| 374 |
-
|
| 375 |
-
// --- MODIFIED NAVIGATION LOGIC ---
|
| 376 |
-
progressTracker?.updateProgress(5, 'navigating', 'Navigating to document...');
|
| 377 |
-
console.log(`π‘οΈ Navigating to ${url} and preparing for Cloudflare challenge...`);
|
| 378 |
-
try {
|
| 379 |
-
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 120000 });
|
| 380 |
-
|
| 381 |
-
// NEW: Wait for potential Cloudflare challenge to solve and redirect.
|
| 382 |
-
// We wait for an element that *only* exists on the actual Studocu page.
|
| 383 |
-
console.log("β³ Waiting for Cloudflare challenge to be solved...");
|
| 384 |
-
progressTracker?.updateProgress(8, 'solving_cf', 'Solving Cloudflare challenge...');
|
| 385 |
-
|
| 386 |
-
await page.waitForSelector('#search-input', { timeout: 90000 });
|
| 387 |
-
|
| 388 |
-
console.log("β
Cloudflare challenge passed! You are on the Studocu page.");
|
| 389 |
-
progressTracker?.updateProgress(10, 'navigation_complete', 'Successfully navigated to document');
|
| 390 |
-
|
| 391 |
-
} catch (e) {
|
| 392 |
-
console.error("β Failed to bypass Cloudflare or navigate to the page.", e.message);
|
| 393 |
-
// NEW: Take a screenshot on failure to help debug
|
| 394 |
-
const screenshotPath = path.join(os.tmpdir(), `cloudflare_failure_${Date.now()}.png`);
|
| 395 |
-
await page.screenshot({ path: screenshotPath, fullPage: true });
|
| 396 |
-
console.log(`πΈ Screenshot saved to ${screenshotPath}`);
|
| 397 |
-
throw new Error("Could not bypass Cloudflare. The site may be actively blocking, or the page structure changed.");
|
| 398 |
-
}
|
| 399 |
-
|
| 400 |
-
// --- RESUME NORMAL SCRIPT FLOW ---
|
| 401 |
-
|
| 402 |
-
// It's better to bypass cookies *after* landing on the actual page
|
| 403 |
-
await bypassCookiesAndRestrictions(page, progressTracker);
|
| 404 |
-
|
| 405 |
if (options.email && options.password) {
|
| 406 |
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 407 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
}
|
| 409 |
|
| 410 |
-
|
| 411 |
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
| 412 |
|
| 413 |
await unblurContent(page, progressTracker);
|
| 414 |
|
| 415 |
-
// ... (The rest of the script is unchanged)
|
| 416 |
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 417 |
console.log("β³ Waiting for document content to load...");
|
| 418 |
|
|
@@ -537,7 +580,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 537 |
console.log(`β
PDF generated successfully! Size: ${(pdfBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
| 538 |
return pdfBuffer;
|
| 539 |
|
| 540 |
-
|
| 541 |
} catch (error) {
|
| 542 |
progressTracker?.updateProgress(-1, 'error', error.message);
|
| 543 |
console.error("β Error during PDF generation:", error);
|
|
@@ -563,7 +605,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 563 |
}
|
| 564 |
};
|
| 565 |
|
| 566 |
-
// --- API Routes
|
| 567 |
app.post('/api/request-download', (req, res) => {
|
| 568 |
const { url, email, password } = req.body;
|
| 569 |
if (!url || !url.includes('studocu.com')) {
|
|
@@ -574,7 +616,7 @@ app.post('/api/request-download', (req, res) => {
|
|
| 574 |
const progressTracker = new ProgressTracker(sessionId);
|
| 575 |
|
| 576 |
progressTrackers.set(sessionId, progressTracker);
|
| 577 |
-
downloadJobs.set(sessionId, { status: 'processing' });
|
| 578 |
|
| 579 |
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 580 |
|
|
@@ -582,13 +624,22 @@ app.post('/api/request-download', (req, res) => {
|
|
| 582 |
|
| 583 |
studocuDownloader(url, { email, password }, progressTracker)
|
| 584 |
.then(pdfBuffer => {
|
| 585 |
-
downloadJobs.set(sessionId, { status: 'completed', buffer: pdfBuffer });
|
| 586 |
progressTrackers.delete(sessionId);
|
| 587 |
})
|
| 588 |
.catch(error => {
|
| 589 |
-
downloadJobs.set(sessionId, { status: 'error', message: error.message });
|
| 590 |
progressTrackers.delete(sessionId);
|
| 591 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 592 |
});
|
| 593 |
|
| 594 |
app.get('/api/progress/:sessionId', (req, res) => {
|
|
@@ -627,7 +678,12 @@ app.get('/api/download/:sessionId', (req, res) => {
|
|
| 627 |
}
|
| 628 |
|
| 629 |
if (job.status === 'processing') {
|
| 630 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 631 |
}
|
| 632 |
|
| 633 |
if (job.status === 'error') {
|
|
@@ -638,11 +694,13 @@ app.get('/api/download/:sessionId', (req, res) => {
|
|
| 638 |
res.setHeader('Content-Type', 'application/pdf');
|
| 639 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 640 |
res.send(job.buffer);
|
|
|
|
| 641 |
} else {
|
| 642 |
res.status(500).json({ error: 'An unknown error occurred.' });
|
| 643 |
}
|
| 644 |
});
|
| 645 |
|
|
|
|
| 646 |
app.get('/health', (req, res) => {
|
| 647 |
res.json({
|
| 648 |
status: 'healthy',
|
|
@@ -654,16 +712,17 @@ app.get('/health', (req, res) => {
|
|
| 654 |
|
| 655 |
app.get('/', (req, res) => {
|
| 656 |
res.json({
|
| 657 |
-
message: 'π Enhanced StuDocu Downloader API v5.
|
| 658 |
-
version: '5.
|
| 659 |
features: [
|
| 660 |
-
'π‘οΈ Cloudflare
|
| 661 |
'πͺ Advanced cookie banner bypass',
|
| 662 |
'π Premium content unblurring',
|
| 663 |
'π Login support for full access',
|
| 664 |
'π Real-time progress tracking via polling',
|
| 665 |
'π Clean PDF generation with print styles',
|
| 666 |
-
'π΅οΈ Enhanced stealth to evade bot detection'
|
|
|
|
| 667 |
],
|
| 668 |
endpoints: {
|
| 669 |
request: 'POST /api/request-download (body: {url, filename?, email?, password?})',
|
|
@@ -685,6 +744,6 @@ process.on('SIGINT', () => {
|
|
| 685 |
});
|
| 686 |
|
| 687 |
app.listen(port, () => {
|
| 688 |
-
console.log(`π Enhanced StuDocu Downloader v5.
|
| 689 |
-
console.log(`β¨ Features:
|
| 690 |
});
|
|
|
|
| 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');
|
|
|
|
| 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;
|
| 21 |
|
| 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 |
|
|
|
|
| 51 |
}
|
| 52 |
}
|
| 53 |
|
| 54 |
+
// --- Cloudflare Challenge Detection and Handling ---
|
| 55 |
+
const handleCloudflareChallenge = async (page, url, progressTracker, maxRetries = 2) => { // MODIFIED: Reduced maxRetries to 2
|
| 56 |
+
let retries = 0;
|
| 57 |
+
while (retries < maxRetries) {
|
| 58 |
+
try {
|
| 59 |
+
console.log(`π‘οΈ Attempt ${retries + 1}: Navigating to ${url}...`);
|
| 60 |
+
progressTracker?.updateProgress(30 + retries * 2, 'navigating', `Attempt ${retries + 1}: Navigating to document...`);
|
| 61 |
+
|
| 62 |
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
| 63 |
+
|
| 64 |
+
const isCloudflarePage = await page.evaluate(() => {
|
| 65 |
+
return document.querySelector('title')?.textContent.includes('Verifying you are human') ||
|
| 66 |
+
document.querySelector('#challenge-form') ||
|
| 67 |
+
document.querySelector('input[name="cf-turnstile-response"]');
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
if (isCloudflarePage) {
|
| 71 |
+
console.log("β³ Detected Cloudflare challenge page. Waiting for resolution...");
|
| 72 |
+
progressTracker?.updateProgress(32 + retries * 2, 'solving_cf', 'Solving Cloudflare challenge...');
|
| 73 |
+
|
| 74 |
+
try {
|
| 75 |
+
const { captchas, solved } = await page.solveRecaptchas();
|
| 76 |
+
if (solved.length > 0) {
|
| 77 |
+
console.log("β
CAPTCHA solved:", solved);
|
| 78 |
+
progressTracker?.updateProgress(34 + retries * 2, 'solving_cf', 'CAPTCHA solved successfully');
|
| 79 |
+
} else {
|
| 80 |
+
console.log("β οΈ No CAPTCHA solved, waiting for JS challenge...");
|
| 81 |
+
}
|
| 82 |
+
} catch (e) {
|
| 83 |
+
console.warn("β οΈ CAPTCHA solving failed:", e.message);
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 30000 }).catch(() => { });
|
| 87 |
+
|
| 88 |
+
const isOnTargetPage = await page.evaluate(() => {
|
| 89 |
+
return document.querySelector('#search-input') || document.querySelector('.document-content');
|
| 90 |
+
});
|
| 91 |
+
|
| 92 |
+
if (isOnTargetPage) {
|
| 93 |
+
console.log("β
Cloudflare challenge passed! On StuDocu page.");
|
| 94 |
+
progressTracker?.updateProgress(40, 'navigation_complete', 'Successfully navigated to document');
|
| 95 |
+
return true;
|
| 96 |
+
} else {
|
| 97 |
+
throw new Error("Navigation did not reach target page.");
|
| 98 |
+
}
|
| 99 |
+
} else {
|
| 100 |
+
console.log("β
No Cloudflare challenge detected. On StuDocu page.");
|
| 101 |
+
progressTracker?.updateProgress(40, 'navigation_complete', 'Successfully navigated to document');
|
| 102 |
+
return true;
|
| 103 |
+
}
|
| 104 |
+
} catch (e) {
|
| 105 |
+
console.error(`β Attempt ${retries + 1} failed:`, e.message);
|
| 106 |
+
retries++;
|
| 107 |
+
if (retries === maxRetries) {
|
| 108 |
+
const screenshotPath = path.join(os.tmpdir(), `cloudflare_failure_${Date.now()}.png`);
|
| 109 |
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
| 110 |
+
console.log(`πΈ Screenshot saved to ${screenshotPath}`);
|
| 111 |
+
throw new Error(`Failed to bypass Cloudflare after ${maxRetries} attempts: ${e.message}`);
|
| 112 |
+
}
|
| 113 |
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
};
|
| 117 |
+
|
| 118 |
+
// --- Puppeteer Logic ---
|
| 119 |
const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
|
|
|
| 120 |
progressTracker?.updateProgress(5, 'bypassing', 'Setting up cookie bypass...');
|
| 121 |
+
|
| 122 |
console.log("πͺ Starting comprehensive cookie and restriction bypass...");
|
|
|
|
| 123 |
const preCookies = [
|
| 124 |
{ name: 'cookieConsent', value: 'accepted', domain: '.studocu.com' },
|
| 125 |
{ name: 'cookie_consent', value: 'true', domain: '.studocu.com' },
|
|
|
|
| 138 |
}
|
| 139 |
}
|
| 140 |
|
|
|
|
| 141 |
await page.addStyleTag({
|
| 142 |
content: `
|
|
|
|
| 143 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i], [aria-label*="cookie" i],
|
| 144 |
.gdpr-banner, .gdpr-popup, .gdpr-modal, .consent-banner, .consent-popup, .consent-modal, .privacy-banner, .privacy-popup, .privacy-modal,
|
| 145 |
.cookie-law, .cookie-policy, .cookie-compliance, .onetrust-banner-sdk, #onetrust-consent-sdk, .cmp-banner, .cmp-popup, .cmp-modal,
|
|
|
|
| 151 |
z-index: -9999 !important;
|
| 152 |
pointer-events: none !important;
|
| 153 |
}
|
|
|
|
| 154 |
[class*="blur" i], [class*="premium" i], [class*="paywall" i], [class*="sample-preview-blur" i] {
|
| 155 |
filter: none !important;
|
| 156 |
backdrop-filter: none !important;
|
| 157 |
opacity: 1 !important;
|
| 158 |
visibility: visible !important;
|
| 159 |
}
|
|
|
|
| 160 |
.document-content, .page-content, [data-page] {
|
| 161 |
filter: none !important;
|
| 162 |
opacity: 1 !important;
|
| 163 |
visibility: visible !important;
|
| 164 |
pointer-events: auto !important;
|
| 165 |
}
|
|
|
|
| 166 |
.fixed-overlay, .sticky-overlay, .content-overlay {
|
| 167 |
display: none !important;
|
| 168 |
}
|
|
|
|
| 169 |
html, body {
|
| 170 |
overflow: auto !important;
|
| 171 |
position: static !important;
|
|
|
|
| 173 |
`
|
| 174 |
});
|
| 175 |
|
|
|
|
| 176 |
await page.evaluateOnNewDocument(() => {
|
|
|
|
| 177 |
window.cookieConsent = { accepted: true };
|
| 178 |
window.gtag = () => { };
|
| 179 |
window.ga = () => { };
|
| 180 |
window.dataLayer = [];
|
| 181 |
|
|
|
|
| 182 |
const observer = new MutationObserver((mutations) => {
|
| 183 |
mutations.forEach((mutation) => {
|
| 184 |
mutation.addedNodes.forEach((node) => {
|
| 185 |
+
if (node.nodeType === 1) {
|
| 186 |
const element = node;
|
| 187 |
const text = element.textContent || '';
|
| 188 |
const className = element.className || '';
|
| 189 |
const id = element.id || '';
|
|
|
|
| 190 |
if (
|
| 191 |
text.toLowerCase().includes('cookie') ||
|
| 192 |
text.toLowerCase().includes('consent') ||
|
|
|
|
| 206 |
});
|
| 207 |
observer.observe(document.body, { childList: true, subtree: true });
|
| 208 |
|
|
|
|
| 209 |
setInterval(() => {
|
| 210 |
const cookieElements = document.querySelectorAll(`
|
| 211 |
[id*="cookie" i]:not(img):not(input), [class*="cookie" i]:not(img):not(input), [data-testid*="cookie" i],
|
|
|
|
| 213 |
.cmp-banner, .cc-banner
|
| 214 |
`);
|
| 215 |
cookieElements.forEach(el => el.remove());
|
|
|
|
| 216 |
document.body.style.overflow = 'auto';
|
| 217 |
document.documentElement.style.overflow = 'auto';
|
| 218 |
}, 1000);
|
| 219 |
});
|
| 220 |
+
|
| 221 |
progressTracker?.updateProgress(10, 'bypassing', 'Cookie bypass configured successfully');
|
| 222 |
return true;
|
| 223 |
};
|
| 224 |
|
|
|
|
| 225 |
const unblurContent = async (page, progressTracker) => {
|
| 226 |
progressTracker?.updateProgress(15, 'unblurring', 'Removing content restrictions...');
|
| 227 |
|
|
|
|
| 347 |
progressTracker?.updateProgress(88, 'styling', 'Print styles applied successfully');
|
| 348 |
};
|
| 349 |
|
|
|
|
| 350 |
const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
| 351 |
let browser;
|
| 352 |
let userDataDir = null;
|
|
|
|
|
|
|
|
|
|
| 353 |
try {
|
| 354 |
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 355 |
|
|
|
|
| 359 |
|
| 360 |
console.log("π Launching browser with enhanced stealth configuration...");
|
| 361 |
browser = await puppeteerExtra.launch({
|
| 362 |
+
headless: true,
|
| 363 |
userDataDir: userDataDir,
|
| 364 |
args: [
|
| 365 |
'--no-sandbox',
|
| 366 |
'--disable-setuid-sandbox',
|
|
|
|
| 367 |
'--disable-dev-shm-usage',
|
| 368 |
+
'--disable-accelerated-2d-canvas',
|
| 369 |
+
'--no-first-run',
|
| 370 |
+
'--no-zygote',
|
| 371 |
+
'--disable-gpu',
|
| 372 |
+
'--disable-features=VizDisplayCompositor',
|
| 373 |
+
'--disable-background-networking',
|
| 374 |
+
'--disable-background-timer-throttling',
|
| 375 |
+
'--disable-renderer-backgrounding',
|
| 376 |
+
'--disable-backgrounding-occluded-windows',
|
| 377 |
+
'--disable-ipc-flooding-protection',
|
| 378 |
+
'--disable-web-security',
|
| 379 |
+
'--disable-features=site-per-process',
|
| 380 |
'--disable-blink-features=AutomationControlled',
|
| 381 |
+
'--disable-extensions',
|
| 382 |
+
'--ignore-certificate-errors'
|
| 383 |
],
|
| 384 |
ignoreHTTPSErrors: true,
|
| 385 |
+
timeout: 300000,
|
| 386 |
});
|
| 387 |
|
| 388 |
const page = await browser.newPage();
|
| 389 |
+
|
| 390 |
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 391 |
|
| 392 |
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');
|
| 393 |
+
await page.setViewport({ width: 794, height: 1122 });
|
| 394 |
|
| 395 |
+
await page.evaluateOnNewDocument(() => {
|
| 396 |
+
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 397 |
+
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
| 398 |
+
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
|
| 399 |
+
});
|
| 400 |
+
|
| 401 |
+
await bypassCookiesAndRestrictions(page, progressTracker);
|
| 402 |
|
|
|
|
| 403 |
await page.setRequestInterception(true);
|
| 404 |
page.on('request', (req) => {
|
| 405 |
const resourceType = req.resourceType();
|
|
|
|
| 409 |
req.continue();
|
| 410 |
return;
|
| 411 |
}
|
| 412 |
+
|
| 413 |
if (
|
| 414 |
['image', 'media', 'font', 'stylesheet'].includes(resourceType) &&
|
| 415 |
!reqUrl.includes('document') && !reqUrl.includes('page') && !reqUrl.includes('studocu') ||
|
|
|
|
| 432 |
}
|
| 433 |
});
|
| 434 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
if (options.email && options.password) {
|
| 436 |
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 437 |
+
|
| 438 |
+
console.log("π Logging in to StuDocu...");
|
| 439 |
+
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded', timeout: 60000 });
|
| 440 |
+
await page.waitForSelector('#email', { timeout: 10000 });
|
| 441 |
+
await page.type('#email', options.email);
|
| 442 |
+
await page.type('#password', options.password);
|
| 443 |
+
await page.click('button[type="submit"]');
|
| 444 |
+
try {
|
| 445 |
+
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 });
|
| 446 |
+
await page.waitForSelector('.user-profile, [data-testid="user-menu"]', { timeout: 5000 });
|
| 447 |
+
console.log("β
Login successful.");
|
| 448 |
+
progressTracker?.updateProgress(18, 'authenticated', 'Login successful');
|
| 449 |
+
} catch (e) {
|
| 450 |
+
console.error("β Login failed:", e.message);
|
| 451 |
+
throw new Error("Login failed. Check credentials or try again.");
|
| 452 |
+
}
|
| 453 |
}
|
| 454 |
|
| 455 |
+
await handleCloudflareChallenge(page, url, progressTracker);
|
|
|
|
| 456 |
|
| 457 |
await unblurContent(page, progressTracker);
|
| 458 |
|
|
|
|
| 459 |
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 460 |
console.log("β³ Waiting for document content to load...");
|
| 461 |
|
|
|
|
| 580 |
console.log(`β
PDF generated successfully! Size: ${(pdfBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
| 581 |
return pdfBuffer;
|
| 582 |
|
|
|
|
| 583 |
} catch (error) {
|
| 584 |
progressTracker?.updateProgress(-1, 'error', error.message);
|
| 585 |
console.error("β Error during PDF generation:", error);
|
|
|
|
| 605 |
}
|
| 606 |
};
|
| 607 |
|
| 608 |
+
// --- API Routes ---
|
| 609 |
app.post('/api/request-download', (req, res) => {
|
| 610 |
const { url, email, password } = req.body;
|
| 611 |
if (!url || !url.includes('studocu.com')) {
|
|
|
|
| 616 |
const progressTracker = new ProgressTracker(sessionId);
|
| 617 |
|
| 618 |
progressTrackers.set(sessionId, progressTracker);
|
| 619 |
+
downloadJobs.set(sessionId, { status: 'processing', createdAt: Date.now() }); // MODIFIED: Added createdAt
|
| 620 |
|
| 621 |
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 622 |
|
|
|
|
| 624 |
|
| 625 |
studocuDownloader(url, { email, password }, progressTracker)
|
| 626 |
.then(pdfBuffer => {
|
| 627 |
+
downloadJobs.set(sessionId, { status: 'completed', buffer: pdfBuffer, createdAt: Date.now() });
|
| 628 |
progressTrackers.delete(sessionId);
|
| 629 |
})
|
| 630 |
.catch(error => {
|
| 631 |
+
downloadJobs.set(sessionId, { status: 'error', message: error.message, createdAt: Date.now() });
|
| 632 |
progressTrackers.delete(sessionId);
|
| 633 |
});
|
| 634 |
+
|
| 635 |
+
// NEW: Timeout for job cleanup
|
| 636 |
+
setTimeout(() => {
|
| 637 |
+
if (downloadJobs.has(sessionId) && downloadJobs.get(sessionId).status === 'processing') {
|
| 638 |
+
downloadJobs.set(sessionId, { status: 'error', message: 'Job timed out after 5 minutes', createdAt: Date.now() });
|
| 639 |
+
progressTrackers.delete(sessionId);
|
| 640 |
+
console.log(`π Session ${sessionId} timed out`);
|
| 641 |
+
}
|
| 642 |
+
}, 300000); // 5 minutes
|
| 643 |
});
|
| 644 |
|
| 645 |
app.get('/api/progress/:sessionId', (req, res) => {
|
|
|
|
| 678 |
}
|
| 679 |
|
| 680 |
if (job.status === 'processing') {
|
| 681 |
+
const elapsed = Date.now() - job.createdAt;
|
| 682 |
+
if (elapsed > 300000) { // 5 minutes
|
| 683 |
+
downloadJobs.set(sessionId, { status: 'error', message: 'Download timed out after 5 minutes' });
|
| 684 |
+
return res.status(500).json({ error: 'Download timed out after 5 minutes.' });
|
| 685 |
+
}
|
| 686 |
+
return res.status(400).json({ error: 'Download is still processing. Please try again in a few seconds.' });
|
| 687 |
}
|
| 688 |
|
| 689 |
if (job.status === 'error') {
|
|
|
|
| 694 |
res.setHeader('Content-Type', 'application/pdf');
|
| 695 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 696 |
res.send(job.buffer);
|
| 697 |
+
downloadJobs.delete(sessionId); // MODIFIED: Clean up after successful download
|
| 698 |
} else {
|
| 699 |
res.status(500).json({ error: 'An unknown error occurred.' });
|
| 700 |
}
|
| 701 |
});
|
| 702 |
|
| 703 |
+
// --- Health and Info Endpoints ---
|
| 704 |
app.get('/health', (req, res) => {
|
| 705 |
res.json({
|
| 706 |
status: 'healthy',
|
|
|
|
| 712 |
|
| 713 |
app.get('/', (req, res) => {
|
| 714 |
res.json({
|
| 715 |
+
message: 'π Enhanced StuDocu Downloader API v5.4 - Download Issue Fixed',
|
| 716 |
+
version: '5.4.0',
|
| 717 |
features: [
|
| 718 |
+
'π‘οΈ Cloudflare CAPTCHA bypass with 2Captcha',
|
| 719 |
'πͺ Advanced cookie banner bypass',
|
| 720 |
'π Premium content unblurring',
|
| 721 |
'π Login support for full access',
|
| 722 |
'π Real-time progress tracking via polling',
|
| 723 |
'π Clean PDF generation with print styles',
|
| 724 |
+
'π΅οΈ Enhanced stealth to evade bot detection',
|
| 725 |
+
'β±οΈ Optimized download timeout handling'
|
| 726 |
],
|
| 727 |
endpoints: {
|
| 728 |
request: 'POST /api/request-download (body: {url, filename?, email?, password?})',
|
|
|
|
| 744 |
});
|
| 745 |
|
| 746 |
app.listen(port, () => {
|
| 747 |
+
console.log(`π Enhanced StuDocu Downloader v5.4.0 running on http://localhost:${port}`);
|
| 748 |
+
console.log(`β¨ Features: CAPTCHA bypass, Real-time progress tracking, enhanced stealth, optimized download`);
|
| 749 |
});
|