feat: finist chromium download douyin media
This commit is contained in:
parent
38dc501f4f
commit
075ed9296d
74
bun.lock
74
bun.lock
@ -4,8 +4,12 @@
|
||||
"": {
|
||||
"name": "qq-bot",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/multer": "^2.0.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.0.0",
|
||||
"express": "^5.1.0",
|
||||
"multer": "^2.0.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
@ -24,6 +28,8 @@
|
||||
|
||||
"@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
|
||||
|
||||
"@types/cors": ["@types/cors@2.8.19", "https://registry.npmmirror.com/@types/cors/-/cors-2.8.19.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="],
|
||||
|
||||
"@types/express": ["@types/express@5.0.3", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "*" } }, "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw=="],
|
||||
|
||||
"@types/express-serve-static-core": ["@types/express-serve-static-core@5.0.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA=="],
|
||||
@ -32,7 +38,9 @@
|
||||
|
||||
"@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="],
|
||||
|
||||
"@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
"@types/multer": ["@types/multer@2.0.0", "https://registry.npmmirror.com/@types/multer/-/multer-2.0.0.tgz", { "dependencies": { "@types/express": "*" } }, "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw=="],
|
||||
|
||||
"@types/node": ["@types/node@24.3.0", "https://registry.npmmirror.com/@types/node/-/node-24.3.0.tgz", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
|
||||
|
||||
"@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="],
|
||||
|
||||
@ -44,16 +52,24 @@
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"append-field": ["append-field@1.0.0", "https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
||||
|
||||
"busboy": ["busboy@1.6.0", "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"concat-stream": ["concat-stream@2.0.0", "https://registry.npmmirror.com/concat-stream/-/concat-stream-2.0.0.tgz", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="],
|
||||
|
||||
"content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="],
|
||||
|
||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||
@ -62,6 +78,8 @@
|
||||
|
||||
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||
|
||||
"cors": ["cors@2.8.5", "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
@ -126,10 +144,18 @@
|
||||
|
||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"mkdirp": ["mkdirp@0.5.6", "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"multer": ["multer@2.0.2", "https://registry.npmmirror.com/multer/-/multer-2.0.2.tgz", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "mkdirp": "^0.5.6", "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" } }, "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
@ -152,6 +178,8 @@
|
||||
|
||||
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
@ -174,20 +202,62 @@
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"streamsearch": ["streamsearch@1.1.0", "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||
|
||||
"typedarray": ["typedarray@0.0.6", "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
"undici-types": ["undici-types@7.10.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.10.0.tgz", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
||||
"@types/body-parser/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
|
||||
"@types/connect/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
|
||||
"@types/express-serve-static-core/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
|
||||
"@types/send/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
|
||||
"@types/serve-static/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
|
||||
"bun-types/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
|
||||
|
||||
"http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||
|
||||
"multer/type-is": ["type-is@1.6.18", "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
|
||||
|
||||
"@types/body-parser/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"@types/connect/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"@types/express-serve-static-core/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"@types/send/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"@types/serve-static/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"bun-types/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"multer/type-is/media-typer": ["media-typer@0.3.0", "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
|
||||
|
||||
"multer/type-is/mime-types": ["mime-types@2.1.35", "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"multer/type-is/mime-types/mime-db": ["mime-db@1.52.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +1,83 @@
|
||||
// node run.js https://example.com/dashboard
|
||||
import path from 'path';
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const userDataDir = './profiles/site1';
|
||||
const targetUrl = process.argv[2] || 'https://baidu.com/';
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
||||
|
||||
const context = await chromium.launchPersistentContext(userDataDir, {
|
||||
headless: true, // 无头
|
||||
});
|
||||
await context.addInitScript({ path: './userscripts/my-script.js' });
|
||||
export async function downloadDouyinMedia(targetUrl: string, target_id?: string) {
|
||||
const USER_DATA_DIR = path.join(__dirname, 'profiles/site1');
|
||||
const TARGET_URL = targetUrl;
|
||||
const HEADLESS = true;
|
||||
const TIMEOUT = 10000;
|
||||
|
||||
const page = context.pages()[0] || await context.newPage();
|
||||
await page.goto(targetUrl, { waitUntil: 'networkidle' });
|
||||
const context = await chromium.launchPersistentContext(USER_DATA_DIR, {
|
||||
headless: HEADLESS,
|
||||
});
|
||||
|
||||
// === 你的自动化逻辑(示例)===
|
||||
await page.click('text=开始任务', { timeout: 10_000 }).catch(()=>{});
|
||||
await page.waitForTimeout(1000);
|
||||
try {
|
||||
const page = context.pages()[0] || (await context.newPage());
|
||||
await page.goto(TARGET_URL, { waitUntil: 'load' });
|
||||
|
||||
// @ts-ignore
|
||||
console.log('当前用户:', await page.evaluate(() => document.title));
|
||||
await page.waitForSelector('.xgplayer', { timeout: TIMEOUT }).catch(() => {
|
||||
});
|
||||
|
||||
// 示例:截图/导出结果
|
||||
await page.screenshot({ path: './out.png', fullPage: true });
|
||||
if (target_id) {
|
||||
await page.evaluate(`window.f_down_target_id = "${target_id}"; window.f_down_server_port = ${process.env.PORT || 6100}`)
|
||||
}
|
||||
|
||||
await context.close();
|
||||
console.log('Done.');
|
||||
await page.evaluate(async () => {
|
||||
const d = (globalThis as any).document;
|
||||
const vEle = d.querySelector('.xgplayer video source')
|
||||
const isVideo = !!vEle && !vEle.src.includes('.mp3');
|
||||
|
||||
const urls: string[] = isVideo ? [d.querySelector(".xgplayer video source").src] : [
|
||||
...new Set(
|
||||
Array.from(d.querySelectorAll('.xgplayer img'))
|
||||
// @ts-ignore
|
||||
.map(img => img?.src)
|
||||
.filter(Boolean)
|
||||
),
|
||||
];
|
||||
|
||||
async function urlToBlob(url: string) {
|
||||
const res = await fetch(url, { credentials: "omit" });
|
||||
if (!res.ok) throw new Error(`fetch image failed: ${res.status}`);
|
||||
return await res.blob(); // 保持原 MIME
|
||||
}
|
||||
|
||||
const fd = new FormData();
|
||||
// 附带标题作为一个普通字段
|
||||
fd.append("title", d.title || "");
|
||||
|
||||
let idx = 1;
|
||||
for (const src of urls) {
|
||||
const blob = await urlToBlob(src); // ✅ 直接用字符串
|
||||
const ext = (blob.type && blob.type.split("/")[1]) || "bin";
|
||||
// @ts-ignore
|
||||
fd.append(`file_${idx}`, blob, `media_${idx}.${ext}`);
|
||||
idx++;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const url = new URL(`http://127.0.0.1:${window.f_down_server_port}/upload`);
|
||||
url.searchParams.set("title", d.title || "");
|
||||
url.searchParams.set("type", isVideo ? "video" : "image");
|
||||
// @ts-ignore
|
||||
url.searchParams.set("target_id", window.f_down_target_id || "");
|
||||
|
||||
const r = await fetch(url.toString(), {
|
||||
method: "POST",
|
||||
body: fd,
|
||||
});
|
||||
|
||||
// 为了在 evaluate 内确保完成整个请求-响应流程,读取下响应
|
||||
const json = await r.json().catch(() => ({}));
|
||||
|
||||
return json;
|
||||
});
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
} finally {
|
||||
await context.close();
|
||||
console.log('Done.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,21 +5,17 @@ const userDataDir = './profiles/site1';
|
||||
const startUrl = process.argv[2] || 'https://douyin.com';
|
||||
|
||||
const context = await chromium.launchPersistentContext(userDataDir, {
|
||||
headless: false, // 首次建议“有头”,方便手工/2FA
|
||||
headless: false,
|
||||
args: ['--disable-blink-features=AutomationControlled'],
|
||||
});
|
||||
await context.addInitScript({ path: './userscripts/my-script.js' });
|
||||
|
||||
const page = context.pages()[0] || await context.newPage();
|
||||
await page.goto(startUrl, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// 在这一步完成你的登录(手输或自动化都可)
|
||||
console.log('>>> 请在打开的浏览器里完成登录,完成后回到终端按回车...');
|
||||
process.stdin.resume();
|
||||
await new Promise(r => process.stdin.once('data', r));
|
||||
|
||||
// 可选:导出 storageState 做备份(即使不用它,持久化目录也保存了)
|
||||
await context.storageState({ path: `${userDataDir}/storageState.json` });
|
||||
await context.close();
|
||||
|
||||
console.log('>>> 登录态已写入 profiles/site1/,后续可无头复用。');
|
||||
|
||||
177
index.ts
177
index.ts
@ -2,48 +2,20 @@ import express from 'express';
|
||||
import type { Request, Response, NextFunction } from 'express';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import cors from "cors";
|
||||
import multer from "multer";
|
||||
import * as douyin from './douyin'
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 6100;
|
||||
|
||||
// 中间件:解析 JSON 请求体
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
||||
|
||||
// 中间件:解析 URL 编码的请求体
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
app.use(cors());
|
||||
|
||||
// 中间件:解析原始请求体
|
||||
app.use(express.raw({ type: '*/*', limit: '10mb' }));
|
||||
|
||||
// 自定义中间件:打印所有请求的详细信息
|
||||
/* app.use((req: Request, res: Response, next: NextFunction) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log(`[${timestamp}] 收到请求:`);
|
||||
console.log(`方法: ${req.method}`);
|
||||
console.log(`路径: ${req.path}`);
|
||||
console.log(`完整URL: ${req.originalUrl}`);
|
||||
console.log(`查询参数:`, req.query);
|
||||
console.log(`请求头:`, req.headers);
|
||||
|
||||
// 打印请求体(如果存在)
|
||||
if (req.body) {
|
||||
if (Buffer.isBuffer(req.body)) {
|
||||
console.log(`请求体 (原始):`, req.body.toString());
|
||||
} else if (typeof req.body === 'object' && Object.keys(req.body).length > 0) {
|
||||
console.log(`请求体 (JSON):`, req.body);
|
||||
} else if (req.body) {
|
||||
console.log(`请求体:`, req.body);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`客户端IP: ${req.ip}`);
|
||||
console.log(`User-Agent: ${req.get('User-Agent')}`);
|
||||
console.log('='.repeat(80));
|
||||
|
||||
next();
|
||||
}); */
|
||||
// 仅解析明确类型;不要用 '*/*'
|
||||
app.use(express.json({ limit: '10mb', type: ['application/json'] }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb', type: ['application/x-www-form-urlencoded'] }));
|
||||
|
||||
// QQ机器人回显功能
|
||||
app.post('/', async (req: Request, res: Response) => {
|
||||
@ -59,77 +31,84 @@ app.post('/', async (req: Request, res: Response) => {
|
||||
// Like: https://v.douyin.com/YqgJL_phY_k/
|
||||
const douyinUrlPattern = /https?:\/\/v\.douyin\.com\/[a-zA-Z0-9_-]+/;
|
||||
const douyinMatch = raw_message.match(douyinUrlPattern);
|
||||
|
||||
|
||||
try {
|
||||
// 如果检测到抖音链接,调用解析API
|
||||
if (douyinMatch) {
|
||||
const douyinUrl = douyinMatch[0];
|
||||
console.log(`[抖音链接检测] 发现抖音链接: ${douyinUrl}`);
|
||||
sendMsg(`[抖音链接检测] 发现抖音链接: ${douyinUrl},启动 Chromium 中...`, target_id);
|
||||
|
||||
// 调用抖音视频解析API
|
||||
const apiUrl = `http://localhost:6101/api/hybrid/video_data?url=${encodeURIComponent(douyinUrl)}`;
|
||||
console.log(`[抖音解析] 调用API: ${apiUrl}`);
|
||||
|
||||
const apiResponse = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (!apiResponse.ok)
|
||||
throw new Error(`抖音API调用失败: ${apiResponse.status} ${apiResponse.statusText}`);
|
||||
|
||||
let vD: any = await apiResponse.json();
|
||||
|
||||
if (!vD.data)
|
||||
throw new Error('抖音API返回数据格式错误');
|
||||
|
||||
vD = vD.data;
|
||||
|
||||
// 发送视频信息
|
||||
sendMsg(`[抖音下载] ${vD.author.nickname}: ${vD.caption}\n`, target_id);
|
||||
|
||||
// 下载视频
|
||||
const downloadUrl = `http://localhost:6101/api/download?url=${encodeURIComponent(douyinUrl)}&prefix=true&with_watermark=false`;
|
||||
console.log(`[抖音下载] 调用下载API: ${downloadUrl}`);
|
||||
|
||||
const downloadResponse = await fetch(downloadUrl);
|
||||
if (!downloadResponse.ok) {
|
||||
throw new Error(`视频下载失败: ${downloadResponse.status} ${downloadResponse.statusText}`);
|
||||
}
|
||||
|
||||
// 创建下载目录
|
||||
const downloadDir = process.env.DOWNLOAD_DIR || path.join(__dirname, 'downloads');
|
||||
if (!fs.existsSync(downloadDir)) {
|
||||
fs.mkdirSync(downloadDir, { recursive: true });
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
const timestamp = Date.now();
|
||||
// 清理文件名中的非法字符
|
||||
const cleanCaption = vD.caption.replace(/[<>:"/\\|?*]/g, '_').substring(0, 50);
|
||||
const fileName = `${cleanCaption}_${timestamp}.mp4`;
|
||||
const filePath = path.join(downloadDir, fileName);
|
||||
|
||||
// 保存视频文件
|
||||
console.log(`[抖音下载] 保存视频到: ${filePath}`);
|
||||
const buffer = await downloadResponse.arrayBuffer();
|
||||
fs.writeFileSync(filePath, Buffer.from(buffer));
|
||||
|
||||
// 发送视频消息
|
||||
console.log(`[抖音发送] 发送视频文件`);
|
||||
await sendVideoMsg(filePath, target_id);
|
||||
|
||||
douyin.downloadDouyinMedia(douyinUrl, target_id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[错误] 处理消息时发生错误:`, error);
|
||||
|
||||
|
||||
sendMsg(String(error), target_id);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function sanitizeDirname(input: string) {
|
||||
// 基础清洗:去除路径分隔符、控制字符,并截断长度(防止奇怪标题)
|
||||
const s = (input || "default")
|
||||
.trim()
|
||||
.replace(/[\\/]+/g, "_")
|
||||
.replace(/[<>:"|?*\u0000-\u001F]/g, "_")
|
||||
.slice(0, 128) || "default";
|
||||
return s;
|
||||
}
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, _file, cb) => {
|
||||
const title = sanitizeDirname(req.query.title as string);
|
||||
const baseDir = process.env.DOWNLOAD_DIR || path.join(__dirname, "downloads");
|
||||
const dest = req.query.type == 'image' ? path.join(baseDir, title) : baseDir;
|
||||
|
||||
fs.mkdir(dest, { recursive: true }, (err) => cb(err, dest));
|
||||
}, filename: (req, file, cb) => {
|
||||
const ext = path.extname(file.originalname || "");
|
||||
const title = sanitizeDirname(req.query.title as string);
|
||||
|
||||
cb(null, `${title}_${Date.now()}_${Math.random().toString(36).slice(2)}${ext}`);
|
||||
},
|
||||
});
|
||||
const upload = multer({
|
||||
storage,
|
||||
// 限制:单文件最大 1GB、最多 50 个字段(可按需调整)
|
||||
limits: { fileSize: 1024 * 1024 * 1024, fields: 50, files: 50 },
|
||||
fileFilter: (_req, file, cb) => {
|
||||
// 只接受字段名以 file_ 开头的文件,其他拒绝
|
||||
if (/^file_\d+$/i.test(file.fieldname)) cb(null, true);
|
||||
else cb(new Error(`Unexpected file field: ${file.fieldname}`));
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/upload", upload.any(), (req, res) => {
|
||||
// 1) 取出文件:multer.any() 把所有文件放在 req.files
|
||||
const files = (req.files as Express.Multer.File[]) || [];
|
||||
// 只保留我们关心的 file_*
|
||||
const accepted = files.filter(f => /^file_\d+$/i.test(f.fieldname));
|
||||
|
||||
let meta = {
|
||||
title: req.body.title,
|
||||
type: req.query.type as 'video' | 'image',
|
||||
target_id: req.query.target_id
|
||||
};
|
||||
|
||||
console.log(meta);
|
||||
console.log(`收到上传: ${accepted.length} 个文件`, files.map(f => f.path));
|
||||
|
||||
if (meta.target_id) {
|
||||
const totalSize = accepted.reduce((sum, f) => sum + f.size, 0);
|
||||
sendMsg(`[抖音下载] ${meta.title},已下载 ${accepted.length} 个文件,类型 ${meta.type},共 ${(totalSize/1024/1024).toFixed(2)} MB,上传中...`, meta.target_id as string);
|
||||
accepted.forEach(f => {
|
||||
sendMediaMsg(f.path, meta.target_id as string, meta.type);
|
||||
})
|
||||
}
|
||||
res.json({ ok: true, files: accepted.length, meta });
|
||||
});
|
||||
|
||||
function sendMsg(msg: string, target_id: string) {
|
||||
const replyMessage = {
|
||||
user_id: String(target_id),
|
||||
@ -145,6 +124,8 @@ function sendMsg(msg: string, target_id: string) {
|
||||
|
||||
const replyUrl = `http://localhost:30000/send_private_msg`;
|
||||
|
||||
console.log(`[发送消息] ${msg} -> ${target_id}`);
|
||||
|
||||
return fetch(replyUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -154,12 +135,12 @@ function sendMsg(msg: string, target_id: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function sendVideoMsg(filePath: string, target_id: string) {
|
||||
const videoMessage = {
|
||||
function sendMediaMsg(filePath: string, target_id: string, type: 'video' | 'image') {
|
||||
const mediaMessage = {
|
||||
user_id: String(target_id),
|
||||
message: [
|
||||
{
|
||||
type: "video",
|
||||
type: type,
|
||||
data: {
|
||||
file: `file://${filePath}`
|
||||
}
|
||||
@ -169,12 +150,14 @@ function sendVideoMsg(filePath: string, target_id: string) {
|
||||
|
||||
const replyUrl = `http://localhost:30000/send_private_msg`;
|
||||
|
||||
console.log(`[发送媒体消息] ${type} - ${filePath} -> ${target_id}`);
|
||||
|
||||
return fetch(replyUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(videoMessage)
|
||||
body: JSON.stringify(mediaMessage)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,11 @@
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/multer": "^2.0.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.0.0",
|
||||
"express": "^5.1.0"
|
||||
"express": "^5.1.0",
|
||||
"multer": "^2.0.2"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user