devusman commited on
Commit
7d05c0c
Β·
2 Parent(s): 3203abb 6fd2132

Merge branch 'main' of https://huggingface.co/spaces/devusman/test

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