From f1d7b4416ad2f03d2f25963804a49edc42e1934b Mon Sep 17 00:00:00 2001 From: daydreamer-json Date: Thu, 12 Feb 2026 01:46:46 +0900 Subject: [PATCH] feat: implement china region launcher api support --- .github/workflows/main.yml | 6 + .gitignore | 1 + README.md | 10 +- bun.lock | 3 + .../launcher/launcher/Arknights/1/all.json | 21 ++ .../launcher/launcher/Arknights/1/latest.json | 18 ++ .../launcher/launcher/Arknights/1/v1.1.1.json | 18 ++ .../launcher/launcher/EndField/1/all.json | 21 ++ .../launcher/launcher/EndField/1/latest.json | 18 ++ .../launcher/launcher/EndField/1/v1.1.1.json | 18 ++ output/akEndfield/launcher/launcher/list.md | 40 +++ .../launcher/launcher/official/1/all.json | 21 ++ .../launcher/launcher/official/1/latest.json | 18 ++ .../launcher/launcher/official/1/v1.1.0.json | 18 ++ .../launcher/launcher/official/6/all.json | 4 +- .../launcher/launcher/official/6/latest.json | 2 +- .../launcher/launcher/official/6/v1.0.0.json | 2 +- .../launcher/launcher/official/6/v1.1.0.json | 2 +- .../launcher/launcherExe/Arknights/1/all.json | 18 ++ .../launcherExe/Arknights/1/latest.json | 15 ++ .../launcherExe/Arknights/1/v1.1.1.json | 15 ++ .../launcher/launcherExe/EndField/1/all.json | 18 ++ .../launcherExe/EndField/1/latest.json | 15 ++ .../launcherExe/EndField/1/v1.1.1.json | 15 ++ .../launcher/launcherExe/EndField/6/all.json | 18 ++ .../launcherExe/EndField/6/latest.json | 15 ++ .../launcherExe/EndField/6/v1.1.0.json | 15 ++ .../launcher/launcherExe/Official/1/all.json | 18 ++ .../launcherExe/Official/1/latest.json | 15 ++ .../launcherExe/Official/1/v1.1.0.json | 15 ++ .../launcher/launcherExe/Official/6/all.json | 18 ++ .../launcherExe/Official/6/latest.json | 15 ++ .../launcherExe/Official/6/v1.1.0.json | 15 ++ .../akEndfield/launcher/launcherExe/list.md | 37 +++ output/wayback_machine.json | 8 + package.json | 1 + src/cmds/test.ts | 239 ++++++++++++++---- src/types/api/akEndfield/Api.ts | 9 + src/utils/api/akEndfield/launcher.ts | 38 ++- src/utils/api/index.ts | 2 + .../api/webArchiveOrg/defaultSettings.ts | 11 + src/utils/api/webArchiveOrg/index.ts | 85 +++++++ src/utils/config.ts | 18 +- src/utils/webArchiveOrg.ts | 65 +++++ 44 files changed, 930 insertions(+), 64 deletions(-) create mode 100644 output/akEndfield/launcher/launcher/Arknights/1/all.json create mode 100644 output/akEndfield/launcher/launcher/Arknights/1/latest.json create mode 100644 output/akEndfield/launcher/launcher/Arknights/1/v1.1.1.json create mode 100644 output/akEndfield/launcher/launcher/EndField/1/all.json create mode 100644 output/akEndfield/launcher/launcher/EndField/1/latest.json create mode 100644 output/akEndfield/launcher/launcher/EndField/1/v1.1.1.json create mode 100644 output/akEndfield/launcher/launcher/list.md create mode 100644 output/akEndfield/launcher/launcher/official/1/all.json create mode 100644 output/akEndfield/launcher/launcher/official/1/latest.json create mode 100644 output/akEndfield/launcher/launcher/official/1/v1.1.0.json create mode 100644 output/akEndfield/launcher/launcherExe/Arknights/1/all.json create mode 100644 output/akEndfield/launcher/launcherExe/Arknights/1/latest.json create mode 100644 output/akEndfield/launcher/launcherExe/Arknights/1/v1.1.1.json create mode 100644 output/akEndfield/launcher/launcherExe/EndField/1/all.json create mode 100644 output/akEndfield/launcher/launcherExe/EndField/1/latest.json create mode 100644 output/akEndfield/launcher/launcherExe/EndField/1/v1.1.1.json create mode 100644 output/akEndfield/launcher/launcherExe/EndField/6/all.json create mode 100644 output/akEndfield/launcher/launcherExe/EndField/6/latest.json create mode 100644 output/akEndfield/launcher/launcherExe/EndField/6/v1.1.0.json create mode 100644 output/akEndfield/launcher/launcherExe/Official/1/all.json create mode 100644 output/akEndfield/launcher/launcherExe/Official/1/latest.json create mode 100644 output/akEndfield/launcher/launcherExe/Official/1/v1.1.0.json create mode 100644 output/akEndfield/launcher/launcherExe/Official/6/all.json create mode 100644 output/akEndfield/launcher/launcherExe/Official/6/latest.json create mode 100644 output/akEndfield/launcher/launcherExe/Official/6/v1.1.0.json create mode 100644 output/akEndfield/launcher/launcherExe/list.md create mode 100644 output/wayback_machine.json create mode 100644 src/utils/api/webArchiveOrg/defaultSettings.ts create mode 100644 src/utils/api/webArchiveOrg/index.ts create mode 100644 src/utils/webArchiveOrg.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 04c2c08..0f74e1e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,6 +20,12 @@ jobs: uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun install + - name: Create config auth + env: + CONFIG_AUTH_TXT_CTX: ${{ secrets.CONFIG_AUTH_TXT }} + run: | + mkdir -p config + echo "$CONFIG_AUTH_TXT_CTX" > config/config_auth.txt - name: Run script uses: nick-fields/retry@v3 with: diff --git a/.gitignore b/.gitignore index 95bc2de..6f15f33 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ config/config.yaml +config/config_auth.txt memo/ # dependencies (bun install) diff --git a/README.md b/README.md index d70ebe6..ff1cef7 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ API outputs are stored in the [`output`](/output/) directory. The APIs currently being monitored are as follows: - Launcher - - Get latest game + - Get latest game (Global) - Get latest game resources - - Get latest launcher + - Get latest launcher (Global, China) ## Download Library @@ -23,9 +23,15 @@ To easily view information about past versions of game packages and other items, - [Windows Official](/output/akEndfield/launcher/game/6/list_patch.md) - [Windows Epic](/output/akEndfield/launcher/game/801/list_patch.md.md) - [**Game resources**](/output/akEndfield/launcher/game_resources/6/list.md) (Windows, Android, iOS, PlayStation) +- **Launcher packages** + - [Launcher (zip)](/output/akEndfield/launcher/launcher/list.md) + - [Launcher (installer)](/output/akEndfield/launcher/launcherExe/list.md) + +Some packages requiring `auth_key` are available via the Wayback Machine mirror. All dates and times specified in Markdown files are in Asia server time (China Standard Time, UTC+8). + ## Contributing Since I can only run the game on platforms and operating systems that are available to me, there may be inaccuracies. If you have information -particularly regarding **Chinese regional game data**, beta versions, encrypted binaries such as `game_files` or `package_files`- or if you're able to help improve the code, or if you encounter any other issues, feel free to submit an issue or a pull request. diff --git a/bun.lock b/bun.lock index 85e3874..48ad019 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,7 @@ "dependencies": { "chalk": "^5.6.2", "cli-table3": "^0.6.5", + "cookie": "^1.1.1", "deepmerge": "^4.3.1", "ky": "^1.14.2", "log4js": "^6.9.1", @@ -139,6 +140,8 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "date-format": ["date-format@4.0.14", "", {}, "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], diff --git a/output/akEndfield/launcher/launcher/Arknights/1/all.json b/output/akEndfield/launcher/launcher/Arknights/1/all.json new file mode 100644 index 0000000..16e85a6 --- /dev/null +++ b/output/akEndfield/launcher/launcher/Arknights/1/all.json @@ -0,0 +1,21 @@ +[ + { + "updatedAt": "2026-02-11T19:23:45.284+09:00", + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "Arknights" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip?auth_key=1770805425-b97874bbf29b4747a0aba81165580890-0-3d4e79aa6cd654b814cc71189103aa83", + "md5": "013d7e900c46b2324c8a749a06b84768", + "package_size": "125456996", + "total_size": "389401989", + "description": "" + } + } +] diff --git a/output/akEndfield/launcher/launcher/Arknights/1/latest.json b/output/akEndfield/launcher/launcher/Arknights/1/latest.json new file mode 100644 index 0000000..e630e12 --- /dev/null +++ b/output/akEndfield/launcher/launcher/Arknights/1/latest.json @@ -0,0 +1,18 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "Arknights" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip?auth_key=1770805425-b97874bbf29b4747a0aba81165580890-0-3d4e79aa6cd654b814cc71189103aa83", + "md5": "013d7e900c46b2324c8a749a06b84768", + "package_size": "125456996", + "total_size": "389401989", + "description": "" + } +} diff --git a/output/akEndfield/launcher/launcher/Arknights/1/v1.1.1.json b/output/akEndfield/launcher/launcher/Arknights/1/v1.1.1.json new file mode 100644 index 0000000..e630e12 --- /dev/null +++ b/output/akEndfield/launcher/launcher/Arknights/1/v1.1.1.json @@ -0,0 +1,18 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "Arknights" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip?auth_key=1770805425-b97874bbf29b4747a0aba81165580890-0-3d4e79aa6cd654b814cc71189103aa83", + "md5": "013d7e900c46b2324c8a749a06b84768", + "package_size": "125456996", + "total_size": "389401989", + "description": "" + } +} diff --git a/output/akEndfield/launcher/launcher/EndField/1/all.json b/output/akEndfield/launcher/launcher/EndField/1/all.json new file mode 100644 index 0000000..1870da0 --- /dev/null +++ b/output/akEndfield/launcher/launcher/EndField/1/all.json @@ -0,0 +1,21 @@ +[ + { + "updatedAt": "2026-02-11T19:23:44.678+09:00", + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "EndField" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip?auth_key=1770805424-b340c15e0d964370803a4e25b35dfae7-0-c4db269999383a19f59306d6cbef99a0", + "md5": "0200ac146e854fc7692361d5f02e7727", + "package_size": "125818123", + "total_size": "390124155", + "description": "" + } + } +] diff --git a/output/akEndfield/launcher/launcher/EndField/1/latest.json b/output/akEndfield/launcher/launcher/EndField/1/latest.json new file mode 100644 index 0000000..97fa28a --- /dev/null +++ b/output/akEndfield/launcher/launcher/EndField/1/latest.json @@ -0,0 +1,18 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "EndField" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip?auth_key=1770805424-b340c15e0d964370803a4e25b35dfae7-0-c4db269999383a19f59306d6cbef99a0", + "md5": "0200ac146e854fc7692361d5f02e7727", + "package_size": "125818123", + "total_size": "390124155", + "description": "" + } +} diff --git a/output/akEndfield/launcher/launcher/EndField/1/v1.1.1.json b/output/akEndfield/launcher/launcher/EndField/1/v1.1.1.json new file mode 100644 index 0000000..97fa28a --- /dev/null +++ b/output/akEndfield/launcher/launcher/EndField/1/v1.1.1.json @@ -0,0 +1,18 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "EndField" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip?auth_key=1770805424-b340c15e0d964370803a4e25b35dfae7-0-c4db269999383a19f59306d6cbef99a0", + "md5": "0200ac146e854fc7692361d5f02e7727", + "package_size": "125818123", + "total_size": "390124155", + "description": "" + } +} diff --git a/output/akEndfield/launcher/launcher/list.md b/output/akEndfield/launcher/launcher/list.md new file mode 100644 index 0000000..e04ea5e --- /dev/null +++ b/output/akEndfield/launcher/launcher/list.md @@ -0,0 +1,40 @@ +# Launcher Packages (zip) + +- [OS EndField](#launcher-os-endfield) +- [OS Official](#launcher-os-official) +- [CN EndField](#launcher-cn-endfield) +- [CN Arknights](#launcher-cn-arknights) +- [CN Official](#launcher-cn-official) + +

