feat : fixing timeout
Browse files- package-lock.json +683 -1
- package.json +3 -1
- server copy.js +233 -330
- server.js +22 -35
package-lock.json
CHANGED
|
@@ -11,9 +11,11 @@
|
|
| 11 |
"axios": "^1.11.0",
|
| 12 |
"cors": "^2.8.5",
|
| 13 |
"express": "^5.1.0",
|
|
|
|
| 14 |
"puppeteer": "^24.16.2",
|
| 15 |
"puppeteer-extra": "^3.3.6",
|
| 16 |
-
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
|
|
|
| 17 |
},
|
| 18 |
"devDependencies": {
|
| 19 |
"nodemon": "^3.1.10",
|
|
@@ -44,6 +46,434 @@
|
|
| 44 |
"node": ">=6.9.0"
|
| 45 |
}
|
| 46 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
"node_modules/@puppeteer/browsers": {
|
| 48 |
"version": "2.10.6",
|
| 49 |
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.6.tgz",
|
|
@@ -65,6 +495,15 @@
|
|
| 65 |
"node": ">=18"
|
| 66 |
}
|
| 67 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
"node_modules/@tootallnate/quickjs-emscripten": {
|
| 69 |
"version": "0.23.0",
|
| 70 |
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
|
|
@@ -294,6 +733,26 @@
|
|
| 294 |
}
|
| 295 |
}
|
| 296 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
"node_modules/basic-ftp": {
|
| 298 |
"version": "5.0.5",
|
| 299 |
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
|
@@ -359,6 +818,15 @@
|
|
| 359 |
"node": ">=8"
|
| 360 |
}
|
| 361 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
"node_modules/buffer-crc32": {
|
| 363 |
"version": "0.2.13",
|
| 364 |
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
@@ -467,6 +935,15 @@
|
|
| 467 |
"node": ">=12"
|
| 468 |
}
|
| 469 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
"node_modules/clone-deep": {
|
| 471 |
"version": "0.2.4",
|
| 472 |
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
|
|
@@ -483,6 +960,19 @@
|
|
| 483 |
"node": ">=0.10.0"
|
| 484 |
}
|
| 485 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
"node_modules/color-convert": {
|
| 487 |
"version": "2.0.1",
|
| 488 |
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
|
@@ -501,6 +991,16 @@
|
|
| 501 |
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
| 502 |
"license": "MIT"
|
| 503 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
"node_modules/combined-stream": {
|
| 505 |
"version": "1.0.8",
|
| 506 |
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
@@ -612,6 +1112,12 @@
|
|
| 612 |
"node": ">= 8"
|
| 613 |
}
|
| 614 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 615 |
"node_modules/data-uri-to-buffer": {
|
| 616 |
"version": "6.0.2",
|
| 617 |
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
|
|
@@ -679,12 +1185,27 @@
|
|
| 679 |
"node": ">= 0.8"
|
| 680 |
}
|
| 681 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
"node_modules/devtools-protocol": {
|
| 683 |
"version": "0.0.1475386",
|
| 684 |
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1475386.tgz",
|
| 685 |
"integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==",
|
| 686 |
"license": "BSD-3-Clause"
|
| 687 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 688 |
"node_modules/dunder-proto": {
|
| 689 |
"version": "1.0.1",
|
| 690 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
@@ -953,6 +1474,12 @@
|
|
| 953 |
"@types/yauzl": "^2.9.1"
|
| 954 |
}
|
| 955 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 956 |
"node_modules/fast-fifo": {
|
| 957 |
"version": "1.3.2",
|
| 958 |
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
|
@@ -1018,6 +1545,23 @@
|
|
| 1018 |
}
|
| 1019 |
}
|
| 1020 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
"node_modules/for-in": {
|
| 1022 |
"version": "1.0.2",
|
| 1023 |
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
|
@@ -1552,6 +2096,12 @@
|
|
| 1552 |
"node": ">=0.10.0"
|
| 1553 |
}
|
| 1554 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1555 |
"node_modules/js-tokens": {
|
| 1556 |
"version": "4.0.0",
|
| 1557 |
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
@@ -1609,6 +2159,25 @@
|
|
| 1609 |
"node": ">=0.10.0"
|
| 1610 |
}
|
| 1611 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1612 |
"node_modules/lines-and-columns": {
|
| 1613 |
"version": "1.2.4",
|
| 1614 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
@@ -1879,6 +2448,12 @@
|
|
| 1879 |
"node": ">= 14"
|
| 1880 |
}
|
| 1881 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1882 |
"node_modules/parent-module": {
|
| 1883 |
"version": "1.0.1",
|
| 1884 |
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
|
@@ -1959,6 +2534,19 @@
|
|
| 1959 |
"through": "~2.3"
|
| 1960 |
}
|
| 1961 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1962 |
"node_modules/pend": {
|
| 1963 |
"version": "1.2.0",
|
| 1964 |
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
|
@@ -1984,6 +2572,11 @@
|
|
| 1984 |
"url": "https://github.com/sponsors/jonschlinkert"
|
| 1985 |
}
|
| 1986 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1987 |
"node_modules/progress": {
|
| 1988 |
"version": "2.0.3",
|
| 1989 |
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
|
@@ -2308,6 +2901,12 @@
|
|
| 2308 |
"node": ">=4"
|
| 2309 |
}
|
| 2310 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2311 |
"node_modules/rimraf": {
|
| 2312 |
"version": "3.0.2",
|
| 2313 |
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
|
@@ -2457,6 +3056,48 @@
|
|
| 2457 |
"node": ">=0.10.0"
|
| 2458 |
}
|
| 2459 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2460 |
"node_modules/shebang-command": {
|
| 2461 |
"version": "2.0.0",
|
| 2462 |
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
|
@@ -2552,6 +3193,21 @@
|
|
| 2552 |
"url": "https://github.com/sponsors/ljharb"
|
| 2553 |
}
|
| 2554 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2555 |
"node_modules/simple-update-notifier": {
|
| 2556 |
"version": "2.0.0",
|
| 2557 |
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
|
@@ -2748,6 +3404,12 @@
|
|
| 2748 |
"dev": true,
|
| 2749 |
"license": "MIT"
|
| 2750 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2751 |
"node_modules/to-regex-range": {
|
| 2752 |
"version": "5.0.1",
|
| 2753 |
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
@@ -2856,6 +3518,26 @@
|
|
| 2856 |
"license": "MIT",
|
| 2857 |
"optional": true
|
| 2858 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2859 |
"node_modules/universalify": {
|
| 2860 |
"version": "2.0.1",
|
| 2861 |
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
|
|
|
| 11 |
"axios": "^1.11.0",
|
| 12 |
"cors": "^2.8.5",
|
| 13 |
"express": "^5.1.0",
|
| 14 |
+
"pdfkit": "^0.17.1",
|
| 15 |
"puppeteer": "^24.16.2",
|
| 16 |
"puppeteer-extra": "^3.3.6",
|
| 17 |
+
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
| 18 |
+
"sharp": "^0.34.3"
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
"nodemon": "^3.1.10",
|
|
|
|
| 46 |
"node": ">=6.9.0"
|
| 47 |
}
|
| 48 |
},
|
| 49 |
+
"node_modules/@emnapi/runtime": {
|
| 50 |
+
"version": "1.4.5",
|
| 51 |
+
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
|
| 52 |
+
"integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
|
| 53 |
+
"license": "MIT",
|
| 54 |
+
"optional": true,
|
| 55 |
+
"dependencies": {
|
| 56 |
+
"tslib": "^2.4.0"
|
| 57 |
+
}
|
| 58 |
+
},
|
| 59 |
+
"node_modules/@img/sharp-darwin-arm64": {
|
| 60 |
+
"version": "0.34.3",
|
| 61 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
|
| 62 |
+
"integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
|
| 63 |
+
"cpu": [
|
| 64 |
+
"arm64"
|
| 65 |
+
],
|
| 66 |
+
"license": "Apache-2.0",
|
| 67 |
+
"optional": true,
|
| 68 |
+
"os": [
|
| 69 |
+
"darwin"
|
| 70 |
+
],
|
| 71 |
+
"engines": {
|
| 72 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 73 |
+
},
|
| 74 |
+
"funding": {
|
| 75 |
+
"url": "https://opencollective.com/libvips"
|
| 76 |
+
},
|
| 77 |
+
"optionalDependencies": {
|
| 78 |
+
"@img/sharp-libvips-darwin-arm64": "1.2.0"
|
| 79 |
+
}
|
| 80 |
+
},
|
| 81 |
+
"node_modules/@img/sharp-darwin-x64": {
|
| 82 |
+
"version": "0.34.3",
|
| 83 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
|
| 84 |
+
"integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
|
| 85 |
+
"cpu": [
|
| 86 |
+
"x64"
|
| 87 |
+
],
|
| 88 |
+
"license": "Apache-2.0",
|
| 89 |
+
"optional": true,
|
| 90 |
+
"os": [
|
| 91 |
+
"darwin"
|
| 92 |
+
],
|
| 93 |
+
"engines": {
|
| 94 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 95 |
+
},
|
| 96 |
+
"funding": {
|
| 97 |
+
"url": "https://opencollective.com/libvips"
|
| 98 |
+
},
|
| 99 |
+
"optionalDependencies": {
|
| 100 |
+
"@img/sharp-libvips-darwin-x64": "1.2.0"
|
| 101 |
+
}
|
| 102 |
+
},
|
| 103 |
+
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
| 104 |
+
"version": "1.2.0",
|
| 105 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
|
| 106 |
+
"integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
|
| 107 |
+
"cpu": [
|
| 108 |
+
"arm64"
|
| 109 |
+
],
|
| 110 |
+
"license": "LGPL-3.0-or-later",
|
| 111 |
+
"optional": true,
|
| 112 |
+
"os": [
|
| 113 |
+
"darwin"
|
| 114 |
+
],
|
| 115 |
+
"funding": {
|
| 116 |
+
"url": "https://opencollective.com/libvips"
|
| 117 |
+
}
|
| 118 |
+
},
|
| 119 |
+
"node_modules/@img/sharp-libvips-darwin-x64": {
|
| 120 |
+
"version": "1.2.0",
|
| 121 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
|
| 122 |
+
"integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
|
| 123 |
+
"cpu": [
|
| 124 |
+
"x64"
|
| 125 |
+
],
|
| 126 |
+
"license": "LGPL-3.0-or-later",
|
| 127 |
+
"optional": true,
|
| 128 |
+
"os": [
|
| 129 |
+
"darwin"
|
| 130 |
+
],
|
| 131 |
+
"funding": {
|
| 132 |
+
"url": "https://opencollective.com/libvips"
|
| 133 |
+
}
|
| 134 |
+
},
|
| 135 |
+
"node_modules/@img/sharp-libvips-linux-arm": {
|
| 136 |
+
"version": "1.2.0",
|
| 137 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
|
| 138 |
+
"integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
|
| 139 |
+
"cpu": [
|
| 140 |
+
"arm"
|
| 141 |
+
],
|
| 142 |
+
"license": "LGPL-3.0-or-later",
|
| 143 |
+
"optional": true,
|
| 144 |
+
"os": [
|
| 145 |
+
"linux"
|
| 146 |
+
],
|
| 147 |
+
"funding": {
|
| 148 |
+
"url": "https://opencollective.com/libvips"
|
| 149 |
+
}
|
| 150 |
+
},
|
| 151 |
+
"node_modules/@img/sharp-libvips-linux-arm64": {
|
| 152 |
+
"version": "1.2.0",
|
| 153 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
|
| 154 |
+
"integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
|
| 155 |
+
"cpu": [
|
| 156 |
+
"arm64"
|
| 157 |
+
],
|
| 158 |
+
"license": "LGPL-3.0-or-later",
|
| 159 |
+
"optional": true,
|
| 160 |
+
"os": [
|
| 161 |
+
"linux"
|
| 162 |
+
],
|
| 163 |
+
"funding": {
|
| 164 |
+
"url": "https://opencollective.com/libvips"
|
| 165 |
+
}
|
| 166 |
+
},
|
| 167 |
+
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
| 168 |
+
"version": "1.2.0",
|
| 169 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
|
| 170 |
+
"integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
|
| 171 |
+
"cpu": [
|
| 172 |
+
"ppc64"
|
| 173 |
+
],
|
| 174 |
+
"license": "LGPL-3.0-or-later",
|
| 175 |
+
"optional": true,
|
| 176 |
+
"os": [
|
| 177 |
+
"linux"
|
| 178 |
+
],
|
| 179 |
+
"funding": {
|
| 180 |
+
"url": "https://opencollective.com/libvips"
|
| 181 |
+
}
|
| 182 |
+
},
|
| 183 |
+
"node_modules/@img/sharp-libvips-linux-s390x": {
|
| 184 |
+
"version": "1.2.0",
|
| 185 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
|
| 186 |
+
"integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
|
| 187 |
+
"cpu": [
|
| 188 |
+
"s390x"
|
| 189 |
+
],
|
| 190 |
+
"license": "LGPL-3.0-or-later",
|
| 191 |
+
"optional": true,
|
| 192 |
+
"os": [
|
| 193 |
+
"linux"
|
| 194 |
+
],
|
| 195 |
+
"funding": {
|
| 196 |
+
"url": "https://opencollective.com/libvips"
|
| 197 |
+
}
|
| 198 |
+
},
|
| 199 |
+
"node_modules/@img/sharp-libvips-linux-x64": {
|
| 200 |
+
"version": "1.2.0",
|
| 201 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
|
| 202 |
+
"integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
|
| 203 |
+
"cpu": [
|
| 204 |
+
"x64"
|
| 205 |
+
],
|
| 206 |
+
"license": "LGPL-3.0-or-later",
|
| 207 |
+
"optional": true,
|
| 208 |
+
"os": [
|
| 209 |
+
"linux"
|
| 210 |
+
],
|
| 211 |
+
"funding": {
|
| 212 |
+
"url": "https://opencollective.com/libvips"
|
| 213 |
+
}
|
| 214 |
+
},
|
| 215 |
+
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
| 216 |
+
"version": "1.2.0",
|
| 217 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
|
| 218 |
+
"integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
|
| 219 |
+
"cpu": [
|
| 220 |
+
"arm64"
|
| 221 |
+
],
|
| 222 |
+
"license": "LGPL-3.0-or-later",
|
| 223 |
+
"optional": true,
|
| 224 |
+
"os": [
|
| 225 |
+
"linux"
|
| 226 |
+
],
|
| 227 |
+
"funding": {
|
| 228 |
+
"url": "https://opencollective.com/libvips"
|
| 229 |
+
}
|
| 230 |
+
},
|
| 231 |
+
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
| 232 |
+
"version": "1.2.0",
|
| 233 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
|
| 234 |
+
"integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
|
| 235 |
+
"cpu": [
|
| 236 |
+
"x64"
|
| 237 |
+
],
|
| 238 |
+
"license": "LGPL-3.0-or-later",
|
| 239 |
+
"optional": true,
|
| 240 |
+
"os": [
|
| 241 |
+
"linux"
|
| 242 |
+
],
|
| 243 |
+
"funding": {
|
| 244 |
+
"url": "https://opencollective.com/libvips"
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
+
"node_modules/@img/sharp-linux-arm": {
|
| 248 |
+
"version": "0.34.3",
|
| 249 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
|
| 250 |
+
"integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
|
| 251 |
+
"cpu": [
|
| 252 |
+
"arm"
|
| 253 |
+
],
|
| 254 |
+
"license": "Apache-2.0",
|
| 255 |
+
"optional": true,
|
| 256 |
+
"os": [
|
| 257 |
+
"linux"
|
| 258 |
+
],
|
| 259 |
+
"engines": {
|
| 260 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 261 |
+
},
|
| 262 |
+
"funding": {
|
| 263 |
+
"url": "https://opencollective.com/libvips"
|
| 264 |
+
},
|
| 265 |
+
"optionalDependencies": {
|
| 266 |
+
"@img/sharp-libvips-linux-arm": "1.2.0"
|
| 267 |
+
}
|
| 268 |
+
},
|
| 269 |
+
"node_modules/@img/sharp-linux-arm64": {
|
| 270 |
+
"version": "0.34.3",
|
| 271 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
|
| 272 |
+
"integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
|
| 273 |
+
"cpu": [
|
| 274 |
+
"arm64"
|
| 275 |
+
],
|
| 276 |
+
"license": "Apache-2.0",
|
| 277 |
+
"optional": true,
|
| 278 |
+
"os": [
|
| 279 |
+
"linux"
|
| 280 |
+
],
|
| 281 |
+
"engines": {
|
| 282 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 283 |
+
},
|
| 284 |
+
"funding": {
|
| 285 |
+
"url": "https://opencollective.com/libvips"
|
| 286 |
+
},
|
| 287 |
+
"optionalDependencies": {
|
| 288 |
+
"@img/sharp-libvips-linux-arm64": "1.2.0"
|
| 289 |
+
}
|
| 290 |
+
},
|
| 291 |
+
"node_modules/@img/sharp-linux-ppc64": {
|
| 292 |
+
"version": "0.34.3",
|
| 293 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
|
| 294 |
+
"integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
|
| 295 |
+
"cpu": [
|
| 296 |
+
"ppc64"
|
| 297 |
+
],
|
| 298 |
+
"license": "Apache-2.0",
|
| 299 |
+
"optional": true,
|
| 300 |
+
"os": [
|
| 301 |
+
"linux"
|
| 302 |
+
],
|
| 303 |
+
"engines": {
|
| 304 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 305 |
+
},
|
| 306 |
+
"funding": {
|
| 307 |
+
"url": "https://opencollective.com/libvips"
|
| 308 |
+
},
|
| 309 |
+
"optionalDependencies": {
|
| 310 |
+
"@img/sharp-libvips-linux-ppc64": "1.2.0"
|
| 311 |
+
}
|
| 312 |
+
},
|
| 313 |
+
"node_modules/@img/sharp-linux-s390x": {
|
| 314 |
+
"version": "0.34.3",
|
| 315 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
|
| 316 |
+
"integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
|
| 317 |
+
"cpu": [
|
| 318 |
+
"s390x"
|
| 319 |
+
],
|
| 320 |
+
"license": "Apache-2.0",
|
| 321 |
+
"optional": true,
|
| 322 |
+
"os": [
|
| 323 |
+
"linux"
|
| 324 |
+
],
|
| 325 |
+
"engines": {
|
| 326 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 327 |
+
},
|
| 328 |
+
"funding": {
|
| 329 |
+
"url": "https://opencollective.com/libvips"
|
| 330 |
+
},
|
| 331 |
+
"optionalDependencies": {
|
| 332 |
+
"@img/sharp-libvips-linux-s390x": "1.2.0"
|
| 333 |
+
}
|
| 334 |
+
},
|
| 335 |
+
"node_modules/@img/sharp-linux-x64": {
|
| 336 |
+
"version": "0.34.3",
|
| 337 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
|
| 338 |
+
"integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
|
| 339 |
+
"cpu": [
|
| 340 |
+
"x64"
|
| 341 |
+
],
|
| 342 |
+
"license": "Apache-2.0",
|
| 343 |
+
"optional": true,
|
| 344 |
+
"os": [
|
| 345 |
+
"linux"
|
| 346 |
+
],
|
| 347 |
+
"engines": {
|
| 348 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 349 |
+
},
|
| 350 |
+
"funding": {
|
| 351 |
+
"url": "https://opencollective.com/libvips"
|
| 352 |
+
},
|
| 353 |
+
"optionalDependencies": {
|
| 354 |
+
"@img/sharp-libvips-linux-x64": "1.2.0"
|
| 355 |
+
}
|
| 356 |
+
},
|
| 357 |
+
"node_modules/@img/sharp-linuxmusl-arm64": {
|
| 358 |
+
"version": "0.34.3",
|
| 359 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
|
| 360 |
+
"integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
|
| 361 |
+
"cpu": [
|
| 362 |
+
"arm64"
|
| 363 |
+
],
|
| 364 |
+
"license": "Apache-2.0",
|
| 365 |
+
"optional": true,
|
| 366 |
+
"os": [
|
| 367 |
+
"linux"
|
| 368 |
+
],
|
| 369 |
+
"engines": {
|
| 370 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 371 |
+
},
|
| 372 |
+
"funding": {
|
| 373 |
+
"url": "https://opencollective.com/libvips"
|
| 374 |
+
},
|
| 375 |
+
"optionalDependencies": {
|
| 376 |
+
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
|
| 377 |
+
}
|
| 378 |
+
},
|
| 379 |
+
"node_modules/@img/sharp-linuxmusl-x64": {
|
| 380 |
+
"version": "0.34.3",
|
| 381 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
|
| 382 |
+
"integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
|
| 383 |
+
"cpu": [
|
| 384 |
+
"x64"
|
| 385 |
+
],
|
| 386 |
+
"license": "Apache-2.0",
|
| 387 |
+
"optional": true,
|
| 388 |
+
"os": [
|
| 389 |
+
"linux"
|
| 390 |
+
],
|
| 391 |
+
"engines": {
|
| 392 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 393 |
+
},
|
| 394 |
+
"funding": {
|
| 395 |
+
"url": "https://opencollective.com/libvips"
|
| 396 |
+
},
|
| 397 |
+
"optionalDependencies": {
|
| 398 |
+
"@img/sharp-libvips-linuxmusl-x64": "1.2.0"
|
| 399 |
+
}
|
| 400 |
+
},
|
| 401 |
+
"node_modules/@img/sharp-wasm32": {
|
| 402 |
+
"version": "0.34.3",
|
| 403 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
|
| 404 |
+
"integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
|
| 405 |
+
"cpu": [
|
| 406 |
+
"wasm32"
|
| 407 |
+
],
|
| 408 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
| 409 |
+
"optional": true,
|
| 410 |
+
"dependencies": {
|
| 411 |
+
"@emnapi/runtime": "^1.4.4"
|
| 412 |
+
},
|
| 413 |
+
"engines": {
|
| 414 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 415 |
+
},
|
| 416 |
+
"funding": {
|
| 417 |
+
"url": "https://opencollective.com/libvips"
|
| 418 |
+
}
|
| 419 |
+
},
|
| 420 |
+
"node_modules/@img/sharp-win32-arm64": {
|
| 421 |
+
"version": "0.34.3",
|
| 422 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
|
| 423 |
+
"integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
|
| 424 |
+
"cpu": [
|
| 425 |
+
"arm64"
|
| 426 |
+
],
|
| 427 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 428 |
+
"optional": true,
|
| 429 |
+
"os": [
|
| 430 |
+
"win32"
|
| 431 |
+
],
|
| 432 |
+
"engines": {
|
| 433 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 434 |
+
},
|
| 435 |
+
"funding": {
|
| 436 |
+
"url": "https://opencollective.com/libvips"
|
| 437 |
+
}
|
| 438 |
+
},
|
| 439 |
+
"node_modules/@img/sharp-win32-ia32": {
|
| 440 |
+
"version": "0.34.3",
|
| 441 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
|
| 442 |
+
"integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
|
| 443 |
+
"cpu": [
|
| 444 |
+
"ia32"
|
| 445 |
+
],
|
| 446 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 447 |
+
"optional": true,
|
| 448 |
+
"os": [
|
| 449 |
+
"win32"
|
| 450 |
+
],
|
| 451 |
+
"engines": {
|
| 452 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 453 |
+
},
|
| 454 |
+
"funding": {
|
| 455 |
+
"url": "https://opencollective.com/libvips"
|
| 456 |
+
}
|
| 457 |
+
},
|
| 458 |
+
"node_modules/@img/sharp-win32-x64": {
|
| 459 |
+
"version": "0.34.3",
|
| 460 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
|
| 461 |
+
"integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
|
| 462 |
+
"cpu": [
|
| 463 |
+
"x64"
|
| 464 |
+
],
|
| 465 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 466 |
+
"optional": true,
|
| 467 |
+
"os": [
|
| 468 |
+
"win32"
|
| 469 |
+
],
|
| 470 |
+
"engines": {
|
| 471 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 472 |
+
},
|
| 473 |
+
"funding": {
|
| 474 |
+
"url": "https://opencollective.com/libvips"
|
| 475 |
+
}
|
| 476 |
+
},
|
| 477 |
"node_modules/@puppeteer/browsers": {
|
| 478 |
"version": "2.10.6",
|
| 479 |
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.6.tgz",
|
|
|
|
| 495 |
"node": ">=18"
|
| 496 |
}
|
| 497 |
},
|
| 498 |
+
"node_modules/@swc/helpers": {
|
| 499 |
+
"version": "0.5.17",
|
| 500 |
+
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
| 501 |
+
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
| 502 |
+
"license": "Apache-2.0",
|
| 503 |
+
"dependencies": {
|
| 504 |
+
"tslib": "^2.8.0"
|
| 505 |
+
}
|
| 506 |
+
},
|
| 507 |
"node_modules/@tootallnate/quickjs-emscripten": {
|
| 508 |
"version": "0.23.0",
|
| 509 |
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
|
|
|
|
| 733 |
}
|
| 734 |
}
|
| 735 |
},
|
| 736 |
+
"node_modules/base64-js": {
|
| 737 |
+
"version": "1.5.1",
|
| 738 |
+
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
| 739 |
+
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
| 740 |
+
"funding": [
|
| 741 |
+
{
|
| 742 |
+
"type": "github",
|
| 743 |
+
"url": "https://github.com/sponsors/feross"
|
| 744 |
+
},
|
| 745 |
+
{
|
| 746 |
+
"type": "patreon",
|
| 747 |
+
"url": "https://www.patreon.com/feross"
|
| 748 |
+
},
|
| 749 |
+
{
|
| 750 |
+
"type": "consulting",
|
| 751 |
+
"url": "https://feross.org/support"
|
| 752 |
+
}
|
| 753 |
+
],
|
| 754 |
+
"license": "MIT"
|
| 755 |
+
},
|
| 756 |
"node_modules/basic-ftp": {
|
| 757 |
"version": "5.0.5",
|
| 758 |
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
|
|
|
| 818 |
"node": ">=8"
|
| 819 |
}
|
| 820 |
},
|
| 821 |
+
"node_modules/brotli": {
|
| 822 |
+
"version": "1.3.3",
|
| 823 |
+
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
| 824 |
+
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
| 825 |
+
"license": "MIT",
|
| 826 |
+
"dependencies": {
|
| 827 |
+
"base64-js": "^1.1.2"
|
| 828 |
+
}
|
| 829 |
+
},
|
| 830 |
"node_modules/buffer-crc32": {
|
| 831 |
"version": "0.2.13",
|
| 832 |
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
|
|
| 935 |
"node": ">=12"
|
| 936 |
}
|
| 937 |
},
|
| 938 |
+
"node_modules/clone": {
|
| 939 |
+
"version": "2.1.2",
|
| 940 |
+
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
| 941 |
+
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
| 942 |
+
"license": "MIT",
|
| 943 |
+
"engines": {
|
| 944 |
+
"node": ">=0.8"
|
| 945 |
+
}
|
| 946 |
+
},
|
| 947 |
"node_modules/clone-deep": {
|
| 948 |
"version": "0.2.4",
|
| 949 |
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
|
|
|
|
| 960 |
"node": ">=0.10.0"
|
| 961 |
}
|
| 962 |
},
|
| 963 |
+
"node_modules/color": {
|
| 964 |
+
"version": "4.2.3",
|
| 965 |
+
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
| 966 |
+
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
| 967 |
+
"license": "MIT",
|
| 968 |
+
"dependencies": {
|
| 969 |
+
"color-convert": "^2.0.1",
|
| 970 |
+
"color-string": "^1.9.0"
|
| 971 |
+
},
|
| 972 |
+
"engines": {
|
| 973 |
+
"node": ">=12.5.0"
|
| 974 |
+
}
|
| 975 |
+
},
|
| 976 |
"node_modules/color-convert": {
|
| 977 |
"version": "2.0.1",
|
| 978 |
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
|
|
|
| 991 |
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
| 992 |
"license": "MIT"
|
| 993 |
},
|
| 994 |
+
"node_modules/color-string": {
|
| 995 |
+
"version": "1.9.1",
|
| 996 |
+
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
| 997 |
+
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
| 998 |
+
"license": "MIT",
|
| 999 |
+
"dependencies": {
|
| 1000 |
+
"color-name": "^1.0.0",
|
| 1001 |
+
"simple-swizzle": "^0.2.2"
|
| 1002 |
+
}
|
| 1003 |
+
},
|
| 1004 |
"node_modules/combined-stream": {
|
| 1005 |
"version": "1.0.8",
|
| 1006 |
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
|
|
| 1112 |
"node": ">= 8"
|
| 1113 |
}
|
| 1114 |
},
|
| 1115 |
+
"node_modules/crypto-js": {
|
| 1116 |
+
"version": "4.2.0",
|
| 1117 |
+
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
| 1118 |
+
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
| 1119 |
+
"license": "MIT"
|
| 1120 |
+
},
|
| 1121 |
"node_modules/data-uri-to-buffer": {
|
| 1122 |
"version": "6.0.2",
|
| 1123 |
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
|
|
|
|
| 1185 |
"node": ">= 0.8"
|
| 1186 |
}
|
| 1187 |
},
|
| 1188 |
+
"node_modules/detect-libc": {
|
| 1189 |
+
"version": "2.0.4",
|
| 1190 |
+
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
| 1191 |
+
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
|
| 1192 |
+
"license": "Apache-2.0",
|
| 1193 |
+
"engines": {
|
| 1194 |
+
"node": ">=8"
|
| 1195 |
+
}
|
| 1196 |
+
},
|
| 1197 |
"node_modules/devtools-protocol": {
|
| 1198 |
"version": "0.0.1475386",
|
| 1199 |
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1475386.tgz",
|
| 1200 |
"integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==",
|
| 1201 |
"license": "BSD-3-Clause"
|
| 1202 |
},
|
| 1203 |
+
"node_modules/dfa": {
|
| 1204 |
+
"version": "1.2.0",
|
| 1205 |
+
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
|
| 1206 |
+
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
|
| 1207 |
+
"license": "MIT"
|
| 1208 |
+
},
|
| 1209 |
"node_modules/dunder-proto": {
|
| 1210 |
"version": "1.0.1",
|
| 1211 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
|
|
| 1474 |
"@types/yauzl": "^2.9.1"
|
| 1475 |
}
|
| 1476 |
},
|
| 1477 |
+
"node_modules/fast-deep-equal": {
|
| 1478 |
+
"version": "3.1.3",
|
| 1479 |
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
| 1480 |
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
| 1481 |
+
"license": "MIT"
|
| 1482 |
+
},
|
| 1483 |
"node_modules/fast-fifo": {
|
| 1484 |
"version": "1.3.2",
|
| 1485 |
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
|
|
|
| 1545 |
}
|
| 1546 |
}
|
| 1547 |
},
|
| 1548 |
+
"node_modules/fontkit": {
|
| 1549 |
+
"version": "2.0.4",
|
| 1550 |
+
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
|
| 1551 |
+
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
|
| 1552 |
+
"license": "MIT",
|
| 1553 |
+
"dependencies": {
|
| 1554 |
+
"@swc/helpers": "^0.5.12",
|
| 1555 |
+
"brotli": "^1.3.2",
|
| 1556 |
+
"clone": "^2.1.2",
|
| 1557 |
+
"dfa": "^1.2.0",
|
| 1558 |
+
"fast-deep-equal": "^3.1.3",
|
| 1559 |
+
"restructure": "^3.0.0",
|
| 1560 |
+
"tiny-inflate": "^1.0.3",
|
| 1561 |
+
"unicode-properties": "^1.4.0",
|
| 1562 |
+
"unicode-trie": "^2.0.0"
|
| 1563 |
+
}
|
| 1564 |
+
},
|
| 1565 |
"node_modules/for-in": {
|
| 1566 |
"version": "1.0.2",
|
| 1567 |
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
|
|
|
| 2096 |
"node": ">=0.10.0"
|
| 2097 |
}
|
| 2098 |
},
|
| 2099 |
+
"node_modules/jpeg-exif": {
|
| 2100 |
+
"version": "1.1.4",
|
| 2101 |
+
"resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz",
|
| 2102 |
+
"integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==",
|
| 2103 |
+
"license": "MIT"
|
| 2104 |
+
},
|
| 2105 |
"node_modules/js-tokens": {
|
| 2106 |
"version": "4.0.0",
|
| 2107 |
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
|
|
| 2159 |
"node": ">=0.10.0"
|
| 2160 |
}
|
| 2161 |
},
|
| 2162 |
+
"node_modules/linebreak": {
|
| 2163 |
+
"version": "1.1.0",
|
| 2164 |
+
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
| 2165 |
+
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
| 2166 |
+
"license": "MIT",
|
| 2167 |
+
"dependencies": {
|
| 2168 |
+
"base64-js": "0.0.8",
|
| 2169 |
+
"unicode-trie": "^2.0.0"
|
| 2170 |
+
}
|
| 2171 |
+
},
|
| 2172 |
+
"node_modules/linebreak/node_modules/base64-js": {
|
| 2173 |
+
"version": "0.0.8",
|
| 2174 |
+
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
| 2175 |
+
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
| 2176 |
+
"license": "MIT",
|
| 2177 |
+
"engines": {
|
| 2178 |
+
"node": ">= 0.4"
|
| 2179 |
+
}
|
| 2180 |
+
},
|
| 2181 |
"node_modules/lines-and-columns": {
|
| 2182 |
"version": "1.2.4",
|
| 2183 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
|
|
| 2448 |
"node": ">= 14"
|
| 2449 |
}
|
| 2450 |
},
|
| 2451 |
+
"node_modules/pako": {
|
| 2452 |
+
"version": "0.2.9",
|
| 2453 |
+
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
| 2454 |
+
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
|
| 2455 |
+
"license": "MIT"
|
| 2456 |
+
},
|
| 2457 |
"node_modules/parent-module": {
|
| 2458 |
"version": "1.0.1",
|
| 2459 |
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
|
|
|
| 2534 |
"through": "~2.3"
|
| 2535 |
}
|
| 2536 |
},
|
| 2537 |
+
"node_modules/pdfkit": {
|
| 2538 |
+
"version": "0.17.1",
|
| 2539 |
+
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.1.tgz",
|
| 2540 |
+
"integrity": "sha512-Kkf1I9no14O/uo593DYph5u3QwiMfby7JsBSErN1WqeyTgCBNJE3K4pXBn3TgkdKUIVu+buSl4bYUNC+8Up4xg==",
|
| 2541 |
+
"license": "MIT",
|
| 2542 |
+
"dependencies": {
|
| 2543 |
+
"crypto-js": "^4.2.0",
|
| 2544 |
+
"fontkit": "^2.0.4",
|
| 2545 |
+
"jpeg-exif": "^1.1.4",
|
| 2546 |
+
"linebreak": "^1.1.0",
|
| 2547 |
+
"png-js": "^1.0.0"
|
| 2548 |
+
}
|
| 2549 |
+
},
|
| 2550 |
"node_modules/pend": {
|
| 2551 |
"version": "1.2.0",
|
| 2552 |
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
|
|
|
| 2572 |
"url": "https://github.com/sponsors/jonschlinkert"
|
| 2573 |
}
|
| 2574 |
},
|
| 2575 |
+
"node_modules/png-js": {
|
| 2576 |
+
"version": "1.0.0",
|
| 2577 |
+
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
|
| 2578 |
+
"integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g=="
|
| 2579 |
+
},
|
| 2580 |
"node_modules/progress": {
|
| 2581 |
"version": "2.0.3",
|
| 2582 |
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
|
|
|
| 2901 |
"node": ">=4"
|
| 2902 |
}
|
| 2903 |
},
|
| 2904 |
+
"node_modules/restructure": {
|
| 2905 |
+
"version": "3.0.2",
|
| 2906 |
+
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
|
| 2907 |
+
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
|
| 2908 |
+
"license": "MIT"
|
| 2909 |
+
},
|
| 2910 |
"node_modules/rimraf": {
|
| 2911 |
"version": "3.0.2",
|
| 2912 |
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
|
|
|
| 3056 |
"node": ">=0.10.0"
|
| 3057 |
}
|
| 3058 |
},
|
| 3059 |
+
"node_modules/sharp": {
|
| 3060 |
+
"version": "0.34.3",
|
| 3061 |
+
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
|
| 3062 |
+
"integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
|
| 3063 |
+
"hasInstallScript": true,
|
| 3064 |
+
"license": "Apache-2.0",
|
| 3065 |
+
"dependencies": {
|
| 3066 |
+
"color": "^4.2.3",
|
| 3067 |
+
"detect-libc": "^2.0.4",
|
| 3068 |
+
"semver": "^7.7.2"
|
| 3069 |
+
},
|
| 3070 |
+
"engines": {
|
| 3071 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 3072 |
+
},
|
| 3073 |
+
"funding": {
|
| 3074 |
+
"url": "https://opencollective.com/libvips"
|
| 3075 |
+
},
|
| 3076 |
+
"optionalDependencies": {
|
| 3077 |
+
"@img/sharp-darwin-arm64": "0.34.3",
|
| 3078 |
+
"@img/sharp-darwin-x64": "0.34.3",
|
| 3079 |
+
"@img/sharp-libvips-darwin-arm64": "1.2.0",
|
| 3080 |
+
"@img/sharp-libvips-darwin-x64": "1.2.0",
|
| 3081 |
+
"@img/sharp-libvips-linux-arm": "1.2.0",
|
| 3082 |
+
"@img/sharp-libvips-linux-arm64": "1.2.0",
|
| 3083 |
+
"@img/sharp-libvips-linux-ppc64": "1.2.0",
|
| 3084 |
+
"@img/sharp-libvips-linux-s390x": "1.2.0",
|
| 3085 |
+
"@img/sharp-libvips-linux-x64": "1.2.0",
|
| 3086 |
+
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
|
| 3087 |
+
"@img/sharp-libvips-linuxmusl-x64": "1.2.0",
|
| 3088 |
+
"@img/sharp-linux-arm": "0.34.3",
|
| 3089 |
+
"@img/sharp-linux-arm64": "0.34.3",
|
| 3090 |
+
"@img/sharp-linux-ppc64": "0.34.3",
|
| 3091 |
+
"@img/sharp-linux-s390x": "0.34.3",
|
| 3092 |
+
"@img/sharp-linux-x64": "0.34.3",
|
| 3093 |
+
"@img/sharp-linuxmusl-arm64": "0.34.3",
|
| 3094 |
+
"@img/sharp-linuxmusl-x64": "0.34.3",
|
| 3095 |
+
"@img/sharp-wasm32": "0.34.3",
|
| 3096 |
+
"@img/sharp-win32-arm64": "0.34.3",
|
| 3097 |
+
"@img/sharp-win32-ia32": "0.34.3",
|
| 3098 |
+
"@img/sharp-win32-x64": "0.34.3"
|
| 3099 |
+
}
|
| 3100 |
+
},
|
| 3101 |
"node_modules/shebang-command": {
|
| 3102 |
"version": "2.0.0",
|
| 3103 |
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
|
|
|
| 3193 |
"url": "https://github.com/sponsors/ljharb"
|
| 3194 |
}
|
| 3195 |
},
|
| 3196 |
+
"node_modules/simple-swizzle": {
|
| 3197 |
+
"version": "0.2.2",
|
| 3198 |
+
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
| 3199 |
+
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
| 3200 |
+
"license": "MIT",
|
| 3201 |
+
"dependencies": {
|
| 3202 |
+
"is-arrayish": "^0.3.1"
|
| 3203 |
+
}
|
| 3204 |
+
},
|
| 3205 |
+
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
| 3206 |
+
"version": "0.3.2",
|
| 3207 |
+
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
| 3208 |
+
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
| 3209 |
+
"license": "MIT"
|
| 3210 |
+
},
|
| 3211 |
"node_modules/simple-update-notifier": {
|
| 3212 |
"version": "2.0.0",
|
| 3213 |
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
|
|
|
| 3404 |
"dev": true,
|
| 3405 |
"license": "MIT"
|
| 3406 |
},
|
| 3407 |
+
"node_modules/tiny-inflate": {
|
| 3408 |
+
"version": "1.0.3",
|
| 3409 |
+
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
| 3410 |
+
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
| 3411 |
+
"license": "MIT"
|
| 3412 |
+
},
|
| 3413 |
"node_modules/to-regex-range": {
|
| 3414 |
"version": "5.0.1",
|
| 3415 |
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
|
|
| 3518 |
"license": "MIT",
|
| 3519 |
"optional": true
|
| 3520 |
},
|
| 3521 |
+
"node_modules/unicode-properties": {
|
| 3522 |
+
"version": "1.4.1",
|
| 3523 |
+
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
| 3524 |
+
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
| 3525 |
+
"license": "MIT",
|
| 3526 |
+
"dependencies": {
|
| 3527 |
+
"base64-js": "^1.3.0",
|
| 3528 |
+
"unicode-trie": "^2.0.0"
|
| 3529 |
+
}
|
| 3530 |
+
},
|
| 3531 |
+
"node_modules/unicode-trie": {
|
| 3532 |
+
"version": "2.0.0",
|
| 3533 |
+
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
| 3534 |
+
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
| 3535 |
+
"license": "MIT",
|
| 3536 |
+
"dependencies": {
|
| 3537 |
+
"pako": "^0.2.5",
|
| 3538 |
+
"tiny-inflate": "^1.0.0"
|
| 3539 |
+
}
|
| 3540 |
+
},
|
| 3541 |
"node_modules/universalify": {
|
| 3542 |
"version": "2.0.1",
|
| 3543 |
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
package.json
CHANGED
|
@@ -7,9 +7,11 @@
|
|
| 7 |
"axios": "^1.11.0",
|
| 8 |
"cors": "^2.8.5",
|
| 9 |
"express": "^5.1.0",
|
|
|
|
| 10 |
"puppeteer": "^24.16.2",
|
| 11 |
"puppeteer-extra": "^3.3.6",
|
| 12 |
-
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
|
|
|
| 13 |
},
|
| 14 |
"devDependencies": {
|
| 15 |
"nodemon": "^3.1.10",
|
|
|
|
| 7 |
"axios": "^1.11.0",
|
| 8 |
"cors": "^2.8.5",
|
| 9 |
"express": "^5.1.0",
|
| 10 |
+
"pdfkit": "^0.17.1",
|
| 11 |
"puppeteer": "^24.16.2",
|
| 12 |
"puppeteer-extra": "^3.3.6",
|
| 13 |
+
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
| 14 |
+
"sharp": "^0.34.3"
|
| 15 |
},
|
| 16 |
"devDependencies": {
|
| 17 |
"nodemon": "^3.1.10",
|
server copy.js
CHANGED
|
@@ -1,16 +1,50 @@
|
|
| 1 |
const express = require('express');
|
| 2 |
-
const
|
|
|
|
| 3 |
const cors = require('cors');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
const app = express();
|
| 5 |
const port = 7860;
|
| 6 |
|
| 7 |
app.use(cors());
|
| 8 |
app.use(express.json());
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
console.log("πͺ Starting comprehensive cookie and restriction bypass...");
|
| 15 |
// Step 1: Set cookies before page load
|
| 16 |
const preCookies = [
|
|
@@ -31,7 +65,7 @@ const bypassCookiesAndRestrictions = async (page) => {
|
|
| 31 |
}
|
| 32 |
}
|
| 33 |
|
| 34 |
-
// Step 2: Inject CSS to hide cookie banners immediately
|
| 35 |
await page.addStyleTag({
|
| 36 |
content: `
|
| 37 |
/* Hide all possible cookie banners */
|
|
@@ -72,7 +106,7 @@ const bypassCookiesAndRestrictions = async (page) => {
|
|
| 72 |
`
|
| 73 |
});
|
| 74 |
|
| 75 |
-
// Step 3: Inject JavaScript to handle dynamic cookie banners
|
| 76 |
await page.evaluateOnNewDocument(() => {
|
| 77 |
// Override common cookie consent functions
|
| 78 |
window.cookieConsent = { accepted: true };
|
|
@@ -123,36 +157,26 @@ const bypassCookiesAndRestrictions = async (page) => {
|
|
| 123 |
}, 1000);
|
| 124 |
});
|
| 125 |
|
|
|
|
| 126 |
return true;
|
| 127 |
};
|
| 128 |
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
const unblurContent = async (page) => {
|
| 133 |
console.log("π Unblurring content and bypassing premium restrictions...");
|
| 134 |
await page.evaluate(() => {
|
| 135 |
-
// Function to remove all visual restrictions
|
| 136 |
const removeRestrictions = () => {
|
| 137 |
const removeBySelector = (selector) => {
|
| 138 |
document.querySelectorAll(selector).forEach(el => el.remove());
|
| 139 |
};
|
| 140 |
|
| 141 |
-
|
| 142 |
-
removeBySelector("#adbox");
|
| 143 |
-
removeBySelector(".adsbox");
|
| 144 |
-
removeBySelector(".ad-box");
|
| 145 |
-
removeBySelector(".banner-ads");
|
| 146 |
-
removeBySelector(".advert");
|
| 147 |
-
|
| 148 |
-
// Remove premium banner container
|
| 149 |
removeBySelector(".PremiumBannerBlobWrapper_overflow-wrapper__xsaS8");
|
| 150 |
|
| 151 |
-
// Enhanced blur removal
|
| 152 |
const removeBlur = (element = document) => {
|
| 153 |
element.querySelectorAll("*").forEach(el => {
|
| 154 |
const style = window.getComputedStyle(el);
|
| 155 |
-
// Check for blur via filter, backdrop-filter, or class names
|
| 156 |
if (
|
| 157 |
style.filter?.includes("blur") ||
|
| 158 |
style.backdropFilter?.includes("blur") ||
|
|
@@ -167,41 +191,12 @@ const unblurContent = async (page) => {
|
|
| 167 |
el.classList.remove("blur", "blurred", "premium-blur");
|
| 168 |
}
|
| 169 |
}
|
| 170 |
-
// Check parent elements for blur-inducing styles
|
| 171 |
-
const parent = el.parentElement;
|
| 172 |
-
if (parent) {
|
| 173 |
-
const parentStyle = window.getComputedStyle(parent);
|
| 174 |
-
if (
|
| 175 |
-
parentStyle.filter?.includes("blur") ||
|
| 176 |
-
parentStyle.backdropFilter?.includes("blur") ||
|
| 177 |
-
parseFloat(parentStyle.opacity) < 1
|
| 178 |
-
) {
|
| 179 |
-
parent.style.filter = "none !important";
|
| 180 |
-
parent.style.backdropFilter = "none !important";
|
| 181 |
-
parent.style.opacity = "1 !important";
|
| 182 |
-
}
|
| 183 |
-
}
|
| 184 |
});
|
| 185 |
};
|
| 186 |
|
| 187 |
-
// Remove dark overlays and paywall-like elements
|
| 188 |
-
document.querySelectorAll("div, section, aside").forEach(el => {
|
| 189 |
-
const style = window.getComputedStyle(el);
|
| 190 |
-
if (
|
| 191 |
-
(style.backgroundColor.includes("rgba") && (style.backgroundColor.includes("0.5") || parseFloat(style.zIndex) > 1000)) ||
|
| 192 |
-
(el.className && el.className.toString().toLowerCase().includes("overlay")) ||
|
| 193 |
-
(el.className && el.className.toString().toLowerCase().includes("paywall"))
|
| 194 |
-
) {
|
| 195 |
-
el.remove();
|
| 196 |
-
}
|
| 197 |
-
});
|
| 198 |
-
|
| 199 |
removeBlur();
|
|
|
|
| 200 |
|
| 201 |
-
// Remove other restrictions
|
| 202 |
-
removeBySelector('[class*="blur" i], [class*="premium" i], [class*="paywall" i], [class*="sample-preview-blur" i]');
|
| 203 |
-
|
| 204 |
-
// Ensure document content is visible
|
| 205 |
const contentSelectors = [
|
| 206 |
'.document-content', '.page-content', '.content', '[data-page]', '[data-testid*="document"]',
|
| 207 |
'[data-testid*="page"]', '.page', '.document-page', 'main', 'article'
|
|
@@ -215,53 +210,44 @@ const unblurContent = async (page) => {
|
|
| 215 |
el.style.setProperty('pointer-events', 'auto', 'important');
|
| 216 |
});
|
| 217 |
});
|
| 218 |
-
|
| 219 |
-
// Remove overlay divs that might be blocking content
|
| 220 |
-
const overlays = document.querySelectorAll(`
|
| 221 |
-
[class*="overlay" i], [class*="modal" i], [class*="popup" i], [class*="banner" i],
|
| 222 |
-
[style*="position: fixed"], [style*="position: absolute"][style*="z-index"]
|
| 223 |
-
`);
|
| 224 |
-
overlays.forEach(overlay => {
|
| 225 |
-
const text = overlay.textContent || '';
|
| 226 |
-
if (text.includes('premium') || text.includes('unlock') || text.includes('subscribe') || text.includes('cookie') || text.includes('consent') || text.includes('login')) {
|
| 227 |
-
overlay.remove();
|
| 228 |
-
}
|
| 229 |
-
});
|
| 230 |
};
|
| 231 |
|
| 232 |
-
// Run immediately
|
| 233 |
removeRestrictions();
|
| 234 |
-
|
| 235 |
-
// Run periodically
|
| 236 |
const intervalId = setInterval(removeRestrictions, 2000);
|
| 237 |
-
|
| 238 |
-
// Clean up after 60 seconds
|
| 239 |
-
setTimeout(() => {
|
| 240 |
-
clearInterval(intervalId);
|
| 241 |
-
}, 60000);
|
| 242 |
});
|
|
|
|
|
|
|
| 243 |
};
|
| 244 |
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
const applyPrintStyles = async (page) => {
|
| 249 |
console.log("π¨οΈ Applying print styles for clean PDF...");
|
| 250 |
await page.evaluate(() => {
|
| 251 |
const style = document.createElement("style");
|
| 252 |
style.id = "print-style-extension";
|
| 253 |
style.innerHTML = `
|
| 254 |
@page {
|
|
|
|
| 255 |
size: A4 portrait;
|
| 256 |
-
margin:
|
| 257 |
}
|
| 258 |
@media print {
|
| 259 |
html, body {
|
|
|
|
|
|
|
|
|
|
| 260 |
margin: 0 !important;
|
| 261 |
padding: 0 !important;
|
| 262 |
overflow: visible !important;
|
|
|
|
|
|
|
| 263 |
}
|
|
|
|
|
|
|
| 264 |
header, footer, nav, aside, .no-print, .ads, .sidebar, .premium-banner,
|
|
|
|
| 265 |
.ViewerToolbar, .Layout_info-bar-wrapper__He0Ho, .Sidebar_sidebar-scrollable__kqeBZ,
|
| 266 |
.HeaderWrapper_header-wrapper__mCmf3, .Layout_visible-content-bottom-wrapper-sticky__yaaAB,
|
| 267 |
.Layout_bottom-section-wrapper__yBWWk, .Layout_footer-wrapper__bheJQ,
|
|
@@ -269,21 +255,32 @@ const applyPrintStyles = async (page) => {
|
|
| 269 |
.Layout_sidebar-wrapper__unavM, .Layout_is-open__9DQr4 {
|
| 270 |
display: none !important;
|
| 271 |
}
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
color: black !important;
|
| 275 |
-
}
|
| 276 |
* {
|
| 277 |
box-shadow: none !important;
|
| 278 |
background: transparent !important;
|
|
|
|
| 279 |
}
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
width: 100% !important;
|
| 284 |
-
max-width:
|
| 285 |
-
margin: 0
|
|
|
|
|
|
|
|
|
|
| 286 |
}
|
|
|
|
|
|
|
| 287 |
[data-page], .page, .document-page, img {
|
| 288 |
page-break-after: always !important;
|
| 289 |
page-break-inside: avoid !important;
|
|
@@ -291,21 +288,25 @@ const applyPrintStyles = async (page) => {
|
|
| 291 |
width: 100% !important;
|
| 292 |
max-width: 100% !important;
|
| 293 |
height: auto !important;
|
|
|
|
|
|
|
|
|
|
| 294 |
}
|
| 295 |
}
|
| 296 |
`;
|
| 297 |
document.head.appendChild(style);
|
| 298 |
});
|
|
|
|
|
|
|
| 299 |
};
|
| 300 |
|
| 301 |
-
|
| 302 |
-
* Enhanced StuDocu downloader with comprehensive bypasses and login support
|
| 303 |
-
*/
|
| 304 |
-
const studocuDownloader = async (url, options = {}) => {
|
| 305 |
let browser;
|
| 306 |
try {
|
| 307 |
-
|
| 308 |
-
|
|
|
|
|
|
|
| 309 |
headless: true,
|
| 310 |
args: [
|
| 311 |
'--no-sandbox',
|
|
@@ -324,33 +325,44 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 324 |
'--disable-web-security',
|
| 325 |
'--disable-features=site-per-process',
|
| 326 |
'--disable-blink-features=AutomationControlled',
|
| 327 |
-
'--disable-extensions'
|
|
|
|
| 328 |
],
|
|
|
|
| 329 |
timeout: 300000,
|
|
|
|
|
|
|
| 330 |
});
|
| 331 |
|
| 332 |
const page = await browser.newPage();
|
| 333 |
|
| 334 |
-
//
|
| 335 |
-
|
| 336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
|
| 338 |
-
// Hide webdriver property
|
| 339 |
await page.evaluateOnNewDocument(() => {
|
| 340 |
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 341 |
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
| 342 |
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
|
| 343 |
});
|
| 344 |
|
| 345 |
-
|
| 346 |
-
await bypassCookiesAndRestrictions(page);
|
| 347 |
|
| 348 |
-
// Block unnecessary resources
|
| 349 |
await page.setRequestInterception(true);
|
| 350 |
page.on('request', (req) => {
|
| 351 |
const resourceType = req.resourceType();
|
| 352 |
-
const reqUrl = req.url();
|
| 353 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
if (
|
| 355 |
reqUrl.includes('doubleclick') ||
|
| 356 |
reqUrl.includes('googletagmanager') ||
|
|
@@ -362,7 +374,7 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 362 |
reqUrl.includes('mixpanel') ||
|
| 363 |
reqUrl.includes('onetrust') ||
|
| 364 |
reqUrl.includes('cookielaw') ||
|
| 365 |
-
(resourceType === 'other' && reqUrl.includes('track'))
|
| 366 |
) {
|
| 367 |
req.abort();
|
| 368 |
} else {
|
|
@@ -370,53 +382,59 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 370 |
}
|
| 371 |
});
|
| 372 |
|
| 373 |
-
// Login if credentials provided (for premium content)
|
| 374 |
if (options.email && options.password) {
|
|
|
|
|
|
|
| 375 |
console.log("π Logging in to StuDocu...");
|
| 376 |
-
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded'
|
| 377 |
-
await page.waitForSelector('#email'
|
| 378 |
await page.type('#email', options.email);
|
| 379 |
await page.type('#password', options.password);
|
| 380 |
await page.click('button[type="submit"]');
|
| 381 |
try {
|
| 382 |
-
await page.waitForNavigation({ waitUntil: 'networkidle2'
|
| 383 |
-
|
| 384 |
-
await page.waitForSelector('.user-profile, [data-testid="user-menu"]', { timeout: 10000 });
|
| 385 |
console.log("β
Login successful.");
|
|
|
|
| 386 |
} catch (e) {
|
| 387 |
console.error("β Login failed:", e.message);
|
| 388 |
-
throw new Error("Login failed. Check credentials
|
| 389 |
}
|
| 390 |
-
} else {
|
| 391 |
-
console.log("β οΈ No login credentials provided. Full unblurred content requires premium account.");
|
| 392 |
}
|
| 393 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
console.log(`π Navigating to ${url}...`);
|
| 395 |
-
|
| 396 |
let navigationSuccess = false;
|
| 397 |
let attempts = 0;
|
| 398 |
-
const maxAttempts =
|
| 399 |
while (!navigationSuccess && attempts < maxAttempts) {
|
| 400 |
try {
|
| 401 |
attempts++;
|
|
|
|
| 402 |
console.log(`Navigation attempt ${attempts}/${maxAttempts}`);
|
| 403 |
-
await page.goto(url, { waitUntil: 'domcontentloaded'
|
| 404 |
navigationSuccess = true;
|
| 405 |
} catch (e) {
|
| 406 |
console.log(`Navigation attempt ${attempts} failed:`, e.message);
|
| 407 |
if (attempts >= maxAttempts) throw e;
|
| 408 |
-
await new Promise(resolve => setTimeout(resolve,
|
| 409 |
}
|
| 410 |
}
|
| 411 |
|
| 412 |
-
|
| 413 |
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 414 |
|
| 415 |
-
|
| 416 |
-
await unblurContent(page);
|
| 417 |
|
| 418 |
-
|
| 419 |
console.log("β³ Waiting for document content to load...");
|
|
|
|
| 420 |
const contentSelectors = [
|
| 421 |
'.document-content', '.page-content', '[data-page]', '[data-testid*="document"]',
|
| 422 |
'img[src*="document"]', 'img[src*="page"]', '.page', 'main img', 'article img'
|
|
@@ -424,7 +442,7 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 424 |
let contentFound = false;
|
| 425 |
for (const selector of contentSelectors) {
|
| 426 |
try {
|
| 427 |
-
await page.waitForSelector(selector
|
| 428 |
console.log(`β
Found content with selector: ${selector}`);
|
| 429 |
contentFound = true;
|
| 430 |
break;
|
|
@@ -437,8 +455,9 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 437 |
console.log("β οΈ No specific content selector found, proceeding with page content...");
|
| 438 |
}
|
| 439 |
|
| 440 |
-
|
| 441 |
console.log("π Loading all document pages with enhanced slow scroll...");
|
|
|
|
| 442 |
await page.evaluate(async () => {
|
| 443 |
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
| 444 |
let scrollHeight = document.body.scrollHeight;
|
|
@@ -448,23 +467,24 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 448 |
while (totalHeight < scrollHeight) {
|
| 449 |
window.scrollBy(0, distance);
|
| 450 |
totalHeight += distance;
|
| 451 |
-
await delay(500);
|
| 452 |
}
|
| 453 |
-
await delay(2000);
|
| 454 |
const newHeight = document.body.scrollHeight;
|
| 455 |
if (newHeight === scrollHeight) break;
|
| 456 |
scrollHeight = newHeight;
|
| 457 |
}
|
| 458 |
-
// Scroll to top
|
| 459 |
window.scrollTo({ top: 0, behavior: "smooth" });
|
| 460 |
await delay(1000);
|
| 461 |
});
|
| 462 |
|
| 463 |
-
|
| 464 |
-
await unblurContent(page);
|
| 465 |
|
| 466 |
-
|
|
|
|
|
|
|
| 467 |
console.log("πΌοΈ Waiting for all images to load...");
|
|
|
|
| 468 |
await page.evaluate(async () => {
|
| 469 |
const images = Array.from(document.querySelectorAll('img'));
|
| 470 |
await Promise.all(images.map(img => {
|
|
@@ -477,11 +497,9 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 477 |
}));
|
| 478 |
});
|
| 479 |
|
| 480 |
-
|
| 481 |
-
|
| 482 |
|
| 483 |
-
// Set exact height to avoid extra blank pages
|
| 484 |
-
console.log("π Setting exact document height...");
|
| 485 |
await page.evaluate(() => {
|
| 486 |
const getDocumentHeight = () => Math.max(
|
| 487 |
document.body.scrollHeight, document.body.offsetHeight,
|
|
@@ -493,7 +511,6 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 493 |
document.body.style.overflow = 'hidden !important';
|
| 494 |
});
|
| 495 |
|
| 496 |
-
// Final content verification
|
| 497 |
const contentCheck = await page.evaluate(() => {
|
| 498 |
const textContent = document.body.textContent || '';
|
| 499 |
const images = document.querySelectorAll('img');
|
|
@@ -505,10 +522,10 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 505 |
totalText: textContent.length,
|
| 506 |
totalImages: images.length,
|
| 507 |
documentImages: documentImages.length,
|
| 508 |
-
hasDocumentContent: documentImages.length > 0 || textContent.length > 1000
|
| 509 |
-
sampleText: textContent.substring(0, 300)
|
| 510 |
};
|
| 511 |
});
|
|
|
|
| 512 |
console.log("π Content verification:", {
|
| 513 |
textLength: contentCheck.totalText,
|
| 514 |
images: contentCheck.totalImages,
|
|
@@ -517,40 +534,33 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 517 |
});
|
| 518 |
|
| 519 |
if (!contentCheck.hasDocumentContent) {
|
| 520 |
-
console.warn("β οΈ Warning: Limited document content detected.
|
| 521 |
}
|
| 522 |
|
| 523 |
-
|
| 524 |
-
await applyPrintStyles(page);
|
| 525 |
-
|
| 526 |
-
// Emulate print media
|
| 527 |
await page.emulateMediaType('print');
|
| 528 |
|
| 529 |
-
|
| 530 |
console.log("π Generating PDF...");
|
|
|
|
| 531 |
const pdfBuffer = await page.pdf({
|
| 532 |
printBackground: true,
|
| 533 |
preferCSSPageSize: true,
|
| 534 |
displayHeaderFooter: false,
|
| 535 |
-
|
|
|
|
| 536 |
scale: 1,
|
| 537 |
omitBackground: false
|
| 538 |
});
|
| 539 |
|
|
|
|
| 540 |
console.log(`β
PDF generated successfully! Size: ${(pdfBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
| 541 |
return pdfBuffer;
|
| 542 |
|
| 543 |
} catch (error) {
|
|
|
|
| 544 |
console.error("β Error during PDF generation:", error);
|
| 545 |
-
|
| 546 |
-
throw new Error("Request timed out. The document may be taking too long to load. Please try again.");
|
| 547 |
-
} else if (error.message.includes('net::')) {
|
| 548 |
-
throw new Error("Network error. Please check the URL and your internet connection.");
|
| 549 |
-
} else if (error.message.includes('ERR_BLOCKED')) {
|
| 550 |
-
throw new Error("Access blocked. Try again or check if the document is publicly accessible.");
|
| 551 |
-
} else {
|
| 552 |
-
throw new Error(`Failed to generate PDF: ${error.message}`);
|
| 553 |
-
}
|
| 554 |
} finally {
|
| 555 |
if (browser) {
|
| 556 |
console.log("π Closing browser...");
|
|
@@ -563,221 +573,114 @@ const studocuDownloader = async (url, options = {}) => {
|
|
| 563 |
}
|
| 564 |
};
|
| 565 |
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
console.log("π Launching browser for streaming with stealth configuration...");
|
| 573 |
-
browser = await puppeteer.launch({
|
| 574 |
-
headless: true,
|
| 575 |
-
args: [
|
| 576 |
-
'--no-sandbox',
|
| 577 |
-
'--disable-setuid-sandbox',
|
| 578 |
-
'--disable-dev-shm-usage',
|
| 579 |
-
'--disable-accelerated-2d-canvas',
|
| 580 |
-
'--no-first-run',
|
| 581 |
-
'--no-zygote',
|
| 582 |
-
'--disable-gpu'
|
| 583 |
-
],
|
| 584 |
-
timeout: 300000,
|
| 585 |
-
});
|
| 586 |
-
|
| 587 |
-
const page = await browser.newPage();
|
| 588 |
-
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');
|
| 589 |
-
await page.setViewport({ width: 794, height: 1122 });
|
| 590 |
-
|
| 591 |
-
await page.evaluateOnNewDocument(() => {
|
| 592 |
-
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 593 |
-
});
|
| 594 |
-
|
| 595 |
-
await bypassCookiesAndRestrictions(page);
|
| 596 |
-
|
| 597 |
-
await page.setRequestInterception(true);
|
| 598 |
-
page.on('request', (req) => {
|
| 599 |
-
if (['image', 'stylesheet', 'font', 'other'].includes(req.resourceType()) && !req.url().includes('studocu.com')) {
|
| 600 |
-
req.abort();
|
| 601 |
-
} else {
|
| 602 |
-
req.continue();
|
| 603 |
-
}
|
| 604 |
-
});
|
| 605 |
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded' });
|
| 609 |
-
await page.waitForSelector('#email');
|
| 610 |
-
await page.type('#email', options.email);
|
| 611 |
-
await page.type('#password', options.password);
|
| 612 |
-
await page.click('button[type="submit"]');
|
| 613 |
-
await page.waitForNavigation({ waitUntil: 'networkidle2' });
|
| 614 |
-
console.log("β
Login successful for streaming.");
|
| 615 |
-
}
|
| 616 |
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 620 |
|
| 621 |
-
|
| 622 |
|
| 623 |
-
|
| 624 |
-
await page.waitForSelector('[data-page]', { timeout: 30000 });
|
| 625 |
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
totalHeight += distance;
|
| 635 |
-
if (totalHeight >= scrollHeight) {
|
| 636 |
-
clearInterval(timer);
|
| 637 |
-
resolve();
|
| 638 |
-
}
|
| 639 |
-
}, 100);
|
| 640 |
-
});
|
| 641 |
});
|
|
|
|
| 642 |
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
res.setHeader('Transfer-Encoding', 'chunked');
|
| 657 |
-
|
| 658 |
-
for (let i = 0; i < totalPages; i++) {
|
| 659 |
-
console.log(`π¨ Rendering page ${i + 1} of ${totalPages}...`);
|
| 660 |
-
const pageElement = pageElements[i];
|
| 661 |
-
const imageData = await pageElement.screenshot({ type: 'png', encoding: 'base64' });
|
| 662 |
-
|
| 663 |
-
const progressUpdate = {
|
| 664 |
-
pageNumber: i + 1,
|
| 665 |
-
totalPages: totalPages,
|
| 666 |
-
imageData: `data:image/png;base64,${imageData}`
|
| 667 |
-
};
|
| 668 |
-
|
| 669 |
-
res.write(JSON.stringify(progressUpdate) + '\n'); // Send as a new line delimited JSON
|
| 670 |
-
}
|
| 671 |
-
|
| 672 |
-
console.log("β
All pages have been rendered and sent.");
|
| 673 |
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
};
|
| 679 |
-
if (!res.headersSent) {
|
| 680 |
-
res.status(500).json(errorResponse);
|
| 681 |
-
} else {
|
| 682 |
-
res.write(JSON.stringify(errorResponse) + '\n');
|
| 683 |
}
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
console.log("π Closing browser for streaming...");
|
| 687 |
-
await browser.close();
|
| 688 |
-
}
|
| 689 |
-
if (!res.writableEnded) {
|
| 690 |
-
res.end(); // End the stream
|
| 691 |
}
|
| 692 |
}
|
| 693 |
-
};
|
| 694 |
|
|
|
|
|
|
|
| 695 |
|
| 696 |
-
|
|
|
|
|
|
|
| 697 |
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
const { url, filename, email, password } = req.body;
|
| 701 |
-
if (!url) {
|
| 702 |
-
return res.status(400).json({ error: 'URL is required.' });
|
| 703 |
}
|
| 704 |
-
|
| 705 |
-
|
|
|
|
| 706 |
}
|
| 707 |
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
normalizedUrl = 'https://' + normalizedUrl;
|
| 711 |
}
|
| 712 |
|
| 713 |
-
|
| 714 |
-
try {
|
| 715 |
-
const startTime = Date.now();
|
| 716 |
-
const pdfBuffer = await studocuDownloader(normalizedUrl, { filename, email, password });
|
| 717 |
-
const processingTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
| 718 |
res.setHeader('Content-Type', 'application/pdf');
|
| 719 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 720 |
-
res.
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
} catch (error) {
|
| 724 |
-
console.error(`β Failed to process ${normalizedUrl}:`, error.message);
|
| 725 |
-
res.status(500).json({ error: error.message });
|
| 726 |
-
}
|
| 727 |
-
});
|
| 728 |
-
|
| 729 |
-
// NEW: Endpoint for streaming the document page by page
|
| 730 |
-
app.post('/api/download-stream', async (req, res) => {
|
| 731 |
-
const { url, email, password } = req.body;
|
| 732 |
-
if (!url) {
|
| 733 |
-
return res.status(400).json({ error: 'URL is required.' });
|
| 734 |
-
}
|
| 735 |
-
if (!url.includes('studocu.com')) {
|
| 736 |
-
return res.status(400).json({ error: 'Please provide a valid StuDocu URL.' });
|
| 737 |
-
}
|
| 738 |
-
|
| 739 |
-
let normalizedUrl = url.trim();
|
| 740 |
-
if (!normalizedUrl.startsWith('http')) {
|
| 741 |
-
normalizedUrl = 'https://' + normalizedUrl;
|
| 742 |
-
}
|
| 743 |
-
|
| 744 |
-
console.log(`π― Processing stream request for: ${normalizedUrl}`);
|
| 745 |
-
try {
|
| 746 |
-
await studocuDownloaderStreamed(normalizedUrl, { email, password }, res);
|
| 747 |
-
console.log(`π Stream request completed for ${normalizedUrl}`);
|
| 748 |
-
} catch (error) {
|
| 749 |
-
console.error(`β Failed to process stream for ${normalizedUrl}:`, error.message);
|
| 750 |
-
// Error is handled within the downloader function to ensure proper response closure
|
| 751 |
}
|
| 752 |
});
|
| 753 |
|
| 754 |
-
|
| 755 |
app.get('/health', (req, res) => {
|
| 756 |
res.json({
|
| 757 |
status: 'healthy',
|
| 758 |
timestamp: new Date().toISOString(),
|
| 759 |
-
uptime: process.uptime()
|
|
|
|
| 760 |
});
|
| 761 |
});
|
| 762 |
|
| 763 |
app.get('/', (req, res) => {
|
| 764 |
res.json({
|
| 765 |
-
message: 'π Enhanced StuDocu Downloader API v5.
|
| 766 |
-
version: '5.
|
| 767 |
features: [
|
| 768 |
'πͺ Advanced cookie banner bypass',
|
| 769 |
-
'π Premium content unblurring
|
| 770 |
-
'π Login support for full
|
| 771 |
-
'
|
| 772 |
-
'π
|
| 773 |
-
'
|
| 774 |
],
|
| 775 |
endpoints: {
|
| 776 |
-
|
| 777 |
-
|
|
|
|
| 778 |
health: 'GET /health'
|
| 779 |
-
}
|
| 780 |
-
note: 'For full unblurred content, provide premium email and password. Blurring is often server-side, so CSS bypass may not suffice without login.'
|
| 781 |
});
|
| 782 |
});
|
| 783 |
|
|
@@ -792,6 +695,6 @@ process.on('SIGINT', () => {
|
|
| 792 |
});
|
| 793 |
|
| 794 |
app.listen(port, () => {
|
| 795 |
-
console.log(`π Enhanced StuDocu Downloader v5.
|
| 796 |
-
console.log(`β¨ Features:
|
| 797 |
});
|
|
|
|
| 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;
|
| 11 |
|
| 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 |
+
|
| 19 |
+
class ProgressTracker extends EventEmitter {
|
| 20 |
+
constructor(sessionId) {
|
| 21 |
+
super();
|
| 22 |
+
this.sessionId = sessionId;
|
| 23 |
+
this.progress = 0;
|
| 24 |
+
this.status = 'initializing';
|
| 25 |
+
this.message = '';
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
updateProgress(progress, status, message) {
|
| 29 |
+
this.progress = progress;
|
| 30 |
+
this.status = status;
|
| 31 |
+
this.message = message;
|
| 32 |
+
const update = {
|
| 33 |
+
sessionId: this.sessionId,
|
| 34 |
+
progress,
|
| 35 |
+
status,
|
| 36 |
+
message,
|
| 37 |
+
timestamp: new Date().toISOString()
|
| 38 |
+
};
|
| 39 |
+
this.emit('progress', update);
|
| 40 |
+
console.log(`π [${this.sessionId}] ${progress}% - ${status}: ${message}`);
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
// --- Puppeteer Logic (Updated for Stealth and Reliability) ---
|
| 45 |
+
const bypassCookiesAndRestrictions = async (page, progressTracker) => {
|
| 46 |
+
progressTracker?.updateProgress(5, 'bypassing', 'Setting up cookie bypass...');
|
| 47 |
+
|
| 48 |
console.log("πͺ Starting comprehensive cookie and restriction bypass...");
|
| 49 |
// Step 1: Set cookies before page load
|
| 50 |
const preCookies = [
|
|
|
|
| 65 |
}
|
| 66 |
}
|
| 67 |
|
| 68 |
+
// Step 2: Inject CSS to hide cookie banners immediately (Unchanged)
|
| 69 |
await page.addStyleTag({
|
| 70 |
content: `
|
| 71 |
/* Hide all possible cookie banners */
|
|
|
|
| 106 |
`
|
| 107 |
});
|
| 108 |
|
| 109 |
+
// Step 3: Inject JavaScript to handle dynamic cookie banners (Unchanged)
|
| 110 |
await page.evaluateOnNewDocument(() => {
|
| 111 |
// Override common cookie consent functions
|
| 112 |
window.cookieConsent = { accepted: true };
|
|
|
|
| 157 |
}, 1000);
|
| 158 |
});
|
| 159 |
|
| 160 |
+
progressTracker?.updateProgress(10, 'bypassing', 'Cookie bypass configured successfully');
|
| 161 |
return true;
|
| 162 |
};
|
| 163 |
|
| 164 |
+
const unblurContent = async (page, progressTracker) => {
|
| 165 |
+
progressTracker?.updateProgress(15, 'unblurring', 'Removing content restrictions...');
|
| 166 |
+
|
|
|
|
| 167 |
console.log("π Unblurring content and bypassing premium restrictions...");
|
| 168 |
await page.evaluate(() => {
|
|
|
|
| 169 |
const removeRestrictions = () => {
|
| 170 |
const removeBySelector = (selector) => {
|
| 171 |
document.querySelectorAll(selector).forEach(el => el.remove());
|
| 172 |
};
|
| 173 |
|
| 174 |
+
removeBySelector("#adbox, .adsbox, .ad-box, .banner-ads, .advert");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
removeBySelector(".PremiumBannerBlobWrapper_overflow-wrapper__xsaS8");
|
| 176 |
|
|
|
|
| 177 |
const removeBlur = (element = document) => {
|
| 178 |
element.querySelectorAll("*").forEach(el => {
|
| 179 |
const style = window.getComputedStyle(el);
|
|
|
|
| 180 |
if (
|
| 181 |
style.filter?.includes("blur") ||
|
| 182 |
style.backdropFilter?.includes("blur") ||
|
|
|
|
| 191 |
el.classList.remove("blur", "blurred", "premium-blur");
|
| 192 |
}
|
| 193 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
});
|
| 195 |
};
|
| 196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
removeBlur();
|
| 198 |
+
removeBySelector('[class*="blur" i], [class*="premium" i], [class*="paywall" i]');
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
const contentSelectors = [
|
| 201 |
'.document-content', '.page-content', '.content', '[data-page]', '[data-testid*="document"]',
|
| 202 |
'[data-testid*="page"]', '.page', '.document-page', 'main', 'article'
|
|
|
|
| 210 |
el.style.setProperty('pointer-events', 'auto', 'important');
|
| 211 |
});
|
| 212 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
};
|
| 214 |
|
|
|
|
| 215 |
removeRestrictions();
|
|
|
|
|
|
|
| 216 |
const intervalId = setInterval(removeRestrictions, 2000);
|
| 217 |
+
setTimeout(() => clearInterval(intervalId), 60000);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
});
|
| 219 |
+
|
| 220 |
+
progressTracker?.updateProgress(20, 'unblurring', 'Content restrictions removed');
|
| 221 |
};
|
| 222 |
|
| 223 |
+
const applyPrintStyles = async (page, progressTracker) => {
|
| 224 |
+
progressTracker?.updateProgress(85, 'styling', 'Applying print styles...');
|
| 225 |
+
|
|
|
|
| 226 |
console.log("π¨οΈ Applying print styles for clean PDF...");
|
| 227 |
await page.evaluate(() => {
|
| 228 |
const style = document.createElement("style");
|
| 229 |
style.id = "print-style-extension";
|
| 230 |
style.innerHTML = `
|
| 231 |
@page {
|
| 232 |
+
/* Set page size to A4 and remove default margins */
|
| 233 |
size: A4 portrait;
|
| 234 |
+
margin: 0mm;
|
| 235 |
}
|
| 236 |
@media print {
|
| 237 |
html, body {
|
| 238 |
+
/* Ensure the body takes the full width and has no extra padding/margin */
|
| 239 |
+
width: 210mm !important;
|
| 240 |
+
height: auto !important;
|
| 241 |
margin: 0 !important;
|
| 242 |
padding: 0 !important;
|
| 243 |
overflow: visible !important;
|
| 244 |
+
background: white !important;
|
| 245 |
+
color: black !important;
|
| 246 |
}
|
| 247 |
+
|
| 248 |
+
/* Remove all unwanted elements like headers, footers, sidebars, etc. */
|
| 249 |
header, footer, nav, aside, .no-print, .ads, .sidebar, .premium-banner,
|
| 250 |
+
[class*="Header"], [class*="Footer"], [class*="Sidebar"], [id*="Header"],
|
| 251 |
.ViewerToolbar, .Layout_info-bar-wrapper__He0Ho, .Sidebar_sidebar-scrollable__kqeBZ,
|
| 252 |
.HeaderWrapper_header-wrapper__mCmf3, .Layout_visible-content-bottom-wrapper-sticky__yaaAB,
|
| 253 |
.Layout_bottom-section-wrapper__yBWWk, .Layout_footer-wrapper__bheJQ,
|
|
|
|
| 255 |
.Layout_sidebar-wrapper__unavM, .Layout_is-open__9DQr4 {
|
| 256 |
display: none !important;
|
| 257 |
}
|
| 258 |
+
|
| 259 |
+
/* Force all elements to have a transparent background and no shadow */
|
|
|
|
|
|
|
| 260 |
* {
|
| 261 |
box-shadow: none !important;
|
| 262 |
background: transparent !important;
|
| 263 |
+
color: inherit !important;
|
| 264 |
}
|
| 265 |
+
|
| 266 |
+
/*
|
| 267 |
+
* KEY FIX: Target the main document container.
|
| 268 |
+
* Force it to be a block element, remove any transforms or max-widths,
|
| 269 |
+
* and center it perfectly within the page.
|
| 270 |
+
*/
|
| 271 |
+
.Viewer_document-wrapper__JPBWQ, .Viewer_document-wrapper__LXzoQ,
|
| 272 |
+
.Viewer_document-wrapper__XsO4j, .page-content, .document-viewer, #page-container {
|
| 273 |
+
position: static !important;
|
| 274 |
+
display: block !important;
|
| 275 |
width: 100% !important;
|
| 276 |
+
max-width: none !important;
|
| 277 |
+
margin: 0 !important;
|
| 278 |
+
padding: 0 !important;
|
| 279 |
+
box-sizing: border-box; /* Include padding in width calculation */
|
| 280 |
+
transform: none !important;
|
| 281 |
}
|
| 282 |
+
|
| 283 |
+
/* Ensure individual pages and images within the document use the full width */
|
| 284 |
[data-page], .page, .document-page, img {
|
| 285 |
page-break-after: always !important;
|
| 286 |
page-break-inside: avoid !important;
|
|
|
|
| 288 |
width: 100% !important;
|
| 289 |
max-width: 100% !important;
|
| 290 |
height: auto !important;
|
| 291 |
+
display: block !important;
|
| 292 |
+
margin: 0 !important;
|
| 293 |
+
padding: 0 !important;
|
| 294 |
}
|
| 295 |
}
|
| 296 |
`;
|
| 297 |
document.head.appendChild(style);
|
| 298 |
});
|
| 299 |
+
|
| 300 |
+
progressTracker?.updateProgress(88, 'styling', 'Print styles applied successfully');
|
| 301 |
};
|
| 302 |
|
| 303 |
+
const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
|
|
|
|
|
|
|
|
| 304 |
let browser;
|
| 305 |
try {
|
| 306 |
+
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 307 |
+
|
| 308 |
+
console.log("π Launching browser with enhanced stealth configuration...");
|
| 309 |
+
browser = await puppeteerExtra.launch({
|
| 310 |
headless: true,
|
| 311 |
args: [
|
| 312 |
'--no-sandbox',
|
|
|
|
| 325 |
'--disable-web-security',
|
| 326 |
'--disable-features=site-per-process',
|
| 327 |
'--disable-blink-features=AutomationControlled',
|
| 328 |
+
'--disable-extensions',
|
| 329 |
+
'--ignore-certificate-errors'
|
| 330 |
],
|
| 331 |
+
ignoreHTTPSErrors: true,
|
| 332 |
timeout: 300000,
|
| 333 |
+
// --- FIX: Increased protocolTimeout for long-running operations ---
|
| 334 |
+
protocolTimeout: 600000 // 10 minutes, increased from the default 30 seconds
|
| 335 |
});
|
| 336 |
|
| 337 |
const page = await browser.newPage();
|
| 338 |
|
| 339 |
+
// --- FIX: Increase default navigation and action timeouts ---
|
| 340 |
+
page.setDefaultNavigationTimeout(180000); // 3 minutes for navigation
|
| 341 |
+
page.setDefaultTimeout(180000); // 3 minutes for other actions (e.g., waitForSelector)
|
| 342 |
+
|
| 343 |
+
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 344 |
+
|
| 345 |
+
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');
|
| 346 |
+
await page.setViewport({ width: 794, height: 1122 }); // A4 size in pixels at 96 DPI
|
| 347 |
|
|
|
|
| 348 |
await page.evaluateOnNewDocument(() => {
|
| 349 |
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 350 |
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
| 351 |
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
|
| 352 |
});
|
| 353 |
|
| 354 |
+
await bypassCookiesAndRestrictions(page, progressTracker);
|
|
|
|
| 355 |
|
|
|
|
| 356 |
await page.setRequestInterception(true);
|
| 357 |
page.on('request', (req) => {
|
| 358 |
const resourceType = req.resourceType();
|
| 359 |
+
const reqUrl = req.url().toLowerCase();
|
| 360 |
+
|
| 361 |
+
if (resourceType === 'document') {
|
| 362 |
+
req.continue();
|
| 363 |
+
return;
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
if (
|
| 367 |
reqUrl.includes('doubleclick') ||
|
| 368 |
reqUrl.includes('googletagmanager') ||
|
|
|
|
| 374 |
reqUrl.includes('mixpanel') ||
|
| 375 |
reqUrl.includes('onetrust') ||
|
| 376 |
reqUrl.includes('cookielaw') ||
|
| 377 |
+
(resourceType === 'other' && reqUrl.includes('/track/'))
|
| 378 |
) {
|
| 379 |
req.abort();
|
| 380 |
} else {
|
|
|
|
| 382 |
}
|
| 383 |
});
|
| 384 |
|
|
|
|
| 385 |
if (options.email && options.password) {
|
| 386 |
+
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 387 |
+
|
| 388 |
console.log("π Logging in to StuDocu...");
|
| 389 |
+
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded' });
|
| 390 |
+
await page.waitForSelector('#email');
|
| 391 |
await page.type('#email', options.email);
|
| 392 |
await page.type('#password', options.password);
|
| 393 |
await page.click('button[type="submit"]');
|
| 394 |
try {
|
| 395 |
+
await page.waitForNavigation({ waitUntil: 'networkidle2' });
|
| 396 |
+
await page.waitForSelector('.user-profile, [data-testid="user-menu"]');
|
|
|
|
| 397 |
console.log("β
Login successful.");
|
| 398 |
+
progressTracker?.updateProgress(18, 'authenticated', 'Login successful');
|
| 399 |
} catch (e) {
|
| 400 |
console.error("β Login failed:", e.message);
|
| 401 |
+
throw new Error("Login failed. Check credentials or try again.");
|
| 402 |
}
|
|
|
|
|
|
|
| 403 |
}
|
| 404 |
|
| 405 |
+
progressTracker?.updateProgress(25, 'navigating', 'Navigating to homepage first for session setup...');
|
| 406 |
+
console.log(`π Navigating to homepage to simulate natural session...`);
|
| 407 |
+
await page.goto('https://www.studocu.com/en-us', { waitUntil: 'domcontentloaded' });
|
| 408 |
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
| 409 |
+
|
| 410 |
+
progressTracker?.updateProgress(30, 'navigating', 'Navigating to document...');
|
| 411 |
console.log(`π Navigating to ${url}...`);
|
| 412 |
+
|
| 413 |
let navigationSuccess = false;
|
| 414 |
let attempts = 0;
|
| 415 |
+
const maxAttempts = 5;
|
| 416 |
while (!navigationSuccess && attempts < maxAttempts) {
|
| 417 |
try {
|
| 418 |
attempts++;
|
| 419 |
+
progressTracker?.updateProgress(30 + (attempts * 5), 'navigating', `Navigation attempt ${attempts}/${maxAttempts}`);
|
| 420 |
console.log(`Navigation attempt ${attempts}/${maxAttempts}`);
|
| 421 |
+
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
| 422 |
navigationSuccess = true;
|
| 423 |
} catch (e) {
|
| 424 |
console.log(`Navigation attempt ${attempts} failed:`, e.message);
|
| 425 |
if (attempts >= maxAttempts) throw e;
|
| 426 |
+
await new Promise(resolve => setTimeout(resolve, 15000));
|
| 427 |
}
|
| 428 |
}
|
| 429 |
|
| 430 |
+
progressTracker?.updateProgress(40, 'loading', 'Page loaded, waiting for content...');
|
| 431 |
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 432 |
|
| 433 |
+
await unblurContent(page, progressTracker);
|
|
|
|
| 434 |
|
| 435 |
+
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 436 |
console.log("β³ Waiting for document content to load...");
|
| 437 |
+
|
| 438 |
const contentSelectors = [
|
| 439 |
'.document-content', '.page-content', '[data-page]', '[data-testid*="document"]',
|
| 440 |
'img[src*="document"]', 'img[src*="page"]', '.page', 'main img', 'article img'
|
|
|
|
| 442 |
let contentFound = false;
|
| 443 |
for (const selector of contentSelectors) {
|
| 444 |
try {
|
| 445 |
+
await page.waitForSelector(selector);
|
| 446 |
console.log(`β
Found content with selector: ${selector}`);
|
| 447 |
contentFound = true;
|
| 448 |
break;
|
|
|
|
| 455 |
console.log("β οΈ No specific content selector found, proceeding with page content...");
|
| 456 |
}
|
| 457 |
|
| 458 |
+
progressTracker?.updateProgress(50, 'scrolling', 'Loading all document pages...');
|
| 459 |
console.log("π Loading all document pages with enhanced slow scroll...");
|
| 460 |
+
|
| 461 |
await page.evaluate(async () => {
|
| 462 |
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
| 463 |
let scrollHeight = document.body.scrollHeight;
|
|
|
|
| 467 |
while (totalHeight < scrollHeight) {
|
| 468 |
window.scrollBy(0, distance);
|
| 469 |
totalHeight += distance;
|
| 470 |
+
await delay(500);
|
| 471 |
}
|
| 472 |
+
await delay(2000);
|
| 473 |
const newHeight = document.body.scrollHeight;
|
| 474 |
if (newHeight === scrollHeight) break;
|
| 475 |
scrollHeight = newHeight;
|
| 476 |
}
|
|
|
|
| 477 |
window.scrollTo({ top: 0, behavior: "smooth" });
|
| 478 |
await delay(1000);
|
| 479 |
});
|
| 480 |
|
| 481 |
+
progressTracker?.updateProgress(70, 'processing', 'Processing loaded content...');
|
|
|
|
| 482 |
|
| 483 |
+
await unblurContent(page, progressTracker);
|
| 484 |
+
|
| 485 |
+
progressTracker?.updateProgress(75, 'loading_images', 'Loading images...');
|
| 486 |
console.log("πΌοΈ Waiting for all images to load...");
|
| 487 |
+
|
| 488 |
await page.evaluate(async () => {
|
| 489 |
const images = Array.from(document.querySelectorAll('img'));
|
| 490 |
await Promise.all(images.map(img => {
|
|
|
|
| 497 |
}));
|
| 498 |
});
|
| 499 |
|
| 500 |
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 501 |
+
progressTracker?.updateProgress(80, 'finalizing', 'Preparing document for PDF generation...');
|
| 502 |
|
|
|
|
|
|
|
| 503 |
await page.evaluate(() => {
|
| 504 |
const getDocumentHeight = () => Math.max(
|
| 505 |
document.body.scrollHeight, document.body.offsetHeight,
|
|
|
|
| 511 |
document.body.style.overflow = 'hidden !important';
|
| 512 |
});
|
| 513 |
|
|
|
|
| 514 |
const contentCheck = await page.evaluate(() => {
|
| 515 |
const textContent = document.body.textContent || '';
|
| 516 |
const images = document.querySelectorAll('img');
|
|
|
|
| 522 |
totalText: textContent.length,
|
| 523 |
totalImages: images.length,
|
| 524 |
documentImages: documentImages.length,
|
| 525 |
+
hasDocumentContent: documentImages.length > 0 || textContent.length > 1000
|
|
|
|
| 526 |
};
|
| 527 |
});
|
| 528 |
+
|
| 529 |
console.log("π Content verification:", {
|
| 530 |
textLength: contentCheck.totalText,
|
| 531 |
images: contentCheck.totalImages,
|
|
|
|
| 534 |
});
|
| 535 |
|
| 536 |
if (!contentCheck.hasDocumentContent) {
|
| 537 |
+
console.warn("β οΈ Warning: Limited document content detected.");
|
| 538 |
}
|
| 539 |
|
| 540 |
+
await applyPrintStyles(page, progressTracker);
|
|
|
|
|
|
|
|
|
|
| 541 |
await page.emulateMediaType('print');
|
| 542 |
|
| 543 |
+
progressTracker?.updateProgress(90, 'generating', 'Generating PDF...');
|
| 544 |
console.log("π Generating PDF...");
|
| 545 |
+
|
| 546 |
const pdfBuffer = await page.pdf({
|
| 547 |
printBackground: true,
|
| 548 |
preferCSSPageSize: true,
|
| 549 |
displayHeaderFooter: false,
|
| 550 |
+
// --- FIX: Increased PDF generation timeout ---
|
| 551 |
+
timeout: 600000, // 10 minutes, increased from the default 30 seconds
|
| 552 |
scale: 1,
|
| 553 |
omitBackground: false
|
| 554 |
});
|
| 555 |
|
| 556 |
+
progressTracker?.updateProgress(100, 'completed', 'PDF generated successfully!');
|
| 557 |
console.log(`β
PDF generated successfully! Size: ${(pdfBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
| 558 |
return pdfBuffer;
|
| 559 |
|
| 560 |
} catch (error) {
|
| 561 |
+
progressTracker?.updateProgress(-1, 'error', error.message);
|
| 562 |
console.error("β Error during PDF generation:", error);
|
| 563 |
+
throw error;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
} finally {
|
| 565 |
if (browser) {
|
| 566 |
console.log("π Closing browser...");
|
|
|
|
| 573 |
}
|
| 574 |
};
|
| 575 |
|
| 576 |
+
// --- API Routes --- (Unchanged)
|
| 577 |
+
app.post('/api/request-download', (req, res) => {
|
| 578 |
+
const { url, email, password } = req.body;
|
| 579 |
+
if (!url || !url.includes('studocu.com')) {
|
| 580 |
+
return res.status(400).json({ error: 'Please provide a valid StuDocu URL.' });
|
| 581 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
|
| 583 |
+
const sessionId = Date.now().toString();
|
| 584 |
+
const progressTracker = new ProgressTracker(sessionId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
|
| 586 |
+
progressTrackers.set(sessionId, progressTracker);
|
| 587 |
+
downloadJobs.set(sessionId, { status: 'processing' });
|
|
|
|
| 588 |
|
| 589 |
+
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 590 |
|
| 591 |
+
res.json({ sessionId });
|
|
|
|
| 592 |
|
| 593 |
+
studocuDownloader(url, { email, password }, progressTracker)
|
| 594 |
+
.then(pdfBuffer => {
|
| 595 |
+
downloadJobs.set(sessionId, { status: 'completed', buffer: pdfBuffer });
|
| 596 |
+
progressTrackers.delete(sessionId);
|
| 597 |
+
})
|
| 598 |
+
.catch(error => {
|
| 599 |
+
downloadJobs.set(sessionId, { status: 'error', message: error.message });
|
| 600 |
+
progressTrackers.delete(sessionId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 601 |
});
|
| 602 |
+
});
|
| 603 |
|
| 604 |
+
app.get('/api/progress/:sessionId', (req, res) => {
|
| 605 |
+
const { sessionId } = req.params;
|
| 606 |
+
const tracker = progressTrackers.get(sessionId);
|
| 607 |
+
|
| 608 |
+
if (tracker) {
|
| 609 |
+
return res.json({
|
| 610 |
+
sessionId,
|
| 611 |
+
progress: tracker.progress,
|
| 612 |
+
status: tracker.status,
|
| 613 |
+
message: tracker.message,
|
| 614 |
+
timestamp: new Date().toISOString()
|
| 615 |
+
});
|
| 616 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 617 |
|
| 618 |
+
const job = downloadJobs.get(sessionId);
|
| 619 |
+
if (job) {
|
| 620 |
+
if (job.status === 'completed') {
|
| 621 |
+
return res.json({ sessionId, progress: 100, status: 'completed', message: 'PDF generated successfully!' });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
}
|
| 623 |
+
if (job.status === 'error') {
|
| 624 |
+
return res.json({ sessionId, progress: -1, status: 'error', message: job.message });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
}
|
| 626 |
}
|
|
|
|
| 627 |
|
| 628 |
+
return res.status(404).json({ error: 'Session not found' });
|
| 629 |
+
});
|
| 630 |
|
| 631 |
+
app.get('/api/download/:sessionId', (req, res) => {
|
| 632 |
+
const { sessionId } = req.params;
|
| 633 |
+
const job = downloadJobs.get(sessionId);
|
| 634 |
|
| 635 |
+
if (!job) {
|
| 636 |
+
return res.status(404).json({ error: 'Download session not found or expired.' });
|
|
|
|
|
|
|
|
|
|
| 637 |
}
|
| 638 |
+
|
| 639 |
+
if (job.status === 'processing') {
|
| 640 |
+
return res.status(400).json({ error: 'Download is still processing.' });
|
| 641 |
}
|
| 642 |
|
| 643 |
+
if (job.status === 'error') {
|
| 644 |
+
return res.status(500).json({ error: `Failed to generate PDF: ${job.message}` });
|
|
|
|
| 645 |
}
|
| 646 |
|
| 647 |
+
if (job.status === 'completed' && job.buffer) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 648 |
res.setHeader('Content-Type', 'application/pdf');
|
| 649 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 650 |
+
res.send(job.buffer);
|
| 651 |
+
} else {
|
| 652 |
+
res.status(500).json({ error: 'An unknown error occurred.' });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
}
|
| 654 |
});
|
| 655 |
|
| 656 |
+
// --- Health and Info Endpoints (Unchanged) ---
|
| 657 |
app.get('/health', (req, res) => {
|
| 658 |
res.json({
|
| 659 |
status: 'healthy',
|
| 660 |
timestamp: new Date().toISOString(),
|
| 661 |
+
uptime: process.uptime(),
|
| 662 |
+
activeDownloads: progressTrackers.size
|
| 663 |
});
|
| 664 |
});
|
| 665 |
|
| 666 |
app.get('/', (req, res) => {
|
| 667 |
res.json({
|
| 668 |
+
message: 'π Enhanced StuDocu Downloader API v5.2 - Real-time Progress Tracking with Stealth',
|
| 669 |
+
version: '5.2.0',
|
| 670 |
features: [
|
| 671 |
'πͺ Advanced cookie banner bypass',
|
| 672 |
+
'π Premium content unblurring',
|
| 673 |
+
'π Login support for full access',
|
| 674 |
+
'π Real-time progress tracking via polling',
|
| 675 |
+
'π Clean PDF generation with print styles',
|
| 676 |
+
'π΅οΈ Enhanced stealth to evade bot detection'
|
| 677 |
],
|
| 678 |
endpoints: {
|
| 679 |
+
request: 'POST /api/request-download (body: {url, filename?, email?, password?})',
|
| 680 |
+
progress: 'GET /api/progress/:sessionId',
|
| 681 |
+
download: 'GET /api/download/:sessionId',
|
| 682 |
health: 'GET /health'
|
| 683 |
+
}
|
|
|
|
| 684 |
});
|
| 685 |
});
|
| 686 |
|
|
|
|
| 695 |
});
|
| 696 |
|
| 697 |
app.listen(port, () => {
|
| 698 |
+
console.log(`π Enhanced StuDocu Downloader v5.2.0 running on http://localhost:${port}`);
|
| 699 |
+
console.log(`β¨ Features: Real-time progress tracking, enhanced stealth, and user feedback`);
|
| 700 |
});
|
server.js
CHANGED
|
@@ -306,7 +306,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 306 |
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 307 |
|
| 308 |
console.log("π Launching browser with enhanced stealth configuration...");
|
| 309 |
-
browser = await puppeteerExtra.launch({
|
| 310 |
headless: true,
|
| 311 |
args: [
|
| 312 |
'--no-sandbox',
|
|
@@ -330,26 +330,29 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 330 |
],
|
| 331 |
ignoreHTTPSErrors: true,
|
| 332 |
timeout: 300000,
|
|
|
|
|
|
|
| 333 |
});
|
| 334 |
|
| 335 |
const page = await browser.newPage();
|
| 336 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 338 |
|
| 339 |
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');
|
| 340 |
await page.setViewport({ width: 794, height: 1122 }); // A4 size in pixels at 96 DPI
|
| 341 |
|
| 342 |
-
// NOTE: Stealth plugin handles most of this, but keeping for extra safety
|
| 343 |
await page.evaluateOnNewDocument(() => {
|
| 344 |
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 345 |
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
| 346 |
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
|
| 347 |
});
|
| 348 |
|
| 349 |
-
// Set up cookie and content bypass
|
| 350 |
await bypassCookiesAndRestrictions(page, progressTracker);
|
| 351 |
|
| 352 |
-
// Block unnecessary resources (UPDATED: Always continue for 'document' to prevent navigation failures)
|
| 353 |
await page.setRequestInterception(true);
|
| 354 |
page.on('request', (req) => {
|
| 355 |
const resourceType = req.resourceType();
|
|
@@ -371,7 +374,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 371 |
reqUrl.includes('mixpanel') ||
|
| 372 |
reqUrl.includes('onetrust') ||
|
| 373 |
reqUrl.includes('cookielaw') ||
|
| 374 |
-
(resourceType === 'other' && reqUrl.includes('/track/'))
|
| 375 |
) {
|
| 376 |
req.abort();
|
| 377 |
} else {
|
|
@@ -379,19 +382,18 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 379 |
}
|
| 380 |
});
|
| 381 |
|
| 382 |
-
// Login if credentials provided
|
| 383 |
if (options.email && options.password) {
|
| 384 |
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 385 |
|
| 386 |
console.log("π Logging in to StuDocu...");
|
| 387 |
-
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded'
|
| 388 |
-
await page.waitForSelector('#email'
|
| 389 |
await page.type('#email', options.email);
|
| 390 |
await page.type('#password', options.password);
|
| 391 |
await page.click('button[type="submit"]');
|
| 392 |
try {
|
| 393 |
-
await page.waitForNavigation({ waitUntil: 'networkidle2'
|
| 394 |
-
await page.waitForSelector('.user-profile, [data-testid="user-menu"]'
|
| 395 |
console.log("β
Login successful.");
|
| 396 |
progressTracker?.updateProgress(18, 'authenticated', 'Login successful');
|
| 397 |
} catch (e) {
|
|
@@ -402,8 +404,8 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 402 |
|
| 403 |
progressTracker?.updateProgress(25, 'navigating', 'Navigating to homepage first for session setup...');
|
| 404 |
console.log(`π Navigating to homepage to simulate natural session...`);
|
| 405 |
-
await page.goto('https://www.studocu.com/en-us', { waitUntil: 'domcontentloaded'
|
| 406 |
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
| 407 |
|
| 408 |
progressTracker?.updateProgress(30, 'navigating', 'Navigating to document...');
|
| 409 |
console.log(`π Navigating to ${url}...`);
|
|
@@ -416,22 +418,20 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 416 |
attempts++;
|
| 417 |
progressTracker?.updateProgress(30 + (attempts * 5), 'navigating', `Navigation attempt ${attempts}/${maxAttempts}`);
|
| 418 |
console.log(`Navigation attempt ${attempts}/${maxAttempts}`);
|
| 419 |
-
await page.goto(url, { waitUntil: 'domcontentloaded'
|
| 420 |
navigationSuccess = true;
|
| 421 |
} catch (e) {
|
| 422 |
console.log(`Navigation attempt ${attempts} failed:`, e.message);
|
| 423 |
if (attempts >= maxAttempts) throw e;
|
| 424 |
-
await new Promise(resolve => setTimeout(resolve, 15000));
|
| 425 |
}
|
| 426 |
}
|
| 427 |
|
| 428 |
progressTracker?.updateProgress(40, 'loading', 'Page loaded, waiting for content...');
|
| 429 |
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 430 |
|
| 431 |
-
// Apply content unblurring
|
| 432 |
await unblurContent(page, progressTracker);
|
| 433 |
|
| 434 |
-
// Wait for document content
|
| 435 |
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 436 |
console.log("β³ Waiting for document content to load...");
|
| 437 |
|
|
@@ -442,7 +442,7 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 442 |
let contentFound = false;
|
| 443 |
for (const selector of contentSelectors) {
|
| 444 |
try {
|
| 445 |
-
await page.waitForSelector(selector
|
| 446 |
console.log(`β
Found content with selector: ${selector}`);
|
| 447 |
contentFound = true;
|
| 448 |
break;
|
|
@@ -455,7 +455,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 455 |
console.log("β οΈ No specific content selector found, proceeding with page content...");
|
| 456 |
}
|
| 457 |
|
| 458 |
-
// Enhanced scrolling to load all content
|
| 459 |
progressTracker?.updateProgress(50, 'scrolling', 'Loading all document pages...');
|
| 460 |
console.log("π Loading all document pages with enhanced slow scroll...");
|
| 461 |
|
|
@@ -481,10 +480,8 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 481 |
|
| 482 |
progressTracker?.updateProgress(70, 'processing', 'Processing loaded content...');
|
| 483 |
|
| 484 |
-
// Re-apply unblur after loading new content
|
| 485 |
await unblurContent(page, progressTracker);
|
| 486 |
|
| 487 |
-
// Wait for all images to load
|
| 488 |
progressTracker?.updateProgress(75, 'loading_images', 'Loading images...');
|
| 489 |
console.log("πΌοΈ Waiting for all images to load...");
|
| 490 |
|
|
@@ -503,7 +500,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 503 |
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 504 |
progressTracker?.updateProgress(80, 'finalizing', 'Preparing document for PDF generation...');
|
| 505 |
|
| 506 |
-
// Set exact height
|
| 507 |
await page.evaluate(() => {
|
| 508 |
const getDocumentHeight = () => Math.max(
|
| 509 |
document.body.scrollHeight, document.body.offsetHeight,
|
|
@@ -515,7 +511,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 515 |
document.body.style.overflow = 'hidden !important';
|
| 516 |
});
|
| 517 |
|
| 518 |
-
// Content verification
|
| 519 |
const contentCheck = await page.evaluate(() => {
|
| 520 |
const textContent = document.body.textContent || '';
|
| 521 |
const images = document.querySelectorAll('img');
|
|
@@ -542,7 +537,6 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 542 |
console.warn("β οΈ Warning: Limited document content detected.");
|
| 543 |
}
|
| 544 |
|
| 545 |
-
// Apply print styles and generate PDF
|
| 546 |
await applyPrintStyles(page, progressTracker);
|
| 547 |
await page.emulateMediaType('print');
|
| 548 |
|
|
@@ -551,9 +545,10 @@ const studocuDownloader = async (url, options = {}, progressTracker = null) => {
|
|
| 551 |
|
| 552 |
const pdfBuffer = await page.pdf({
|
| 553 |
printBackground: true,
|
| 554 |
-
preferCSSPageSize: true,
|
| 555 |
displayHeaderFooter: false,
|
| 556 |
-
|
|
|
|
| 557 |
scale: 1,
|
| 558 |
omitBackground: false
|
| 559 |
});
|
|
@@ -593,20 +588,16 @@ app.post('/api/request-download', (req, res) => {
|
|
| 593 |
|
| 594 |
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 595 |
|
| 596 |
-
// Respond to the client immediately with the session ID
|
| 597 |
res.json({ sessionId });
|
| 598 |
|
| 599 |
-
// --- Start the PDF generation in the background ---
|
| 600 |
studocuDownloader(url, { email, password }, progressTracker)
|
| 601 |
.then(pdfBuffer => {
|
| 602 |
-
// Store the successful result
|
| 603 |
downloadJobs.set(sessionId, { status: 'completed', buffer: pdfBuffer });
|
| 604 |
-
progressTrackers.delete(sessionId);
|
| 605 |
})
|
| 606 |
.catch(error => {
|
| 607 |
-
// Store the error
|
| 608 |
downloadJobs.set(sessionId, { status: 'error', message: error.message });
|
| 609 |
-
progressTrackers.delete(sessionId);
|
| 610 |
});
|
| 611 |
});
|
| 612 |
|
|
@@ -615,7 +606,6 @@ app.get('/api/progress/:sessionId', (req, res) => {
|
|
| 615 |
const tracker = progressTrackers.get(sessionId);
|
| 616 |
|
| 617 |
if (tracker) {
|
| 618 |
-
// Job is in progress, return live data
|
| 619 |
return res.json({
|
| 620 |
sessionId,
|
| 621 |
progress: tracker.progress,
|
|
@@ -627,7 +617,6 @@ app.get('/api/progress/:sessionId', (req, res) => {
|
|
| 627 |
|
| 628 |
const job = downloadJobs.get(sessionId);
|
| 629 |
if (job) {
|
| 630 |
-
// Job is finished, return final state
|
| 631 |
if (job.status === 'completed') {
|
| 632 |
return res.json({ sessionId, progress: 100, status: 'completed', message: 'PDF generated successfully!' });
|
| 633 |
}
|
|
@@ -659,8 +648,6 @@ app.get('/api/download/:sessionId', (req, res) => {
|
|
| 659 |
res.setHeader('Content-Type', 'application/pdf');
|
| 660 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 661 |
res.send(job.buffer);
|
| 662 |
-
// Optional: Clean up the job after download to save memory
|
| 663 |
-
// downloadJobs.delete(sessionId);
|
| 664 |
} else {
|
| 665 |
res.status(500).json({ error: 'An unknown error occurred.' });
|
| 666 |
}
|
|
|
|
| 306 |
progressTracker?.updateProgress(0, 'initializing', 'Starting browser...');
|
| 307 |
|
| 308 |
console.log("π Launching browser with enhanced stealth configuration...");
|
| 309 |
+
browser = await puppeteerExtra.launch({
|
| 310 |
headless: true,
|
| 311 |
args: [
|
| 312 |
'--no-sandbox',
|
|
|
|
| 330 |
],
|
| 331 |
ignoreHTTPSErrors: true,
|
| 332 |
timeout: 300000,
|
| 333 |
+
// --- FIX: Increased protocolTimeout for long-running operations ---
|
| 334 |
+
protocolTimeout: 600000 // 10 minutes, increased from the default 30 seconds
|
| 335 |
});
|
| 336 |
|
| 337 |
const page = await browser.newPage();
|
| 338 |
|
| 339 |
+
// --- FIX: Increase default navigation and action timeouts ---
|
| 340 |
+
page.setDefaultNavigationTimeout(180000); // 3 minutes for navigation
|
| 341 |
+
page.setDefaultTimeout(180000); // 3 minutes for other actions (e.g., waitForSelector)
|
| 342 |
+
|
| 343 |
progressTracker?.updateProgress(2, 'initializing', 'Configuring browser settings...');
|
| 344 |
|
| 345 |
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');
|
| 346 |
await page.setViewport({ width: 794, height: 1122 }); // A4 size in pixels at 96 DPI
|
| 347 |
|
|
|
|
| 348 |
await page.evaluateOnNewDocument(() => {
|
| 349 |
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
| 350 |
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
| 351 |
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
|
| 352 |
});
|
| 353 |
|
|
|
|
| 354 |
await bypassCookiesAndRestrictions(page, progressTracker);
|
| 355 |
|
|
|
|
| 356 |
await page.setRequestInterception(true);
|
| 357 |
page.on('request', (req) => {
|
| 358 |
const resourceType = req.resourceType();
|
|
|
|
| 374 |
reqUrl.includes('mixpanel') ||
|
| 375 |
reqUrl.includes('onetrust') ||
|
| 376 |
reqUrl.includes('cookielaw') ||
|
| 377 |
+
(resourceType === 'other' && reqUrl.includes('/track/'))
|
| 378 |
) {
|
| 379 |
req.abort();
|
| 380 |
} else {
|
|
|
|
| 382 |
}
|
| 383 |
});
|
| 384 |
|
|
|
|
| 385 |
if (options.email && options.password) {
|
| 386 |
progressTracker?.updateProgress(12, 'authenticating', 'Logging into StuDocu...');
|
| 387 |
|
| 388 |
console.log("π Logging in to StuDocu...");
|
| 389 |
+
await page.goto('https://www.studocu.com/en-us/login', { waitUntil: 'domcontentloaded' });
|
| 390 |
+
await page.waitForSelector('#email');
|
| 391 |
await page.type('#email', options.email);
|
| 392 |
await page.type('#password', options.password);
|
| 393 |
await page.click('button[type="submit"]');
|
| 394 |
try {
|
| 395 |
+
await page.waitForNavigation({ waitUntil: 'networkidle2' });
|
| 396 |
+
await page.waitForSelector('.user-profile, [data-testid="user-menu"]');
|
| 397 |
console.log("β
Login successful.");
|
| 398 |
progressTracker?.updateProgress(18, 'authenticated', 'Login successful');
|
| 399 |
} catch (e) {
|
|
|
|
| 404 |
|
| 405 |
progressTracker?.updateProgress(25, 'navigating', 'Navigating to homepage first for session setup...');
|
| 406 |
console.log(`π Navigating to homepage to simulate natural session...`);
|
| 407 |
+
await page.goto('https://www.studocu.com/en-us', { waitUntil: 'domcontentloaded' });
|
| 408 |
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
| 409 |
|
| 410 |
progressTracker?.updateProgress(30, 'navigating', 'Navigating to document...');
|
| 411 |
console.log(`π Navigating to ${url}...`);
|
|
|
|
| 418 |
attempts++;
|
| 419 |
progressTracker?.updateProgress(30 + (attempts * 5), 'navigating', `Navigation attempt ${attempts}/${maxAttempts}`);
|
| 420 |
console.log(`Navigation attempt ${attempts}/${maxAttempts}`);
|
| 421 |
+
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
| 422 |
navigationSuccess = true;
|
| 423 |
} catch (e) {
|
| 424 |
console.log(`Navigation attempt ${attempts} failed:`, e.message);
|
| 425 |
if (attempts >= maxAttempts) throw e;
|
| 426 |
+
await new Promise(resolve => setTimeout(resolve, 15000));
|
| 427 |
}
|
| 428 |
}
|
| 429 |
|
| 430 |
progressTracker?.updateProgress(40, 'loading', 'Page loaded, waiting for content...');
|
| 431 |
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 432 |
|
|
|
|
| 433 |
await unblurContent(page, progressTracker);
|
| 434 |
|
|
|
|
| 435 |
progressTracker?.updateProgress(45, 'loading', 'Waiting for document content...');
|
| 436 |
console.log("β³ Waiting for document content to load...");
|
| 437 |
|
|
|
|
| 442 |
let contentFound = false;
|
| 443 |
for (const selector of contentSelectors) {
|
| 444 |
try {
|
| 445 |
+
await page.waitForSelector(selector);
|
| 446 |
console.log(`β
Found content with selector: ${selector}`);
|
| 447 |
contentFound = true;
|
| 448 |
break;
|
|
|
|
| 455 |
console.log("β οΈ No specific content selector found, proceeding with page content...");
|
| 456 |
}
|
| 457 |
|
|
|
|
| 458 |
progressTracker?.updateProgress(50, 'scrolling', 'Loading all document pages...');
|
| 459 |
console.log("π Loading all document pages with enhanced slow scroll...");
|
| 460 |
|
|
|
|
| 480 |
|
| 481 |
progressTracker?.updateProgress(70, 'processing', 'Processing loaded content...');
|
| 482 |
|
|
|
|
| 483 |
await unblurContent(page, progressTracker);
|
| 484 |
|
|
|
|
| 485 |
progressTracker?.updateProgress(75, 'loading_images', 'Loading images...');
|
| 486 |
console.log("πΌοΈ Waiting for all images to load...");
|
| 487 |
|
|
|
|
| 500 |
await new Promise(resolve => setTimeout(resolve, 5000));
|
| 501 |
progressTracker?.updateProgress(80, 'finalizing', 'Preparing document for PDF generation...');
|
| 502 |
|
|
|
|
| 503 |
await page.evaluate(() => {
|
| 504 |
const getDocumentHeight = () => Math.max(
|
| 505 |
document.body.scrollHeight, document.body.offsetHeight,
|
|
|
|
| 511 |
document.body.style.overflow = 'hidden !important';
|
| 512 |
});
|
| 513 |
|
|
|
|
| 514 |
const contentCheck = await page.evaluate(() => {
|
| 515 |
const textContent = document.body.textContent || '';
|
| 516 |
const images = document.querySelectorAll('img');
|
|
|
|
| 537 |
console.warn("β οΈ Warning: Limited document content detected.");
|
| 538 |
}
|
| 539 |
|
|
|
|
| 540 |
await applyPrintStyles(page, progressTracker);
|
| 541 |
await page.emulateMediaType('print');
|
| 542 |
|
|
|
|
| 545 |
|
| 546 |
const pdfBuffer = await page.pdf({
|
| 547 |
printBackground: true,
|
| 548 |
+
preferCSSPageSize: true,
|
| 549 |
displayHeaderFooter: false,
|
| 550 |
+
// --- FIX: Increased PDF generation timeout ---
|
| 551 |
+
timeout: 600000, // 10 minutes, increased from the default 30 seconds
|
| 552 |
scale: 1,
|
| 553 |
omitBackground: false
|
| 554 |
});
|
|
|
|
| 588 |
|
| 589 |
console.log(`π― Processing request for: ${url} [Session: ${sessionId}]`);
|
| 590 |
|
|
|
|
| 591 |
res.json({ sessionId });
|
| 592 |
|
|
|
|
| 593 |
studocuDownloader(url, { email, password }, progressTracker)
|
| 594 |
.then(pdfBuffer => {
|
|
|
|
| 595 |
downloadJobs.set(sessionId, { status: 'completed', buffer: pdfBuffer });
|
| 596 |
+
progressTrackers.delete(sessionId);
|
| 597 |
})
|
| 598 |
.catch(error => {
|
|
|
|
| 599 |
downloadJobs.set(sessionId, { status: 'error', message: error.message });
|
| 600 |
+
progressTrackers.delete(sessionId);
|
| 601 |
});
|
| 602 |
});
|
| 603 |
|
|
|
|
| 606 |
const tracker = progressTrackers.get(sessionId);
|
| 607 |
|
| 608 |
if (tracker) {
|
|
|
|
| 609 |
return res.json({
|
| 610 |
sessionId,
|
| 611 |
progress: tracker.progress,
|
|
|
|
| 617 |
|
| 618 |
const job = downloadJobs.get(sessionId);
|
| 619 |
if (job) {
|
|
|
|
| 620 |
if (job.status === 'completed') {
|
| 621 |
return res.json({ sessionId, progress: 100, status: 'completed', message: 'PDF generated successfully!' });
|
| 622 |
}
|
|
|
|
| 648 |
res.setHeader('Content-Type', 'application/pdf');
|
| 649 |
res.setHeader('Content-Disposition', 'attachment; filename=studocu-document.pdf');
|
| 650 |
res.send(job.buffer);
|
|
|
|
|
|
|
| 651 |
} else {
|
| 652 |
res.status(500).json({ error: 'An unknown error occurred.' });
|
| 653 |
}
|