devusman commited on
Commit
6fd2132
Β·
1 Parent(s): 0f0b365

added captacha verification bypass in this

Browse files
Files changed (5) hide show
  1. .gitignore +2 -1
  2. index.html +2 -1
  3. package-lock.json +369 -5
  4. package.json +4 -1
  5. 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: 'cc4f0d688032c69ecf359cccdabbacb9' }
 
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 (No changes) ---
30
  const progressTrackers = new Map();
31
  const downloadJobs = new Map();
32
 
@@ -55,13 +51,75 @@ class ProgressTracker extends EventEmitter {
55
  }
56
  }
57
 
58
- // --- Puppeteer Logic (Updated for Cloudflare Bypass) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- // (The implementation of this function is unchanged from your original code)
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) { // Element node
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: !isDebugging, // Use the debugging flag
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
- '--window-size=1920,1080'
 
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: 1920, height: 1080 });
338
 
339
- // The stealth plugin and other `evaluateOnNewDocument` calls handle this better now.
340
- // await page.evaluateOnNewDocument(...) is handled by plugins.
 
 
 
 
 
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
- // ... (Login logic is unchanged)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  }
409
 
410
- progressTracker?.updateProgress(40, 'loading', 'Page loaded, waiting for content...');
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, Health, and Info Endpoints (Unchanged) ---
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
- return res.status(400).json({ error: 'Download is still processing.' });
 
 
 
 
 
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.3 - Real-time Progress Tracking with Cloudflare Bypass',
658
- version: '5.3.0',
659
  features: [
660
- 'πŸ›‘οΈ Cloudflare JS Challenge Bypass',
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.3.0 running on http://localhost:${port}`);
689
- console.log(`✨ Features: Cloudflare Bypass, Real-time progress tracking, enhanced stealth, and user feedback`);
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
  });