OS EndField

+ +| Date | Version | File | MD5 Checksum | Unpacked | Packed | +| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------: | ---------: | +| 2026/01/22 17:44:29 | 1.0.0 | [GRYPHLINK_1.0.0_6_6_official.zip](https://launcher.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.0.0/6/6/D6KGicSF/GRYPHLINK_1.0.0_6_6_official.zip) | `fe71ba125d1b1c30d4bf1c05d7af4be6` | 238.08 MiB | 106.04 MiB | +| 2026/01/29 18:30:23 | 1.0.2 | [GRYPHLINK_1.0.2_6_6_endfield.zip](https://launcher.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.0.2/6/6/FCLRoWw78h2SQCYy/GRYPHLINK_1.0.2_6_6_endfield.zip) | `154d73bd32b34e9e3429a1b4fda44a0c` | 252.01 MiB | 119.97 MiB | +| 2026/02/09 10:00:29 | 1.1.0 | [GRYPHLINK_v1.1.0.1107_6_6_endfield.zip](https://launcher.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/NVX60B0FgrIAIWH2/GRYPHLINK_v1.1.0.1107_6_6_endfield.zip) | `b099efdca62ba3a86e1160487b8c0589` | 412.06 MiB | 279.70 MiB | + +

OS Official

+ +| Date | Version | File | MD5 Checksum | Unpacked | Packed | +| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------: | ---------: | +| 2026/01/22 17:44:47 | 1.0.0 | [GRYPHLINK_1.0.0_6_6_official.zip](https://launcher.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.0.0/6/6/D6KGicSF/GRYPHLINK_1.0.0_6_6_official.zip) | `fe71ba125d1b1c30d4bf1c05d7af4be6` | 238.08 MiB | 106.04 MiB | +| 2026/02/09 10:00:29 | 1.1.0 | [GRYPHLINK_v1.1.0.1112_6_6_official.zip](https://launcher.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/oL3UfvgkscD1onKF/GRYPHLINK_v1.1.0.1112_6_6_official.zip) | `dd92acd3fd5217992a64cb4b22729740` | 238.13 MiB | 106.06 MiB | + +

CN EndField

+ +| Date | Version | File | MD5 Checksum | Unpacked | Packed | +| ------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------: | ---------: | +| 2026/02/11 18:23:44 | 1.1.1 | HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip [Orig](https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip?auth_key=1770805424-b340c15e0d964370803a4e25b35dfae7-0-c4db269999383a19f59306d6cbef99a0) / [Mirror](https://web.archive.org/web/20260211102702/https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip?auth_key=1770805594-f034be52dc1c4149b8e5863d81942bc5-0-ebc04db7ac1c356da84cbd05abb7dc8a) | `0200ac146e854fc7692361d5f02e7727` | 252.06 MiB | 119.99 MiB | + +

CN Arknights

+ +| Date | Version | File | MD5 Checksum | Unpacked | Packed | +| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------: | ---------: | +| 2026/02/11 18:23:45 | 1.1.1 | HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip [Orig](https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip?auth_key=1770805425-b97874bbf29b4747a0aba81165580890-0-3d4e79aa6cd654b814cc71189103aa83) / [Mirror](https://web.archive.org/web/20260211103331/https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip?auth_key=1770805996-d0cbf63e930148b0911850e320f016f1-0-5cfcf50e25770622c3c1bd2ab32b3165) | `013d7e900c46b2324c8a749a06b84768` | 251.72 MiB | 119.65 MiB | + +

CN Official

+ +| Date | Version | File | MD5 Checksum | Unpacked | Packed | +| ------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------: | ---------: | +| 2026/02/11 18:23:46 | 1.1.0 | HypergryphLauncher_v1.1.0.1111_1_1_official.zip [Orig](https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.zip?auth_key=1770805425-4767f09255e04759acec76fee5709419-0-704e6c4ef290a9ed54699fa50d25896f) / [Mirror](https://web.archive.org/web/20260211075808/https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.zip?auth_key=1770796678-5a2fff9d792c4621b62bf9b541e36f4c-0-e9484399f81f6163d5ffea3ea3d0772f) | `ac840b12eeeee1e09be228e767300cc5` | 238.14 MiB | 106.06 MiB | diff --git a/output/akEndfield/launcher/launcher/official/1/all.json b/output/akEndfield/launcher/launcher/official/1/all.json new file mode 100644 index 0000000..044a9b5 --- /dev/null +++ b/output/akEndfield/launcher/launcher/official/1/all.json @@ -0,0 +1,21 @@ +[ + { + "updatedAt": "2026-02-11T19:23:46.357+09:00", + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "Official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.zip?auth_key=1770805425-4767f09255e04759acec76fee5709419-0-704e6c4ef290a9ed54699fa50d25896f", + "md5": "ac840b12eeeee1e09be228e767300cc5", + "package_size": "111213036", + "total_size": "360918444", + "description": "" + } + } +] diff --git a/output/akEndfield/launcher/launcher/official/1/latest.json b/output/akEndfield/launcher/launcher/official/1/latest.json new file mode 100644 index 0000000..7f0176e --- /dev/null +++ b/output/akEndfield/launcher/launcher/official/1/latest.json @@ -0,0 +1,18 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "Official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.zip?auth_key=1770805425-4767f09255e04759acec76fee5709419-0-704e6c4ef290a9ed54699fa50d25896f", + "md5": "ac840b12eeeee1e09be228e767300cc5", + "package_size": "111213036", + "total_size": "360918444", + "description": "" + } +} diff --git a/output/akEndfield/launcher/launcher/official/1/v1.1.0.json b/output/akEndfield/launcher/launcher/official/1/v1.1.0.json new file mode 100644 index 0000000..7f0176e --- /dev/null +++ b/output/akEndfield/launcher/launcher/official/1/v1.1.0.json @@ -0,0 +1,18 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "targetApp": "Official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "zip_package_url": "https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.zip?auth_key=1770805425-4767f09255e04759acec76fee5709419-0-704e6c4ef290a9ed54699fa50d25896f", + "md5": "ac840b12eeeee1e09be228e767300cc5", + "package_size": "111213036", + "total_size": "360918444", + "description": "" + } +} diff --git a/output/akEndfield/launcher/launcher/official/6/all.json b/output/akEndfield/launcher/launcher/official/6/all.json index 204f27a..6b041bf 100644 --- a/output/akEndfield/launcher/launcher/official/6/all.json +++ b/output/akEndfield/launcher/launcher/official/6/all.json @@ -5,7 +5,7 @@ "appCode": "TiaytKBUIEdoEwRT", "channel": 6, "subChannel": 6, - "targetApp": null + "targetApp": "Official" }, "rsp": { "action": 1, @@ -24,7 +24,7 @@ "appCode": "TiaytKBUIEdoEwRT", "channel": 6, "subChannel": 6, - "targetApp": null + "targetApp": "Official" }, "rsp": { "action": 1, diff --git a/output/akEndfield/launcher/launcher/official/6/latest.json b/output/akEndfield/launcher/launcher/official/6/latest.json index 72be10c..a432d7f 100644 --- a/output/akEndfield/launcher/launcher/official/6/latest.json +++ b/output/akEndfield/launcher/launcher/official/6/latest.json @@ -3,7 +3,7 @@ "appCode": "TiaytKBUIEdoEwRT", "channel": 6, "subChannel": 6, - "targetApp": null + "targetApp": "Official" }, "rsp": { "action": 1, diff --git a/output/akEndfield/launcher/launcher/official/6/v1.0.0.json b/output/akEndfield/launcher/launcher/official/6/v1.0.0.json index 3714b9a..3378d53 100644 --- a/output/akEndfield/launcher/launcher/official/6/v1.0.0.json +++ b/output/akEndfield/launcher/launcher/official/6/v1.0.0.json @@ -3,7 +3,7 @@ "appCode": "TiaytKBUIEdoEwRT", "channel": 6, "subChannel": 6, - "targetApp": null + "targetApp": "Official" }, "rsp": { "action": 1, diff --git a/output/akEndfield/launcher/launcher/official/6/v1.1.0.json b/output/akEndfield/launcher/launcher/official/6/v1.1.0.json index 72be10c..a432d7f 100644 --- a/output/akEndfield/launcher/launcher/official/6/v1.1.0.json +++ b/output/akEndfield/launcher/launcher/official/6/v1.1.0.json @@ -3,7 +3,7 @@ "appCode": "TiaytKBUIEdoEwRT", "channel": 6, "subChannel": 6, - "targetApp": null + "targetApp": "Official" }, "rsp": { "action": 1, diff --git a/output/akEndfield/launcher/launcherExe/Arknights/1/all.json b/output/akEndfield/launcher/launcherExe/Arknights/1/all.json new file mode 100644 index 0000000..a530f06 --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Arknights/1/all.json @@ -0,0 +1,18 @@ +[ + { + "updatedAt": "2026-02-11T19:23:45.295+09:00", + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "arknights" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe?auth_key=1770805425-172992522f484813a1114b3495dbf372-0-08a2616d5eb0731a005ece3fd6e606a9&tracking=Hypergryph", + "exe_size": "166524160" + } + } +] diff --git a/output/akEndfield/launcher/launcherExe/Arknights/1/latest.json b/output/akEndfield/launcher/launcherExe/Arknights/1/latest.json new file mode 100644 index 0000000..1425582 --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Arknights/1/latest.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "arknights" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe?auth_key=1770805425-172992522f484813a1114b3495dbf372-0-08a2616d5eb0731a005ece3fd6e606a9&tracking=Hypergryph", + "exe_size": "166524160" + } +} diff --git a/output/akEndfield/launcher/launcherExe/Arknights/1/v1.1.1.json b/output/akEndfield/launcher/launcherExe/Arknights/1/v1.1.1.json new file mode 100644 index 0000000..1425582 --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Arknights/1/v1.1.1.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "arknights" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe?auth_key=1770805425-172992522f484813a1114b3495dbf372-0-08a2616d5eb0731a005ece3fd6e606a9&tracking=Hypergryph", + "exe_size": "166524160" + } +} diff --git a/output/akEndfield/launcher/launcherExe/EndField/1/all.json b/output/akEndfield/launcher/launcherExe/EndField/1/all.json new file mode 100644 index 0000000..ff135da --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/EndField/1/all.json @@ -0,0 +1,18 @@ +[ + { + "updatedAt": "2026-02-11T19:23:44.682+09:00", + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "endfield" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe?auth_key=1770805424-20054abefba5462089be18ccc722a216-0-ea895a486266c0d352a65e21d49127d5&tracking=Hypergryph", + "exe_size": "167777352" + } + } +] diff --git a/output/akEndfield/launcher/launcherExe/EndField/1/latest.json b/output/akEndfield/launcher/launcherExe/EndField/1/latest.json new file mode 100644 index 0000000..7a4dedd --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/EndField/1/latest.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "endfield" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe?auth_key=1770805424-20054abefba5462089be18ccc722a216-0-ea895a486266c0d352a65e21d49127d5&tracking=Hypergryph", + "exe_size": "167777352" + } +} diff --git a/output/akEndfield/launcher/launcherExe/EndField/1/v1.1.1.json b/output/akEndfield/launcher/launcherExe/EndField/1/v1.1.1.json new file mode 100644 index 0000000..7a4dedd --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/EndField/1/v1.1.1.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "endfield" + }, + "rsp": { + "action": 1, + "version": "1.1.1", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe?auth_key=1770805424-20054abefba5462089be18ccc722a216-0-ea895a486266c0d352a65e21d49127d5&tracking=Hypergryph", + "exe_size": "167777352" + } +} diff --git a/output/akEndfield/launcher/launcherExe/EndField/6/all.json b/output/akEndfield/launcher/launcherExe/EndField/6/all.json new file mode 100644 index 0000000..7bbd99f --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/EndField/6/all.json @@ -0,0 +1,18 @@ +[ + { + "updatedAt": "2026-02-11T19:23:41.749+09:00", + "req": { + "appCode": "TiaytKBUIEdoEwRT", + "channel": 6, + "subChannel": 6, + "ta": "endfield" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/NVX60B0FgrIAIWH2/GRYPHLINK_v1.1.0.1107_6_6_endfield.exe?tracking=GRYPHLINK", + "exe_size": "167774552" + } + } +] diff --git a/output/akEndfield/launcher/launcherExe/EndField/6/latest.json b/output/akEndfield/launcher/launcherExe/EndField/6/latest.json new file mode 100644 index 0000000..582ac4f --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/EndField/6/latest.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "TiaytKBUIEdoEwRT", + "channel": 6, + "subChannel": 6, + "ta": "endfield" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/NVX60B0FgrIAIWH2/GRYPHLINK_v1.1.0.1107_6_6_endfield.exe?tracking=GRYPHLINK", + "exe_size": "167774552" + } +} diff --git a/output/akEndfield/launcher/launcherExe/EndField/6/v1.1.0.json b/output/akEndfield/launcher/launcherExe/EndField/6/v1.1.0.json new file mode 100644 index 0000000..582ac4f --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/EndField/6/v1.1.0.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "TiaytKBUIEdoEwRT", + "channel": 6, + "subChannel": 6, + "ta": "endfield" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/NVX60B0FgrIAIWH2/GRYPHLINK_v1.1.0.1107_6_6_endfield.exe?tracking=GRYPHLINK", + "exe_size": "167774552" + } +} diff --git a/output/akEndfield/launcher/launcherExe/Official/1/all.json b/output/akEndfield/launcher/launcherExe/Official/1/all.json new file mode 100644 index 0000000..14083f0 --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Official/1/all.json @@ -0,0 +1,18 @@ +[ + { + "updatedAt": "2026-02-11T19:23:46.359+09:00", + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.exe?auth_key=1770805426-482b692452694284b22c17c22bfb41ca-0-3a0e71c35f55d5c438e0b88d2a134d6e&tracking=Hypergryph", + "exe_size": "124329856" + } + } +] diff --git a/output/akEndfield/launcher/launcherExe/Official/1/latest.json b/output/akEndfield/launcher/launcherExe/Official/1/latest.json new file mode 100644 index 0000000..aeeb7be --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Official/1/latest.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.exe?auth_key=1770805426-482b692452694284b22c17c22bfb41ca-0-3a0e71c35f55d5c438e0b88d2a134d6e&tracking=Hypergryph", + "exe_size": "124329856" + } +} diff --git a/output/akEndfield/launcher/launcherExe/Official/1/v1.1.0.json b/output/akEndfield/launcher/launcherExe/Official/1/v1.1.0.json new file mode 100644 index 0000000..aeeb7be --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Official/1/v1.1.0.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "abYeZZ16BPluCFyT", + "channel": 1, + "subChannel": 1, + "ta": "official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.exe?auth_key=1770805426-482b692452694284b22c17c22bfb41ca-0-3a0e71c35f55d5c438e0b88d2a134d6e&tracking=Hypergryph", + "exe_size": "124329856" + } +} diff --git a/output/akEndfield/launcher/launcherExe/Official/6/all.json b/output/akEndfield/launcher/launcherExe/Official/6/all.json new file mode 100644 index 0000000..206c05c --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Official/6/all.json @@ -0,0 +1,18 @@ +[ + { + "updatedAt": "2026-02-11T19:23:42.081+09:00", + "req": { + "appCode": "TiaytKBUIEdoEwRT", + "channel": 6, + "subChannel": 6, + "ta": "official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/oL3UfvgkscD1onKF/GRYPHLINK_v1.1.0.1112_6_6_official.exe?tracking=GRYPHLINK", + "exe_size": "124325992" + } + } +] diff --git a/output/akEndfield/launcher/launcherExe/Official/6/latest.json b/output/akEndfield/launcher/launcherExe/Official/6/latest.json new file mode 100644 index 0000000..bef6b27 --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Official/6/latest.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "TiaytKBUIEdoEwRT", + "channel": 6, + "subChannel": 6, + "ta": "official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/oL3UfvgkscD1onKF/GRYPHLINK_v1.1.0.1112_6_6_official.exe?tracking=GRYPHLINK", + "exe_size": "124325992" + } +} diff --git a/output/akEndfield/launcher/launcherExe/Official/6/v1.1.0.json b/output/akEndfield/launcher/launcherExe/Official/6/v1.1.0.json new file mode 100644 index 0000000..bef6b27 --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/Official/6/v1.1.0.json @@ -0,0 +1,15 @@ +{ + "req": { + "appCode": "TiaytKBUIEdoEwRT", + "channel": 6, + "subChannel": 6, + "ta": "official" + }, + "rsp": { + "action": 1, + "version": "1.1.0", + "request_version": "", + "exe_url": "https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/oL3UfvgkscD1onKF/GRYPHLINK_v1.1.0.1112_6_6_official.exe?tracking=GRYPHLINK", + "exe_size": "124325992" + } +} diff --git a/output/akEndfield/launcher/launcherExe/list.md b/output/akEndfield/launcher/launcherExe/list.md new file mode 100644 index 0000000..9833eea --- /dev/null +++ b/output/akEndfield/launcher/launcherExe/list.md @@ -0,0 +1,37 @@ +# Launcher Packages (Installer) + +- [OS EndField](#launcher-os-endfield) +- [OS Official](#launcher-os-official) +- [CN EndField](#launcher-cn-endfield) +- [CN Arknights](#launcher-cn-arknights) +- [CN Official](#launcher-cn-official) + +

OS EndField

+ +| Date | Version | File | Size | +| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------: | +| 2026/02/11 18:23:41 | 1.1.0 | [GRYPHLINK_v1.1.0.1107_6_6_endfield.exe](https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/NVX60B0FgrIAIWH2/GRYPHLINK_v1.1.0.1107_6_6_endfield.exe?tracking=GRYPHLINK) | 160.00 MiB | + +

OS Official

+ +| Date | Version | File | Size | +| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------: | +| 2026/02/11 18:23:42 | 1.1.0 | [GRYPHLINK_v1.1.0.1112_6_6_official.exe](https://launcher-rule.hg-cdn.com/TiaytKBUIEdoEwRT/launcher/1.1.0/6/6/oL3UfvgkscD1onKF/GRYPHLINK_v1.1.0.1112_6_6_official.exe?tracking=GRYPHLINK) | 118.57 MiB | + +

CN EndField

+ +| Date | Version | File | Size | +| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------: | +| 2026/02/11 18:23:44 | 1.1.1 | HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe [Orig](https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe?auth_key=1770805424-20054abefba5462089be18ccc722a216-0-ea895a486266c0d352a65e21d49127d5&tracking=Hypergryph) / [Mirror](https://web.archive.org/web/20260211080403/https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe?auth_key=1770796924-4c08f546f40f471885825225162e3acd-0-051b324e1cee0bb6f261f16999d72abb) | 160.00 MiB | + +

CN Arknights

+ +| Date | Version | File | Size | +| ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------: | +| 2026/02/11 18:23:45 | 1.1.1 | HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe [Orig](https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe?auth_key=1770805425-172992522f484813a1114b3495dbf372-0-08a2616d5eb0731a005ece3fd6e606a9&tracking=Hypergryph) / [Mirror](https://web.archive.org/web/20260211104513/https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe?auth_key=1770806682-d158e31170ce4841ad36c2f52ad9e818-0-aa7b23946d4c2499383620ea525a8792) | 158.81 MiB | + +

CN Official

+ +| Date | Version | File | Size | +| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------: | +| 2026/02/11 18:23:46 | 1.1.0 | HypergryphLauncher_v1.1.0.1111_1_1_official.exe [Orig](https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.exe?auth_key=1770805426-482b692452694284b22c17c22bfb41ca-0-3a0e71c35f55d5c438e0b88d2a134d6e&tracking=Hypergryph) / [Mirror](https://web.archive.org/web/20260211112144/https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.exe?auth_key=1770808883-5e39f01f0f6244e8a94f354e98f4e12d-0-7b8f1742c7d1646b9c3d618f2dd58f0c) | 118.57 MiB | diff --git a/output/wayback_machine.json b/output/wayback_machine.json new file mode 100644 index 0000000..224556b --- /dev/null +++ b/output/wayback_machine.json @@ -0,0 +1,8 @@ +[ + "https://web.archive.org/web/20260211075808/https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.zip?auth_key=1770796678-5a2fff9d792c4621b62bf9b541e36f4c-0-e9484399f81f6163d5ffea3ea3d0772f", + "https://web.archive.org/web/20260211080403/https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.exe?auth_key=1770796924-4c08f546f40f471885825225162e3acd-0-051b324e1cee0bb6f261f16999d72abb", + "https://web.archive.org/web/20260211102702/https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/NO0nx1dZDOVcjo27/HypergryphLauncher_v1.1.1.1124_1_1_endfield.zip?auth_key=1770805594-f034be52dc1c4149b8e5863d81942bc5-0-ebc04db7ac1c356da84cbd05abb7dc8a", + "https://web.archive.org/web/20260211103331/https://launcher-sign.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.zip?auth_key=1770805996-d0cbf63e930148b0911850e320f016f1-0-5cfcf50e25770622c3c1bd2ab32b3165", + "https://web.archive.org/web/20260211104513/https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.1/1/1/9lgNrktxI8ARzguv/HypergryphLauncher_v1.1.1.1121_1_1_arknights.exe?auth_key=1770806682-d158e31170ce4841ad36c2f52ad9e818-0-aa7b23946d4c2499383620ea525a8792", + "https://web.archive.org/web/20260211112144/https://launcher-rule.hycdn.cn/abYeZZ16BPluCFyT/launcher/1.1.0/1/1/2vyVIpSrNaIQNLLk/HypergryphLauncher_v1.1.0.1111_1_1_official.exe?auth_key=1770808883-5e39f01f0f6244e8a94f354e98f4e12d-0-7b8f1742c7d1646b9c3d618f2dd58f0c" +] diff --git a/package.json b/package.json index 2d13202..6b1fce9 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "chalk": "^5.6.2", "cli-table3": "^0.6.5", + "cookie": "^1.1.1", "deepmerge": "^4.3.1", "ky": "^1.14.2", "log4js": "^6.9.1", diff --git a/src/cmds/test.ts b/src/cmds/test.ts index 707382a..1a827b6 100644 --- a/src/cmds/test.ts +++ b/src/cmds/test.ts @@ -7,6 +7,9 @@ import argvUtils from '../utils/argv.js'; import appConfig from '../utils/config.js'; import logger from '../utils/logger.js'; import mathUtils from '../utils/math.js'; +import webArchiveOrg from '../utils/webArchiveOrg.js'; + +let waybackCred: { user: string; sig: string } | null = null; const formatBytes = (size: number) => mathUtils.formatFileSize(size, { @@ -21,6 +24,7 @@ const formatBytes = (size: number) => type LatestGameResponse = Awaited>; type LatestGameResourcesResponse = Awaited>; // type LatestLauncherResponse = Awaited>; +// type LatestLauncherExeResponse = Awaited>; interface StoredData { req: any; @@ -36,23 +40,42 @@ interface GameTarget { dirName: string; } -function getObjectDiff(obj1: any, obj2: any) { +function getObjectDiff( + obj1: any, + obj2: any, + ignoreRules: { + path: string[]; + pattern: RegExp; + }[] = [], + currentPath: string[] = [], +) { const diff: any = {}; const keys = new Set([...Object.keys(obj1 || {}), ...Object.keys(obj2 || {})]); + for (const key of keys) { const val1 = obj1?.[key]; const val2 = obj2?.[key]; - if (JSON.stringify(val1) !== JSON.stringify(val2)) { - if (typeof val1 === 'object' && val1 !== null && typeof val2 === 'object' && val2 !== null) { - const nestedDiff = getObjectDiff(val1, val2); - if (Object.keys(nestedDiff).length > 0) { - diff[key] = nestedDiff; - } - } else { - diff[key] = { old: val1, new: val2 }; - } + const fullPath = [...currentPath, key]; + if (JSON.stringify(val1) === JSON.stringify(val2)) continue; + + const rule = ignoreRules.find( + (r) => r.path.length === fullPath.length && r.path.every((p, i) => p === fullPath[i]), + ); + + if (rule && typeof val1 === 'string' && typeof val2 === 'string') { + const normalized1 = val1.replace(rule.pattern, ''); + const normalized2 = val2.replace(rule.pattern, ''); + if (normalized1 === normalized2) continue; + } + + if (typeof val1 === 'object' && val1 !== null && typeof val2 === 'object' && val2 !== null) { + const nestedDiff = getObjectDiff(val1, val2, ignoreRules, fullPath); + if (Object.keys(nestedDiff).length > 0) diff[key] = nestedDiff; + } else { + diff[key] = { old: val1, new: val2 }; } } + return diff; } @@ -61,6 +84,7 @@ async function saveResult( version: string, data: { req: any; rsp: T }, saveLatest: boolean = true, + ignoreRules: Parameters[2] = [], ) { const outputDir = argvUtils.getArgv()['outputDir']; const filePathBase = path.join(outputDir, ...subPaths); @@ -71,20 +95,20 @@ async function saveResult( } const dataStr = JSON.stringify(data, null, 2); - const dataMinified = JSON.stringify(data); for (const filePath of filesToCheck) { const file = Bun.file(filePath); const exists = await file.exists(); - let currentData: any = null; - if (exists) { - currentData = await file.json(); - } - if (!exists || JSON.stringify(currentData) !== dataMinified) { - if (exists) { - logger.trace(`Diff detected in ${filePath}:`, JSON.stringify(getObjectDiff(currentData, data), null, 2)); - } + + if (!exists) { await Bun.write(filePath, dataStr); + } else { + const currentData = await file.json(); + const diff = getObjectDiff(currentData, data, ignoreRules); + if (Object.keys(diff).length > 0) { + logger.trace(`Diff detected in ${filePath}:`, JSON.stringify(diff, null, 2)); + await Bun.write(filePath, dataStr); + } } } @@ -95,7 +119,10 @@ async function saveResult( allData = await allFile.json(); } - const exists = allData.some((e) => JSON.stringify({ req: e.req, rsp: e.rsp }) === dataMinified); + const exists = allData.some((e) => { + const diff = getObjectDiff({ req: e.req, rsp: e.rsp }, data, ignoreRules); + return Object.keys(diff).length === 0; + }); if (!exists) { allData.push({ updatedAt: DateTime.now().toISO(), ...data }); @@ -303,6 +330,93 @@ async function generateResourceListMd(channelStr: string) { ); } +async function generateLauncherMd(type: 'zip' | 'exe') { + const cfg = appConfig.network.api.akEndfield; + const outputDir = argvUtils.getArgv()['outputDir']; + const settings = { + zip: { + subdir: 'launcher', + title: 'Launcher Packages (zip)', + headers: ['Date', 'Version', 'File', 'MD5 Checksum', 'Unpacked', 'Packed'], + align: ['---', '---', '---', '---', '--:', '--:'], + }, + exe: { + subdir: 'launcherExe', + title: 'Launcher Packages (Installer)', + headers: ['Date', 'Version', 'File', 'Size'], + align: ['---', '---', '---', '--:'], + }, + }[type]; + + const regions = [ + { id: 'os' as const, apps: ['EndField', 'Official'] as const, channel: cfg.channel.osWinRel }, + { id: 'cn' as const, apps: ['EndField', 'Arknights', 'Official'] as const, channel: cfg.channel.cnWinRel }, + ]; + + const mdTexts: string[] = [ + `# ${settings.title}\n`, + ...regions.flatMap((r) => + r.apps.map((app) => `- [${r.id.toUpperCase()} ${app}](#launcher-${r.id}-${app.toLowerCase()})`), + ), + '\n', + ]; + + const waybackDb = await (async () => { + const localJsonPath = path.join(outputDir, 'wayback_machine.json'); + return (await Bun.file(localJsonPath).json()) as string[]; + })(); + + for (const region of regions) { + for (const appName of region.apps) { + const jsonPath = path.join( + outputDir, + 'akEndfield', + 'launcher', + settings.subdir, + appName, + String(region.channel), + 'all.json', + ); + const jsonData = (await Bun.file(jsonPath).json()) as StoredData[]; + + mdTexts.push( + `

${region.id.toUpperCase()} ${appName}

\n`, + `|${settings.headers.join('|')}|`, + `|${settings.align.join('|')}|`, + ); + + for (const e of jsonData) { + const url = type === 'zip' ? e.rsp.zip_package_url : e.rsp.exe_url; + const fileName = new URL(url).pathname.split('/').pop() ?? ''; + const cleanUrl = new URL(url); + cleanUrl.search = ''; + const waybackUrl = waybackDb.find((f) => f.includes(cleanUrl.toString())); + const fileLink = waybackUrl ? `${fileName} [Orig](${url}) / [Mirror](${waybackUrl})` : `[${fileName}](${url})`; + const date = DateTime.fromISO(e.updatedAt, { setZone: true }).setZone('UTC+8').toFormat('yyyy/MM/dd HH:mm:ss'); + const row = + type === 'zip' + ? [ + date, + e.rsp.version, + fileLink, + `\`${e.rsp.md5}\``, + formatBytes(parseInt(e.rsp.total_size) - parseInt(e.rsp.package_size)), + formatBytes(parseInt(e.rsp.package_size)), + ] + : [date, e.rsp.version, fileLink, formatBytes(parseInt(e.rsp.exe_size))]; + + mdTexts.push(`|${row.join('|')}|`); + } + mdTexts.push(''); + } + } + await Bun.write(path.join(outputDir, 'akEndfield', 'launcher', settings.subdir, 'list.md'), mdTexts.join('\n')); +} + +// 実行例 +// await generateLauncherMd('zip'); +// await generateLauncherMd('exe'); + async function fetchAndSaveLatestGames(cfg: typeof appConfig.network.api.akEndfield, gameTargets: GameTarget[]) { for (const target of gameTargets) { logger.debug(`Fetching latestGame (${target.name}) ...`); @@ -489,37 +603,68 @@ async function fetchAndSaveLatestGameResources(cfg: typeof appConfig.network.api } } -async function fetchAndSaveLatestLauncher(cfg: typeof appConfig.network.api.akEndfield, channelStr: string) { +async function fetchAndSaveLatestLauncher(cfg: typeof appConfig.network.api.akEndfield) { logger.debug('Fetching latestLauncher ...'); - const launcherTargetAppList = ['EndField', 'official'] as const; - for (const launcherTargetAppEntry of launcherTargetAppList) { - const rsp = await apiUtils.akEndfield.launcher.latestLauncher( - cfg.appCode.launcher.osWinRel, - cfg.channel.osWinRel, - cfg.channel.osWinRel, - null, - launcherTargetAppEntry === 'official' ? null : launcherTargetAppEntry, - ); - logger.info(`Fetched latestLauncher: v${rsp.version}, ${launcherTargetAppEntry}`); - const prettyRsp = { - req: { - appCode: cfg.appCode.launcher.osWinRel, - channel: cfg.channel.osWinRel, - subChannel: cfg.channel.osWinRel, - targetApp: launcherTargetAppEntry === 'official' ? null : launcherTargetAppEntry, - }, - rsp, - }; + const regions = [ + { + id: 'os' as const, + apps: ['EndField', 'Official'] as const, + code: cfg.appCode.launcher.osWinRel, + channel: cfg.channel.osWinRel, + }, + { + id: 'cn' as const, + apps: ['EndField', 'Arknights', 'Official'] as const, + code: cfg.appCode.launcher.cnWinRel, + channel: cfg.channel.cnWinRel, + }, + ]; + const removeQueryStr = (url: string): string => { + const urlObj = new URL(url); + urlObj.search = ''; + return urlObj.toString(); + }; + const saveToWM = async (url: string): Promise => { + if (waybackCred === null) throw new Error('Wayback Machine auth is null'); + const localJsonPath = path.join(argvUtils.getArgv()['outputDir'], 'wayback_machine.json'); + const localJson: string[] = await Bun.file(localJsonPath).json(); + if (!localJson.find((e) => e.includes(removeQueryStr(url)))) { + await webArchiveOrg.savePage(url, waybackCred); + } + }; - await saveResult( - ['akEndfield', 'launcher', 'launcher', launcherTargetAppEntry, channelStr], - rsp.version, - prettyRsp, - ); + for (const { id, apps, code, channel } of regions) { + for (const app of apps) { + const apiArgs = [code, channel, channel, null] as const; + const rsp = await apiUtils.akEndfield.launcher.latestLauncher(...apiArgs, app, id); + const prettyRsp = { req: { appCode: code, channel, subChannel: channel, targetApp: app }, rsp }; + const appLower = app.toLowerCase(); + const rspExe = await apiUtils.akEndfield.launcher.latestLauncherExe(...apiArgs, appLower, id); + const prettyRspExe = { req: { appCode: code, channel, subChannel: channel, ta: appLower }, rsp: rspExe }; + logger.info(`Fetched latestLauncher: v${rsp.version}, ${id}, ${app}`); + const basePath = ['akEndfield', 'launcher']; + const channelStr = String(channel); + const ignoreRules = [ + { path: ['rsp', 'zip_package_url'], pattern: /\?auth_key=.*$/ }, + { path: ['rsp', 'exe_url'], pattern: /\?auth_key=.*$/ }, + ]; + if (rsp.zip_package_url.includes('?auth_key')) await saveToWM(rsp.zip_package_url); + if (rspExe.exe_url.includes('?auth_key')) await saveToWM(rspExe.exe_url); + await saveResult([...basePath, 'launcher', app, channelStr], rsp.version, prettyRsp, true, ignoreRules); + await saveResult([...basePath, 'launcherExe', app, channelStr], rspExe.version, prettyRspExe, true, ignoreRules); + } } } async function mainCmdHandler() { + waybackCred = await (async () => { + logger.debug('Fetching credentials for Wayback Machine ...'); + const authTextData = await Bun.file('config/config_auth.txt').json(); + const result = await webArchiveOrg.login(authTextData[0], authTextData[1]); + logger.info('Logged in to Wayback Machine'); + return result; + })(); + const cfg = appConfig.network.api.akEndfield; const channelStr = String(cfg.channel.osWinRel); @@ -550,13 +695,15 @@ async function mainCmdHandler() { await fetchAndSaveLatestGames(cfg, gameTargets); await fetchAndSaveLatestGamePatches(cfg, gameTargets); await fetchAndSaveLatestGameResources(cfg, channelStr); - await fetchAndSaveLatestLauncher(cfg, channelStr); + await fetchAndSaveLatestLauncher(cfg); for (const target of gameTargets) { await generateGameListMd(target); await generatePatchListMd(target); } await generateResourceListMd(channelStr); + await generateLauncherMd('zip'); + await generateLauncherMd('exe'); } export default mainCmdHandler; diff --git a/src/types/api/akEndfield/Api.ts b/src/types/api/akEndfield/Api.ts index 47a0241..f0271bc 100644 --- a/src/types/api/akEndfield/Api.ts +++ b/src/types/api/akEndfield/Api.ts @@ -56,6 +56,14 @@ type LauncherLatestLauncher = { description: string; }; +type LauncherLatestLauncherExe = { + action: number; + version: string; // x.y.z + request_version: string; // x.y.z or blank + exe_url: string; + exe_size: string; +}; + type LauncherWebSidebar = { data_version: string; sidebars: { @@ -355,6 +363,7 @@ export type { LauncherLatestGame, LauncherLatestGameResources, LauncherLatestLauncher, + LauncherLatestLauncherExe, LauncherWebSidebar, LauncherWebSingleEnt, LauncherWebMainBgImage, diff --git a/src/utils/api/akEndfield/launcher.ts b/src/utils/api/akEndfield/launcher.ts index 742e186..7953f13 100644 --- a/src/utils/api/akEndfield/launcher.ts +++ b/src/utils/api/akEndfield/launcher.ts @@ -58,22 +58,54 @@ export default { channel: number, subChannel: number, version: string | null, - targetApp: 'EndField' | null, + targetApp: 'EndField' | 'Arknights' | 'Official', + region: 'os' | 'cn', ): Promise => { if (version !== null && !semver.valid(version)) throw new Error(`Invalid version string (${version})`); + const domain = + region === 'cn' + ? appConfig.network.api.akEndfield.base.launcherCN + : appConfig.network.api.akEndfield.base.launcher; const rsp = await ky - .get(`https://${appConfig.network.api.akEndfield.base.launcher}/launcher/get_latest`, { + .get(`https://${domain}/launcher/get_latest`, { ...defaultSettings.ky, searchParams: { appcode: appCode, channel, sub_channel: subChannel, version: version ?? undefined, - target_app: targetApp ?? undefined, + target_app: targetApp, }, }) .json(); return rsp as TypesApiAkEndfield.LauncherLatestLauncher; }, + latestLauncherExe: async ( + appCode: string, + channel: number, + subChannel: number, + version: string | null, + targetApp: 'endfield' | 'official' | string, + region: 'os' | 'cn', + ): Promise => { + if (version !== null && !semver.valid(version)) throw new Error(`Invalid version string (${version})`); + const domain = + region === 'cn' + ? appConfig.network.api.akEndfield.base.launcherCN + : appConfig.network.api.akEndfield.base.launcher; + const rsp = await ky + .get(`https://${domain}/launcher/get_latest_launcher`, { + ...defaultSettings.ky, + searchParams: { + appcode: appCode, + channel, + sub_channel: subChannel, + version: version ?? undefined, + ta: targetApp, + }, + }) + .json(); + return rsp as TypesApiAkEndfield.LauncherLatestLauncherExe; + }, web: launcherWeb, }; diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts index f636b4a..3be70b4 100644 --- a/src/utils/api/index.ts +++ b/src/utils/api/index.ts @@ -1,5 +1,7 @@ import akEndfield from './akEndfield/index.js'; +import webArchiveOrg from './webArchiveOrg/index.js'; export default { akEndfield, + webArchiveOrg, }; diff --git a/src/utils/api/webArchiveOrg/defaultSettings.ts b/src/utils/api/webArchiveOrg/defaultSettings.ts new file mode 100644 index 0000000..e9e8f5b --- /dev/null +++ b/src/utils/api/webArchiveOrg/defaultSettings.ts @@ -0,0 +1,11 @@ +import appConfig from '../../config.js'; + +export default { + ky: { + headers: { + 'User-Agent': appConfig.network.userAgent.chromeWindows, + }, + timeout: appConfig.network.timeout, + retry: { limit: appConfig.network.retryCount }, + }, +}; diff --git a/src/utils/api/webArchiveOrg/index.ts b/src/utils/api/webArchiveOrg/index.ts new file mode 100644 index 0000000..691b3ac --- /dev/null +++ b/src/utils/api/webArchiveOrg/index.ts @@ -0,0 +1,85 @@ +import cookie from 'cookie'; +import ky from 'ky'; +import defaultSettings from './defaultSettings.js'; + +export default { + login: { + getLoginToken: async (): Promise => { + const rsp: any = await ky.get('https://archive.org/services/account/login/', defaultSettings.ky).json(); + if (!rsp.value.token) throw new Error('Failed to get wayback machine login token'); + return rsp.value.token; + }, + login: async (username: string, password: string, token: string, remember: boolean = true) => { + const rsp = await ky.post('https://archive.org/services/account/login/', { + ...defaultSettings.ky, + json: { + username, + password, + remember: String(remember), + t: token, + }, + }); + if (!((await rsp.json()) as any).success) throw new Error('Wayback Machine login error: ' + rsp); + return rsp.headers.getSetCookie().map((e) => cookie.parseSetCookie(e)); + }, + }, + save: async (url: string, auth: { user: string; sig: string }): Promise => { + const params = new URLSearchParams(); + params.append('url', url); + params.append('capture_all', 'on'); + const rsp = await ky + .post('https://web.archive.org/save/' + url, { + ...defaultSettings.ky, + headers: { + ...defaultSettings.ky.headers, + Cookie: cookie.stringifyCookie({ 'logged-in-sig': auth.sig, 'logged-in-user': auth.user }), + }, + body: params, + }) + .text(); + const match = rsp.match(/spn\.watchJob\("([^"]+)"/); + if (match && match[1]) { + return match[1]; // spn2-xxxxxxxxxxxxxxxxxxx + } + throw new Error('Wayback Machine save job id not found'); + }, + saveStatus: async ( + jobId: string, + auth: { user: string; sig: string }, + ): Promise< + | { + status: 'success'; + job_id: string; + resources: []; + download_size: number; + total_size: number; + timestamp: string; + original_url: string; + duration_sec: number; + counters: { + outlinks: number; + embeds: number; + }; + http_status: number; + first_archive: boolean; + } + | { + status: 'pending'; + job_id: string; + resources: []; + download_size?: number; + total_size?: number; + } + > => { + const rsp = await ky + .get('https://web.archive.org/save/status/' + jobId, { + ...defaultSettings.ky, + headers: { + ...defaultSettings.ky.headers, + Cookie: cookie.stringifyCookie({ 'logged-in-sig': auth.sig, 'logged-in-user': auth.user }), + }, + }) + .json(); + return rsp as any; + }, +}; diff --git a/src/utils/config.ts b/src/utils/config.ts index b1849bc..19e7fb5 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -16,16 +16,17 @@ type ConfigType = AllRequired< api: { akEndfield: { appCode: { - game: { osWinRel: string }; - launcher: { osWinRel: string; osWinRelEpic: string }; + game: { osWinRel: string; cnWinRel: string }; + launcher: { osWinRel: string; osWinRelEpic: string; cnWinRel: string }; accountService: { osWinRel: string; skport: string; binding: string }; u8: { osWinRel: string }; }; - channel: { osWinRel: number }; - subChannel: { osWinRel: number; osWinRelEpic: number, osWinRelGooglePlay: number }; + channel: { osWinRel: number; cnWinRel: number }; + subChannel: { osWinRel: number; osWinRelEpic: number; osWinRelGooglePlay: number; cnWinRel: number }; base: { accountService: string; launcher: string; + launcherCN: string; u8: string; binding: string; webview: string; @@ -63,16 +64,17 @@ const initialConfig: ConfigType = { api: { akEndfield: { appCode: { - game: { osWinRel: 'YDUTE5gscDZ229CW' }, - launcher: { osWinRel: 'TiaytKBUIEdoEwRT', osWinRelEpic: 'BBWoqCzuZ2bZ1Dro' }, + game: { osWinRel: 'YDUTE5gscDZ229CW', cnWinRel: '6LL0KJuqHBVz33WK' }, + launcher: { osWinRel: 'TiaytKBUIEdoEwRT', osWinRelEpic: 'BBWoqCzuZ2bZ1Dro', cnWinRel: 'abYeZZ16BPluCFyT' }, accountService: { osWinRel: 'd9f6dbb6bbd6bb33', skport: '6eb76d4e13aa36e6', binding: '3dacefa138426cfe' }, u8: { osWinRel: '973bd727dd11cbb6ead8' }, }, - channel: { osWinRel: 6 }, - subChannel: { osWinRel: 6, osWinRelEpic: 801, osWinRelGooglePlay: 802 }, + channel: { osWinRel: 6, cnWinRel: 1 }, + subChannel: { osWinRel: 6, osWinRelEpic: 801, osWinRelGooglePlay: 802, cnWinRel: 1 }, base: { accountService: 'YXMuZ3J5cGhsaW5lLmNvbQ==', launcher: 'bGF1bmNoZXIuZ3J5cGhsaW5lLmNvbS9hcGk=', + launcherCN: 'bGF1bmNoZXIuaHlwZXJncnlwaC5jb20vYXBp', u8: 'dTguZ3J5cGhsaW5lLmNvbQ==', binding: 'YmluZGluZy1hcGktYWNjb3VudC1wcm9kLmdyeXBobGluZS5jb20=', webview: 'ZWYtd2Vidmlldy5ncnlwaGxpbmUuY29t', diff --git a/src/utils/webArchiveOrg.ts b/src/utils/webArchiveOrg.ts new file mode 100644 index 0000000..8f10ab4 --- /dev/null +++ b/src/utils/webArchiveOrg.ts @@ -0,0 +1,65 @@ +import path from 'node:path'; +import { HTTPError } from 'ky'; +import api from './api/index.js'; +import argvUtils from './argv.js'; +import logger from './logger.js'; +import mathUtils from './math.js'; + +function formatBytes(bytes: number) { + return mathUtils.formatFileSize(bytes, { + decimals: 2, + decimalPadding: true, + useBinaryUnit: true, + useBitUnit: false, + unitVisible: true, + unit: null, + }); +} + +async function login(username: string, password: string): Promise<{ user: string; sig: string }> { + const token = await api.webArchiveOrg.login.getLoginToken(); + const loginRet = await api.webArchiveOrg.login.login(username, password, token, true); + const credential = { + user: loginRet.find((e) => e.name === 'logged-in-user')?.value, + sig: loginRet.find((e) => e.name === 'logged-in-sig')?.value, + }; + if (credential.sig && credential.user) return credential as { user: string; sig: string }; + throw new Error('Wayback Machine auth error'); +} + +async function savePage(url: string, auth: { user: string; sig: string }): Promise { + const jobId = await api.webArchiveOrg.save(url, auth); + const result = await (async () => { + while (true) { + try { + const status = await api.webArchiveOrg.saveStatus(jobId, auth); + if (status.download_size && status.total_size) + logger.debug( + `Wayback Machine save: ${formatBytes(status.download_size)} / ${formatBytes(status.total_size)}`, + ); + if (status.status === 'success') return status; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } catch (err) { + if (err instanceof HTTPError) { + throw new Error( + `Wayback Machine save: HTTP ${err.response.status} ${err.response.statusText}: ${await err.response.text()}`, + ); + } + } + } + })(); + if (result.http_status >= 400) { + throw new Error('Wayback Machine save: http ' + result.http_status + ' error'); + } + const resultUrl = `https://web.archive.org/web/${result.timestamp}/${result.original_url}`; + const localJsonPath = path.join(argvUtils.getArgv()['outputDir'], 'wayback_machine.json'); + const localJson: string[] = await Bun.file(localJsonPath).json(); + if (localJson.includes(resultUrl) === false) localJson.push(resultUrl); + await Bun.write(localJsonPath, JSON.stringify(localJson, null, 2)); + return resultUrl; +} + +export default { + login, + savePage, +};