initial commit
This commit is contained in:
commit
69da9f42d4
25 files changed
+659
No files matched your search
+34
@@ -0,0 +1,34 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,45 @@
|
||||
# replay_downloader
|
||||
|
||||
A Tool to mass download EZPPFarm replays from specific leaderboards
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [Bun](https://bun.sh)
|
||||
|
||||
## To install bun:
|
||||
|
||||
### Windows:
|
||||
|
||||
```bash
|
||||
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||
```
|
||||
|
||||
### Linux & macOS:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## To debug:
|
||||
|
||||
```bash
|
||||
bun run debug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## To build:
|
||||
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "replay_downloader",
|
||||
"dependencies": {
|
||||
"replay_downloader": ".",
|
||||
"@better-fetch/fetch": "^1.1.18",
|
||||
"chalk": "^5.4.1",
|
||||
"inquirer": "^12.9.0",
|
||||
"ky": "^1.8.2",
|
||||
"ora": "^8.2.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/chalk": "^2.2.4",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@better-fetch/fetch": ["@better-fetch/[email protected]", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
|
||||
|
||||
"@inquirer/checkbox": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA=="],
|
||||
|
||||
"@inquirer/confirm": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q=="],
|
||||
|
||||
"@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA=="],
|
||||
|
||||
"@inquirer/editor": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "external-editor": "^3.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ=="],
|
||||
|
||||
"@inquirer/expand": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw=="],
|
||||
|
||||
"@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw=="],
|
||||
|
||||
"@inquirer/input": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow=="],
|
||||
|
||||
"@inquirer/number": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg=="],
|
||||
|
||||
"@inquirer/password": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA=="],
|
||||
|
||||
"@inquirer/prompts": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/checkbox": "^4.2.0", "@inquirer/confirm": "^5.1.14", "@inquirer/editor": "^4.2.15", "@inquirer/expand": "^4.0.17", "@inquirer/input": "^4.2.1", "@inquirer/number": "^3.0.17", "@inquirer/password": "^4.0.17", "@inquirer/rawlist": "^4.1.5", "@inquirer/search": "^3.1.0", "@inquirer/select": "^4.3.1" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw=="],
|
||||
|
||||
"@inquirer/rawlist": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA=="],
|
||||
|
||||
"@inquirer/search": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q=="],
|
||||
|
||||
"@inquirer/select": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA=="],
|
||||
|
||||
"@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw=="],
|
||||
|
||||
"@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@types/chalk": ["@types/[email protected]", "", { "dependencies": { "chalk": "*" } }, "sha512-pb/QoGqtCpH2famSp72qEsXkNzcErlVmiXlQ/ww+5AddD8TmmYS7EWg5T20YiNCAiTgs8pMf2G8SJG5h/ER1ZQ=="],
|
||||
|
||||
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
||||
|
||||
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="],
|
||||
|
||||
"ansi-escapes": ["[email protected]", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||
|
||||
"ansi-regex": ["[email protected]", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"chalk": ["[email protected]", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||
|
||||
"chardet": ["[email protected]", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="],
|
||||
|
||||
"cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
|
||||
|
||||
"cli-spinners": ["[email protected]", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
||||
|
||||
"cli-width": ["[email protected]", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="],
|
||||
|
||||
"color-convert": ["[email protected]", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["[email protected]", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"emoji-regex": ["[email protected]", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
||||
|
||||
"external-editor": ["[email protected]", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="],
|
||||
|
||||
"get-east-asian-width": ["[email protected]", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="],
|
||||
|
||||
"iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"inquirer": ["[email protected]", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/prompts": "^7.8.0", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "mute-stream": "^2.0.0", "run-async": "^4.0.5", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-LlFVmvWVCun7uEgPB3vups9NzBrjJn48kRNtFGw3xU1H5UXExTEz/oF1JGLaB0fvlkUB+W6JfgLcSEaSdH7RPA=="],
|
||||
|
||||
"is-fullwidth-code-point": ["[email protected]", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-interactive": ["[email protected]", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="],
|
||||
|
||||
"is-unicode-supported": ["[email protected]", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
|
||||
|
||||
"ky": ["[email protected]", "", {}, "sha512-XybQJ3d4Ea1kI27DoelE5ZCT3bSJlibYTtQuMsyzKox3TMyayw1asgQdl54WroAm+fIA3ZCr8zXW2RpR7qWVpA=="],
|
||||
|
||||
"log-symbols": ["[email protected]", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="],
|
||||
|
||||
"mimic-function": ["[email protected]", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||
|
||||
"mute-stream": ["[email protected]", "", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="],
|
||||
|
||||
"onetime": ["[email protected]", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
|
||||
|
||||
"ora": ["[email protected]", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="],
|
||||
|
||||
"os-tmpdir": ["[email protected]", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="],
|
||||
|
||||
"replay_downloader": ["replay_downloader@file:", { "dependencies": { "@better-fetch/fetch": "^1.1.18", "chalk": "^5.4.1", "inquirer": "^12.9.0", "ky": "^1.8.2", "ora": "^8.2.0" }, "devDependencies": { "@types/bun": "latest", "@types/chalk": "^2.2.4" }, "peerDependencies": { "typescript": "^5" } }],
|
||||
|
||||
"restore-cursor": ["[email protected]", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
||||
|
||||
"run-async": ["[email protected]", "", {}, "sha512-oN9GTgxUNDBumHTTDmQ8dep6VIJbgj9S3dPP+9XylVLIK4xB9XTXtKWROd5pnhdXR9k0EgO1JRcNh0T+Ny2FsA=="],
|
||||
|
||||
"rxjs": ["[email protected]", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
||||
|
||||
"safer-buffer": ["[email protected]", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"stdin-discarder": ["[email protected]", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
|
||||
|
||||
"string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"tmp": ["[email protected]", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
|
||||
|
||||
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"type-fest": ["[email protected]", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||
|
||||
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||
|
||||
"undici-types": ["[email protected]", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
||||
|
||||
"yoctocolors-cjs": ["[email protected]", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
||||
|
||||
"log-symbols/is-unicode-supported": ["[email protected]", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
|
||||
|
||||
"wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "replay_downloader",
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"debug": "bun --watch src/index.ts",
|
||||
"build": "bun run build:win && bun run build:linux && bun run build:mac",
|
||||
"build:win": "bun build ./src/index.ts --compile --target=bun-windows-x64-modern --outfile ./target/replay_downloader-windows-x64",
|
||||
"build:linux": "bun build ./src/index.ts --compile --target=bun-linux-x64-modern --outfile ./target/replay_downloader-linux-x64",
|
||||
"build:mac": "bun build ./src/index.ts --compile --target=bun-darwin-x64 --outfile ./target/replay_downloader-darwin-x64"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/chalk": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@better-fetch/fetch": "^1.1.18",
|
||||
"chalk": "^5.4.1",
|
||||
"inquirer": "^12.9.0",
|
||||
"ky": "^1.8.2",
|
||||
"ora": "^8.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type Method, createFetch } from '@better-fetch/fetch';
|
||||
import type { HeadersInit } from 'bun';
|
||||
|
||||
const apiClient = createFetch({
|
||||
baseURL: 'https://api.ez-pp.farm/',
|
||||
throw: true,
|
||||
});
|
||||
|
||||
export type ApiResponse<T> = { data: T; error: undefined } | { data: undefined; error: Error };
|
||||
|
||||
export const fetchApi = async <T>(
|
||||
route: string,
|
||||
options?: {
|
||||
query?: Record<string, unknown>;
|
||||
headers?: HeadersInit;
|
||||
method?: Method;
|
||||
params?: Record<string, unknown>;
|
||||
timeout?: number;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
): Promise<ApiResponse<T>> => {
|
||||
try {
|
||||
const data = await apiClient<T>(route, {
|
||||
query: options?.query,
|
||||
headers: options?.headers ?? {
|
||||
accept: 'application/json',
|
||||
},
|
||||
method: options?.method ?? 'GET',
|
||||
params: options?.params,
|
||||
timeout: options?.timeout,
|
||||
signal: options?.signal,
|
||||
});
|
||||
return { data, error: undefined };
|
||||
} catch (err) {
|
||||
return { data: undefined, error: err as Error };
|
||||
}
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
import inquirer from "inquirer";
|
||||
import { fetchApi } from "./api/ezpp";
|
||||
import ora from "ora";
|
||||
import {
|
||||
getGamemodeName,
|
||||
getModeAndTypeFromGamemode,
|
||||
modeIntToStr,
|
||||
typeIntToStr,
|
||||
validModeTypeCombinationsSorted,
|
||||
} from "./util/mode";
|
||||
import type { MapScores } from "./interface/MapScores";
|
||||
import * as path from "node:path";
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import ky from "ky";
|
||||
import { mkdir } from "node:fs/promises";
|
||||
import { existsSync } from "node:fs";
|
||||
import chalk from "chalk";
|
||||
|
||||
(async () => {
|
||||
const modeChoices = validModeTypeCombinationsSorted.map((gamemode) => {
|
||||
const gmode = getModeAndTypeFromGamemode(gamemode);
|
||||
return {
|
||||
value: gamemode,
|
||||
name: getGamemodeName(modeIntToStr(gmode.mode), typeIntToStr(gmode.type)),
|
||||
};
|
||||
});
|
||||
|
||||
const answers: { beatmapId: number; mode: number } = await inquirer.prompt([
|
||||
{
|
||||
message: "Please enter a beatmap ID",
|
||||
name: "beatmapId",
|
||||
type: "number",
|
||||
min: 0,
|
||||
required: true,
|
||||
async validate(value) {
|
||||
const { error } = await fetchApi("v1/get_map_info", {
|
||||
query: {
|
||||
id: value,
|
||||
},
|
||||
});
|
||||
if (error) return "Beatmap not found!";
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
name: "mode",
|
||||
message: "Please select a mode",
|
||||
choices: modeChoices,
|
||||
loop: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const leaderboardLoadingSpinner = ora("Loading scores...").start();
|
||||
|
||||
const { error, data } = await fetchApi<MapScores>("v1/get_map_scores", {
|
||||
query: {
|
||||
id: answers.beatmapId,
|
||||
mode: answers.mode,
|
||||
scope: "best",
|
||||
limit: 50,
|
||||
},
|
||||
});
|
||||
|
||||
if (error || !data.scores || data.scores.length <= 0) {
|
||||
leaderboardLoadingSpinner.fail("Failed to get map scores.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
leaderboardLoadingSpinner.succeed("Scores loaded!");
|
||||
|
||||
const currentFolder = path.join(process.cwd());
|
||||
const downloadFolder = path.join(currentFolder, answers.beatmapId.toFixed());
|
||||
if (!existsSync(downloadFolder)) await mkdir(downloadFolder);
|
||||
|
||||
let downloadedReplaysCount = 0;
|
||||
|
||||
for (const score of data.scores) {
|
||||
const downloadSpinner = ora(
|
||||
`downloading replay ${score.id} (0%)...`
|
||||
).start();
|
||||
try {
|
||||
const replayDownload = await ky(
|
||||
`https://api.ez-pp.farm/v1/get_replay?id=${score.id}&include_headers=true`,
|
||||
{
|
||||
onDownloadProgress(progress) {
|
||||
downloadSpinner.text = `downloading replay ${score.id} (${(progress.percent * 100).toFixed()}%)...`;
|
||||
},
|
||||
}
|
||||
);
|
||||
const replayData = await replayDownload.arrayBuffer();
|
||||
downloadSpinner.text = `Saving replay ${score.id}...`;
|
||||
await Bun.write(
|
||||
path.join(downloadFolder, `${score.id}.osr`),
|
||||
Buffer.from(replayData)
|
||||
);
|
||||
downloadedReplaysCount += 1;
|
||||
} catch {
|
||||
downloadSpinner.fail(`Failed to download replay ${score.id}.`);
|
||||
}
|
||||
if (downloadSpinner.isSpinning) {
|
||||
downloadSpinner.succeed(`Saved replay ${score.id}!`);
|
||||
}
|
||||
await new Promise((res) => setTimeout(res, 150));
|
||||
}
|
||||
console.log(`${chalk.green("✔")} Downloaded ${downloadedReplaysCount}/${data.scores.length} replay(s)!`);
|
||||
process.exit(0);
|
||||
})();
|
||||
@@ -0,0 +1,34 @@
|
||||
export type MapScores = {
|
||||
status: string;
|
||||
scores: {
|
||||
id: number;
|
||||
map_md5: string;
|
||||
score: number;
|
||||
pp: number;
|
||||
acc: number;
|
||||
max_combo: number;
|
||||
mods: number;
|
||||
n300: number;
|
||||
n100: number;
|
||||
n50: number;
|
||||
nmiss: number;
|
||||
ngeki: number;
|
||||
nkatu: number;
|
||||
grade: string;
|
||||
status: number;
|
||||
mode: number;
|
||||
play_time: string;
|
||||
time_elapsed: number;
|
||||
userid: number;
|
||||
perfect: number;
|
||||
player_name: string;
|
||||
country: string;
|
||||
username_style: string;
|
||||
priv: number;
|
||||
creation_time: number;
|
||||
latest_activity: number;
|
||||
clan_id: number | null;
|
||||
clan_name: string | null;
|
||||
clan_tag: string | null;
|
||||
}[];
|
||||
};
|
||||
@@ -0,0 +1,186 @@
|
||||
export enum Gamemodes {
|
||||
VANILLA_OSU = 0,
|
||||
VANILLA_TAIKO = 1,
|
||||
VANILLA_CATCH = 2,
|
||||
VANILLA_MANIA = 3,
|
||||
|
||||
RELAX_OSU = 4,
|
||||
RELAX_TAIKO = 5,
|
||||
RELAX_CATCH = 6,
|
||||
|
||||
AUTOPILOT_OSU = 8,
|
||||
}
|
||||
|
||||
export enum Mode {
|
||||
OSU = 0,
|
||||
TAIKO = 1,
|
||||
CATCH = 2,
|
||||
MANIA = 3,
|
||||
}
|
||||
|
||||
export enum Type {
|
||||
VANILLA = 0,
|
||||
RELAX = 4,
|
||||
AUTOPILOT = 8,
|
||||
}
|
||||
|
||||
export const validModes = [Mode.OSU, Mode.TAIKO, Mode.CATCH, Mode.MANIA];
|
||||
export const validTypes = [Type.VANILLA, Type.RELAX, Type.AUTOPILOT];
|
||||
export const validModeTypeCombinations = [0, 1, 2, 3, 4, 5, 6, 8];
|
||||
export const validModeTypeCombinationsSorted = [0, 4, 8, 1, 5, 2, 6, 3];
|
||||
|
||||
export const validMode = (modeStr: string) => modeStrToInt(modeStr) !== undefined;
|
||||
export const validType = (typeStr: string) => typeStrToInt(typeStr) !== undefined;
|
||||
|
||||
export const modeStrToInt = (modeStr: 'osu' | 'taiko' | 'catch' | 'mania' | string) => {
|
||||
switch (modeStr) {
|
||||
case 'taiko':
|
||||
return Mode.TAIKO;
|
||||
case 'catch':
|
||||
return Mode.CATCH;
|
||||
case 'mania':
|
||||
return Mode.MANIA;
|
||||
case 'osu':
|
||||
return Mode.OSU;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const modeIntToStr = (modeInt: number) => {
|
||||
switch (modeInt) {
|
||||
case Mode.TAIKO:
|
||||
return 'taiko';
|
||||
case Mode.CATCH:
|
||||
return 'catch';
|
||||
case Mode.MANIA:
|
||||
return 'mania';
|
||||
case Mode.OSU:
|
||||
return 'osu';
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const typeStrToInt = (typeStr: 'vanilla' | 'relax' | 'autopilot' | string) => {
|
||||
switch (typeStr) {
|
||||
case 'relax':
|
||||
return Type.RELAX;
|
||||
case 'autopilot':
|
||||
return Type.AUTOPILOT;
|
||||
case 'vanilla':
|
||||
return Type.VANILLA;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const typeIntToStr = (typeInt: number) => {
|
||||
switch (typeInt) {
|
||||
case Type.RELAX:
|
||||
return 'relax';
|
||||
case Type.AUTOPILOT:
|
||||
return 'autopilot';
|
||||
case Type.VANILLA:
|
||||
return 'vanilla';
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getGamemodeInt = (
|
||||
mode: 'osu' | 'taiko' | 'catch' | 'mania' | string | undefined,
|
||||
type: 'vanilla' | 'relax' | 'autopilot' | string | undefined
|
||||
) => {
|
||||
let modee = 0;
|
||||
switch (mode) {
|
||||
case 'taiko':
|
||||
modee += Mode.TAIKO;
|
||||
break;
|
||||
case 'catch':
|
||||
modee += Mode.CATCH;
|
||||
break;
|
||||
case 'mania':
|
||||
modee += Mode.MANIA;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'relax':
|
||||
modee += Type.RELAX;
|
||||
break;
|
||||
case 'autopilot':
|
||||
modee += Type.AUTOPILOT;
|
||||
break;
|
||||
}
|
||||
|
||||
return modee;
|
||||
};
|
||||
|
||||
export const getGamemodeName = (
|
||||
mode: 'osu' | 'taiko' | 'catch' | 'mania' | string | undefined,
|
||||
type: 'vanilla' | 'relax' | 'autopilot' | string | undefined
|
||||
) => {
|
||||
let modeStr = '';
|
||||
switch (mode) {
|
||||
case 'taiko':
|
||||
modeStr += 'taiko!';
|
||||
break;
|
||||
case 'catch':
|
||||
modeStr += 'catch!';
|
||||
break;
|
||||
case 'mania':
|
||||
modeStr += 'mania!';
|
||||
break;
|
||||
default:
|
||||
modeStr += 'osu!';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'relax':
|
||||
modeStr += 'rx';
|
||||
break;
|
||||
case 'autopilot':
|
||||
modeStr += 'ap';
|
||||
break;
|
||||
default:
|
||||
modeStr += 'vn';
|
||||
break;
|
||||
}
|
||||
|
||||
return modeStr;
|
||||
};
|
||||
|
||||
export const getModeAndTypeFromGamemode = (gamemode: number) => {
|
||||
let mode = Mode.OSU;
|
||||
let type = Type.VANILLA;
|
||||
const vanillaMode = gamemode % 4;
|
||||
|
||||
switch (vanillaMode) {
|
||||
case Mode.TAIKO:
|
||||
mode = Mode.TAIKO;
|
||||
break;
|
||||
case Mode.CATCH:
|
||||
mode = Mode.CATCH;
|
||||
break;
|
||||
case Mode.MANIA:
|
||||
mode = Mode.MANIA;
|
||||
break;
|
||||
}
|
||||
|
||||
const typee = gamemode - vanillaMode;
|
||||
switch (typee) {
|
||||
case Type.RELAX:
|
||||
type = Type.RELAX;
|
||||
break;
|
||||
case Type.AUTOPILOT:
|
||||
type = Type.AUTOPILOT;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
mode,
|
||||
type,
|
||||
};
|
||||
};
|
||||
|
||||
export const isValidGamemode = (gamemodeInt: number) => {
|
||||
return validModeTypeCombinations.includes(gamemodeInt);
|
||||
};
|
||||
Submodule
+1
Submodule target added at 13feb373dc.
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user