diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1bb4ce07..45ed8aaf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,23 +25,13 @@ jobs: node-version: 20.18.0 - name: Install dependencies - run: yarn + run: yarn --frozen-lockfile - name: Install Python uses: actions/setup-python@v5 with: python-version: 3.9 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: rustfmt - - - name: Build Rust - run: cargo build --release - working-directory: ./rust_rpc - - name: Install dependencies run: pip install -r requirements.txt diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0a17b329..0a20e469 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: node-version: 20.18.0 - name: Install dependencies - run: yarn + run: yarn --frozen-lockfile - name: Validate current commit (last commit) with commitlint run: npx commitlint --last --verbose diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 818d2d9c..51dcd506 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,23 +26,13 @@ jobs: node-version: 20.18.0 - name: Install dependencies - run: yarn + run: yarn --frozen-lockfile - name: Install Python uses: actions/setup-python@v5 with: python-version: 3.9 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: rustfmt - - - name: Build Rust - run: cargo build --release - working-directory: ./rust_rpc - - name: Install dependencies run: pip install -r requirements.txt diff --git a/.gitignore b/.gitignore index 0df3955a..eb0592e9 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,4 @@ hydra-python-rpc/ *storybook.log - -target/ +aria2/ diff --git a/binaries/7zz b/binaries/7zz old mode 100644 new mode 100755 diff --git a/binaries/7zzs b/binaries/7zzs old mode 100644 new mode 100755 diff --git a/binaries/hydra-httpdl.exe b/binaries/hydra-httpdl.exe deleted file mode 100644 index 7a686d9e..00000000 Binary files a/binaries/hydra-httpdl.exe and /dev/null differ diff --git a/electron-builder.yml b/electron-builder.yml index 74f5b6fa..dd10e81a 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -3,6 +3,7 @@ productName: Hydra directories: buildResources: build extraResources: + - aria2 - ludusavi - hydra-python-rpc - seeds @@ -22,7 +23,6 @@ win: extraResources: - from: binaries/7z.exe - from: binaries/7z.dll - - from: rust_rpc/target/release/hydra-httpdl.exe target: - nsis - portable @@ -40,7 +40,6 @@ mac: entitlementsInherit: build/entitlements.mac.plist extraResources: - from: binaries/7zz - - from: rust_rpc/target/release/hydra-httpdl extendInfo: - NSCameraUsageDescription: Application requests access to the device's camera. - NSMicrophoneUsageDescription: Application requests access to the device's microphone. @@ -52,7 +51,6 @@ dmg: linux: extraResources: - from: binaries/7zzs - - from: rust_rpc/target/release/hydra-httpdl target: - AppImage - snap diff --git a/package.json b/package.json index 1c860062..1e1ca933 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hydralauncher", - "version": "3.4.2", + "version": "3.4.3", "description": "Hydra", "main": "./out/main/index.js", "author": "Los Broxas", @@ -21,7 +21,7 @@ "typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false", "typecheck": "npm run typecheck:node && npm run typecheck:web", "start": "electron-vite preview", - "dev": "cargo build --manifest-path=rust_rpc/Cargo.toml && electron-vite dev", + "dev": "electron-vite dev", "build": "npm run typecheck && electron-vite build", "postinstall": "electron-builder install-app-deps && node ./scripts/postinstall.cjs", "build:unpack": "npm run build && electron-builder --dir", diff --git a/python_rpc/http_downloader.py b/python_rpc/http_downloader.py index b61688fc..71e4b57e 100644 --- a/python_rpc/http_downloader.py +++ b/python_rpc/http_downloader.py @@ -1,94 +1,48 @@ -import os -import subprocess -import json +import aria2p class HttpDownloader: - def __init__(self, hydra_httpdl_bin: str): - self.hydra_exe = hydra_httpdl_bin - self.process = None - self.last_status = None - - def start_download(self, url: str, save_path: str, header: str = None, allow_multiple_connections: bool = False, connections_limit: int = 1): - cmd = [self.hydra_exe] - - cmd.append(url) - - cmd.extend([ - "--chunk-size", "10", - "--buffer-size", "16", - "--force-download", - "--log", - "--silent" - ]) - - if header: - cmd.extend(["--header", header]) - - if allow_multiple_connections: - cmd.extend(["--connections", str(connections_limit)]) - else: - cmd.extend(["--connections", "1"]) - - print(f"running hydra-httpdl: {' '.join(cmd)}") - - try: - self.process = subprocess.Popen( - cmd, - cwd=save_path, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True + def __init__(self): + self.download = None + self.aria2 = aria2p.API( + aria2p.Client( + host="http://localhost", + port=6800, + secret="" ) - except Exception as e: - print(f"error running hydra-httpdl: {e}") - return None + ) + def start_download(self, url: str, save_path: str, header: str, out: str = None): + if self.download: + self.aria2.resume([self.download]) + else: + downloads = self.aria2.add(url, options={"header": header, "dir": save_path, "out": out}) + + self.download = downloads[0] + + def pause_download(self): + if self.download: + self.aria2.pause([self.download]) + + def cancel_download(self): + if self.download: + self.aria2.remove([self.download]) + self.download = None def get_download_status(self): - - if not self.process: + if self.download == None: return None - - try: - line = self.process.stdout.readline() - if line: - status = json.loads(line.strip()) - self.last_status = status - elif self.last_status: - status = self.last_status - else: - return None - - response = { - "status": "active", - "progress": status["progress"], - "downloadSpeed": status["speed_bps"], - "numPeers": 0, - "numSeeds": 0, - "bytesDownloaded": status["downloaded_bytes"], - "fileSize": status["total_bytes"], - "folderName": status["filename"] - } - - if status["progress"] == 1: - response["status"] = "complete" - - return response - - except Exception as e: - print(f"error getting download status: {e}") - return None - - - - def stop_download(self): - if self.process: - self.process.terminate() - self.process = None - self.last_status = None - - def pause_download(self): - self.stop_download() - - def cancel_download(self): - self.stop_download() + + download = self.aria2.get_download(self.download.gid) + + response = { + 'folderName': download.name, + 'fileSize': download.total_length, + 'progress': download.completed_length / download.total_length if download.total_length else 0, + 'downloadSpeed': download.download_speed, + 'numPeers': 0, + 'numSeeds': 0, + 'status': download.status, + 'bytesDownloaded': download.completed_length, + } + + return response diff --git a/python_rpc/main.py b/python_rpc/main.py index 864b4e50..8a64e7c3 100644 --- a/python_rpc/main.py +++ b/python_rpc/main.py @@ -13,7 +13,6 @@ http_port = sys.argv[2] rpc_password = sys.argv[3] start_download_payload = sys.argv[4] start_seeding_payload = sys.argv[5] -hydra_httpdl_bin = sys.argv[6] downloads = {} # This can be streamed down from Node @@ -33,10 +32,10 @@ if start_download_payload: except Exception as e: print("Error starting torrent download", e) else: - http_downloader = HttpDownloader(hydra_httpdl_bin) + http_downloader = HttpDownloader() downloads[initial_download['game_id']] = http_downloader try: - http_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'), initial_download.get('allow_multiple_connections', False), initial_download.get('connections_limit', 24)) + http_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'), initial_download.get('out')) except Exception as e: print("Error starting http download", e) @@ -148,11 +147,11 @@ def action(): torrent_downloader.start_download(url, data['save_path']) else: if existing_downloader and isinstance(existing_downloader, HttpDownloader): - existing_downloader.start_download(url, data['save_path'], data.get('header'), data.get('allow_multiple_connections', False), data.get('connections_limit', 24)) + existing_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out')) else: - http_downloader = HttpDownloader(hydra_httpdl_bin) + http_downloader = HttpDownloader() downloads[game_id] = http_downloader - http_downloader.start_download(url, data['save_path'], data.get('header'), data.get('allow_multiple_connections', False), data.get('connections_limit', 24)) + http_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out')) downloading_game_id = game_id @@ -183,4 +182,3 @@ def action(): if __name__ == "__main__": app.run(host="0.0.0.0", port=int(http_port)) - \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index bad56c4b..ffdfb59b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ pywin32; sys_platform == 'win32' psutil Pillow flask +aria2p diff --git a/rust_rpc/Cargo.lock b/rust_rpc/Cargo.lock deleted file mode 100644 index 2e14f7d9..00000000 --- a/rust_rpc/Cargo.lock +++ /dev/null @@ -1,2040 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" -dependencies = [ - "anstyle", - "once_cell", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" - -[[package]] -name = "async-trait" -version = "0.1.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bitflags" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "cc" -version = "1.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.5.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "console" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "unicode-width", - "windows-sys 0.59.0", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "h2" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hydra-httpdl" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "bitvec", - "bytes", - "clap", - "futures", - "indicatif", - "reqwest", - "serde_json", - "sha2", - "tokio", - "tokio-util", - "urlencoding", -] - -[[package]] -name = "hyper" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "libc", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "indicatif" -version = "0.17.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" -dependencies = [ - "console", - "number_prefix", - "portable-atomic", - "unicode-width", - "web-time", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" - -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - -[[package]] -name = "litemap" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" -dependencies = [ - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", -] - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "openssl" -version = "0.10.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "portable-atomic" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" - -[[package]] -name = "proc-macro2" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "redox_syscall" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" -dependencies = [ - "bitflags", -] - -[[package]] -name = "reqwest" -version = "0.12.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "windows-registry", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustix" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustls" -version = "0.23.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" - -[[package]] -name = "rustls-webpki" -version = "0.103.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" - -[[package]] -name = "socket2" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" -dependencies = [ - "fastrand", - "getrandom 0.3.2", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tokio" -version = "1.44.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.53.0", -] - -[[package]] -name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/rust_rpc/Cargo.toml b/rust_rpc/Cargo.toml deleted file mode 100644 index fb8d4296..00000000 --- a/rust_rpc/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "hydra-httpdl" -version = "0.1.0" -edition = "2021" - -[dependencies] -tokio = { version = "1", features = ["full", "macros", "rt-multi-thread"] } -reqwest = { version = "0.12.5", features = ["stream"] } -futures = "0.3" -bytes = "1.4" -indicatif = "0.17" -anyhow = "1.0" -async-trait = "0.1" -tokio-util = { version = "0.7", features = ["io"] } -clap = { version = "4.4", features = ["derive"] } -urlencoding = "2.1" -serde_json = "1.0" -bitvec = "1.0" -sha2 = "0.10" -[profile.release] -opt-level = 3 -lto = "fat" -codegen-units = 1 -panic = "abort" -strip = true \ No newline at end of file diff --git a/rust_rpc/src/main.rs b/rust_rpc/src/main.rs deleted file mode 100644 index 884366a8..00000000 --- a/rust_rpc/src/main.rs +++ /dev/null @@ -1,966 +0,0 @@ -use anyhow::Result; -use bitvec::prelude::*; -use clap::Parser; -use futures::stream::{FuturesUnordered, StreamExt}; -use indicatif::{ProgressBar, ProgressStyle}; -use reqwest::{Client, StatusCode, Url}; -use serde_json::json; -use sha2::{Digest, Sha256}; -use std::fs::{File, OpenOptions}; -use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}; -use std::path::Path; -use std::sync::Arc; -use tokio::sync::Mutex; - -const DEFAULT_MAX_RETRIES: usize = 3; -const RETRY_BACKOFF_MS: u64 = 500; -const DEFAULT_OUTPUT_FILENAME: &str = "output.bin"; -const DEFAULT_CONNECTIONS: usize = 16; -const DEFAULT_CHUNK_SIZE_MB: usize = 5; -const DEFAULT_BUFFER_SIZE_MB: usize = 8; -const DEFAULT_VERBOSE: bool = false; -const DEFAULT_SILENT: bool = false; -const DEFAULT_LOG: bool = false; -const DEFAULT_FORCE_NEW: bool = false; -const DEFAULT_RESUME_ONLY: bool = false; -const DEFAULT_FORCE_DOWNLOAD: bool = false; -const HEADER_SIZE: usize = 4096; -const MAGIC_NUMBER: &[u8; 5] = b"HYDRA"; -const FORMAT_VERSION: u8 = 1; -const FINALIZE_BUFFER_SIZE: usize = 1024 * 1024; - -#[derive(Parser)] -#[command(name = "hydra-httpdl")] -#[command(author = "los-broxas")] -#[command(version = "0.2.0")] -#[command(about = "high speed and low resource usage http downloader with resume capability", long_about = None)] -struct CliArgs { - /// file url to download - #[arg(required = true)] - url: String, - - /// output file path (or directory to save with original filename) - #[arg(default_value = DEFAULT_OUTPUT_FILENAME)] - output: String, - - /// number of concurrent connections for parallel download - #[arg(short = 'c', long, default_value_t = DEFAULT_CONNECTIONS)] - connections: usize, - - /// chunk size in MB for each connection - #[arg(short = 'k', long, default_value_t = DEFAULT_CHUNK_SIZE_MB)] - chunk_size: usize, - - /// buffer size in MB for file writing - #[arg(short, long, default_value_t = DEFAULT_BUFFER_SIZE_MB)] - buffer_size: usize, - - /// show detailed progress information - #[arg(short = 'v', long, default_value_t = DEFAULT_VERBOSE)] - verbose: bool, - - /// suppress progress bar - #[arg(short = 's', long, default_value_t = DEFAULT_SILENT)] - silent: bool, - - /// log download statistics in JSON format every second - #[arg(short = 'l', long, default_value_t = DEFAULT_LOG)] - log: bool, - - /// force new download, ignore existing partial files - #[arg(short = 'f', long, default_value_t = DEFAULT_FORCE_NEW)] - force_new: bool, - - /// only resume existing download, exit if no partial file exists - #[arg(short = 'r', long, default_value_t = DEFAULT_RESUME_ONLY)] - resume_only: bool, - - /// force download, ignore some verification checks - #[arg(short = 'F', long, default_value_t = DEFAULT_FORCE_DOWNLOAD)] - force_download: bool, - - /// HTTP headers to send with request (format: "Key: Value") - #[arg(short = 'H', long)] - header: Vec, -} - -struct DownloadConfig { - url: String, - output_path: String, - num_connections: usize, - chunk_size: usize, - buffer_size: usize, - verbose: bool, - silent: bool, - log: bool, - force_new: bool, - resume_only: bool, - headers: Vec, - force_download: bool, -} - -impl DownloadConfig { - fn should_log(&self) -> bool { - self.verbose && !self.silent - } - - fn should_log_stats(&self) -> bool { - self.log - } -} - -struct DownloadStats { - progress_percent: f64, - bytes_downloaded: u64, - total_size: u64, - speed_bytes_per_sec: f64, - eta_seconds: u64, - elapsed_seconds: u64, -} - -struct HydraHeader { - magic: [u8; 5], // "HYDRA" identifier - version: u8, // header version - file_size: u64, // file size - etag: [u8; 32], // etag hash - url_hash: [u8; 32], // url hash - chunk_size: u32, // chunk size - chunk_count: u32, // chunk count - chunks_bitmap: BitVec, // chunks bitmap -} - -impl HydraHeader { - fn new(file_size: u64, etag: &str, url: &str, chunk_size: u32) -> Self { - let chunk_count = ((file_size as f64) / (chunk_size as f64)).ceil() as u32; - let chunks_bitmap = bitvec![u8, Lsb0; 0; chunk_count as usize]; - - let mut etag_hash = [0u8; 32]; - let etag_digest = Sha256::digest(etag.as_bytes()); - etag_hash.copy_from_slice(&etag_digest[..]); - - let mut url_hash = [0u8; 32]; - let url_digest = Sha256::digest(url.as_bytes()); - url_hash.copy_from_slice(&url_digest[..]); - - Self { - magic: *MAGIC_NUMBER, - version: FORMAT_VERSION, - file_size, - etag: etag_hash, - url_hash, - chunk_size, - chunk_count, - chunks_bitmap, - } - } - - fn write_to_file(&self, writer: &mut W) -> Result<()> { - writer.write_all(&self.magic)?; - writer.write_all(&[self.version])?; - writer.write_all(&self.file_size.to_le_bytes())?; - writer.write_all(&self.etag)?; - writer.write_all(&self.url_hash)?; - writer.write_all(&self.chunk_size.to_le_bytes())?; - writer.write_all(&self.chunk_count.to_le_bytes())?; - - let bitmap_bytes = self.chunks_bitmap.as_raw_slice(); - writer.write_all(bitmap_bytes)?; - - let header_size = 5 + 1 + 8 + 32 + 32 + 4 + 4 + bitmap_bytes.len(); - let padding_size = HEADER_SIZE - header_size; - let padding = vec![0u8; padding_size]; - writer.write_all(&padding)?; - - Ok(()) - } - - fn read_from_file(reader: &mut R) -> Result { - let mut magic = [0u8; 5]; - reader.read_exact(&mut magic)?; - - if magic != *MAGIC_NUMBER { - anyhow::bail!("Not a valid Hydra download file"); - } - - let mut version = [0u8; 1]; - reader.read_exact(&mut version)?; - - if version[0] != FORMAT_VERSION { - anyhow::bail!("Incompatible format version"); - } - - let mut file_size_bytes = [0u8; 8]; - reader.read_exact(&mut file_size_bytes)?; - let file_size = u64::from_le_bytes(file_size_bytes); - - let mut etag = [0u8; 32]; - reader.read_exact(&mut etag)?; - - let mut url_hash = [0u8; 32]; - reader.read_exact(&mut url_hash)?; - - let mut chunk_size_bytes = [0u8; 4]; - reader.read_exact(&mut chunk_size_bytes)?; - let chunk_size = u32::from_le_bytes(chunk_size_bytes); - - let mut chunk_count_bytes = [0u8; 4]; - reader.read_exact(&mut chunk_count_bytes)?; - let chunk_count = u32::from_le_bytes(chunk_count_bytes); - - let bitmap_bytes_len = (chunk_count as usize + 7) / 8; - let mut bitmap_bytes = vec![0u8; bitmap_bytes_len]; - reader.read_exact(&mut bitmap_bytes)?; - - let chunks_bitmap = BitVec::::from_vec(bitmap_bytes); - - reader.seek(SeekFrom::Start(HEADER_SIZE as u64))?; - - Ok(Self { - magic, - version: version[0], - file_size, - etag, - url_hash, - chunk_size, - chunk_count, - chunks_bitmap, - }) - } - - fn set_chunk_complete(&mut self, chunk_index: usize) -> Result<()> { - if chunk_index >= self.chunk_count as usize { - anyhow::bail!("Chunk index out of bounds"); - } - - self.chunks_bitmap.set(chunk_index, true); - Ok(()) - } - - fn is_chunk_complete(&self, chunk_index: usize) -> bool { - if chunk_index >= self.chunk_count as usize { - return false; - } - - self.chunks_bitmap[chunk_index] - } - - fn get_incomplete_chunks(&self) -> Vec<(u64, u64)> { - let incomplete_count = self.chunk_count as usize - self.chunks_bitmap.count_ones(); - let mut chunks = Vec::with_capacity(incomplete_count); - let chunk_size = self.chunk_size as u64; - - for i in 0..self.chunk_count as usize { - if !self.is_chunk_complete(i) { - let start = i as u64 * chunk_size; - let end = std::cmp::min((i as u64 + 1) * chunk_size - 1, self.file_size - 1); - chunks.push((start, end)); - } - } - - chunks - } - - fn is_download_complete(&self) -> bool { - self.chunks_bitmap.count_ones() == self.chunk_count as usize - } -} - -struct ProgressTracker { - bar: Option, -} - -impl ProgressTracker { - fn new(file_size: u64, silent: bool, enable_stats: bool) -> Result { - let bar = if !silent || enable_stats { - let pb = ProgressBar::new(file_size); - pb.set_style( - ProgressStyle::default_bar() - .template("[{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")? - ); - if silent { - pb.set_draw_target(indicatif::ProgressDrawTarget::hidden()); - } - Some(pb) - } else { - None - }; - - Ok(Self { bar }) - } - - fn increment(&self, amount: u64) { - if let Some(pb) = &self.bar { - pb.inc(amount); - } - } - - fn finish(&self) { - if let Some(pb) = &self.bar { - pb.finish_with_message("Download complete"); - } - } - - fn get_stats(&self) -> Option { - if let Some(pb) = &self.bar { - let position = pb.position(); - let total = pb.length().unwrap_or(1); - - Some(DownloadStats { - progress_percent: position as f64 / total as f64, - bytes_downloaded: position, - total_size: total, - speed_bytes_per_sec: pb.per_sec(), - eta_seconds: pb.eta().as_secs(), - elapsed_seconds: pb.elapsed().as_secs(), - }) - } else { - None - } - } -} - -struct Downloader { - client: Client, - config: DownloadConfig, -} - -impl Downloader { - async fn download(&self) -> Result<()> { - let (file_size, filename, etag) = self.get_file_info().await?; - let output_path = self.determine_output_path(filename); - - if self.config.should_log() { - println!("Detected filename: {}", output_path); - } - - let resume_manager = ResumeManager::try_from_file( - &output_path, - file_size, - &etag, - &self.config.url, - self.config.chunk_size as u32, - self.config.force_new, - self.config.resume_only, - )?; - - let file = self.prepare_output_file(&output_path, file_size)?; - let progress = ProgressTracker::new(file_size, self.config.silent, self.config.log)?; - - let chunks = if resume_manager.is_download_complete() { - if self.config.should_log() { - println!("File is already fully downloaded, finalizing..."); - } - resume_manager.finalize_download()?; - return Ok(()); - } else { - let completed_chunks = resume_manager.header.chunks_bitmap.count_ones() as u32; - let total_chunks = resume_manager.header.chunk_count; - - if completed_chunks > 0 { - if self.config.should_log() { - let percent_done = (completed_chunks as f64 / total_chunks as f64) * 100.0; - println!("Resuming download: {:.1}% already downloaded", percent_done); - } - - if let Some(pb) = &progress.bar { - let downloaded = file_size * completed_chunks as u64 / total_chunks as u64; - pb.inc(downloaded); - } - } - - resume_manager.get_incomplete_chunks() - }; - - if self.config.should_log() { - println!( - "Downloading {} chunks of total {}", - chunks.len(), - resume_manager.header.chunk_count - ); - } - - self.process_chunks_with_resume( - chunks, - file, - file_size, - progress, - output_path.clone(), - resume_manager, - ) - .await?; - - Ok(()) - } - - fn determine_output_path(&self, filename: Option) -> String { - if Path::new(&self.config.output_path) - .file_name() - .unwrap_or_default() - == DEFAULT_OUTPUT_FILENAME - && filename.is_some() - { - filename.unwrap() - } else { - self.config.output_path.clone() - } - } - - fn prepare_output_file(&self, path: &str, size: u64) -> Result>>> { - let file = if Path::new(path).exists() { - OpenOptions::new().read(true).write(true).open(path)? - } else { - let file = File::create(path)?; - file.set_len(HEADER_SIZE as u64 + size)?; - file - }; - - Ok(Arc::new(Mutex::new(BufWriter::with_capacity( - self.config.buffer_size, - file, - )))) - } - - async fn process_chunks_with_resume( - &self, - chunks: Vec<(u64, u64)>, - file: Arc>>, - _file_size: u64, - progress: ProgressTracker, - real_filename: String, - resume_manager: ResumeManager, - ) -> Result<()> { - let mut tasks = FuturesUnordered::new(); - - let log_progress = if self.config.should_log_stats() { - let progress_clone = progress.bar.clone(); - let filename = real_filename.clone(); - - let (log_cancel_tx, mut log_cancel_rx) = tokio::sync::oneshot::channel(); - - let log_task = tokio::spawn(async move { - let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1)); - let tracker = ProgressTracker { - bar: progress_clone, - }; - - loop { - tokio::select! { - _ = interval.tick() => { - if let Some(stats) = tracker.get_stats() { - let json_output = json!({ - "progress": stats.progress_percent, - "speed_bps": stats.speed_bytes_per_sec, - "downloaded_bytes": stats.bytes_downloaded, - "total_bytes": stats.total_size, - "eta_seconds": stats.eta_seconds, - "elapsed_seconds": stats.elapsed_seconds, - "filename": filename - }); - println!("{}", json_output); - } - } - _ = &mut log_cancel_rx => { - break; - } - } - } - }); - Some((log_task, log_cancel_tx)) - } else { - None - }; - - let resume_manager = Arc::new(Mutex::new(resume_manager)); - - for (start, end) in chunks { - let client = self.client.clone(); - let url = self.config.url.clone(); - let file_clone = Arc::clone(&file); - let pb_clone = progress.bar.clone(); - let manager_clone = Arc::clone(&resume_manager); - let headers = self.config.headers.clone(); - let force_download = self.config.force_download; - let should_log = self.config.should_log(); - - let chunk_size = self.config.chunk_size as u64; - let chunk_index = (start / chunk_size) as usize; - - tasks.push(tokio::spawn(async move { - let result = Self::download_chunk_with_retry( - client, - url, - start, - end, - file_clone, - pb_clone, - DEFAULT_MAX_RETRIES, - &headers, - force_download, - should_log, - ) - .await; - - if result.is_ok() { - let mut manager = manager_clone.lock().await; - manager.set_chunk_complete(chunk_index)?; - } - - result - })); - - if tasks.len() >= self.config.num_connections { - if let Some(result) = tasks.next().await { - result??; - } - } - } - - while let Some(result) = tasks.next().await { - result??; - } - - { - let mut writer = file.lock().await; - writer.flush()?; - } - - progress.finish(); - - if let Some((log_handle, log_cancel_tx)) = log_progress { - if self.config.should_log_stats() { - let json_output = json!({ - "progress": 1.0, - "speed_bps": 0.0, - "downloaded_bytes": _file_size, - "total_bytes": _file_size, - "eta_seconds": 0, - "elapsed_seconds": if let Some(pb) = &progress.bar { pb.elapsed().as_secs() } else { 0 }, - "filename": real_filename - }); - println!("{}", json_output); - } - - let _ = log_cancel_tx.send(()); - let _ = log_handle.await; - } - - let manager = resume_manager.lock().await; - if manager.is_download_complete() { - if self.config.should_log() { - println!("Download complete, finalizing file..."); - } - manager.finalize_download()?; - } - - Ok(()) - } - - async fn download_chunk_with_retry( - client: Client, - url: String, - start: u64, - end: u64, - file: Arc>>, - progress_bar: Option, - max_retries: usize, - headers: &[String], - force_download: bool, - should_log: bool, - ) -> Result<()> { - let mut retries = 0; - loop { - match Self::download_chunk( - client.clone(), - url.clone(), - start, - end, - file.clone(), - progress_bar.clone(), - headers, - force_download, - should_log, - ) - .await - { - Ok(_) => return Ok(()), - Err(e) => { - retries += 1; - if retries >= max_retries { - return Err(e); - } - tokio::time::sleep(tokio::time::Duration::from_millis( - RETRY_BACKOFF_MS * (2_u64.pow(retries as u32 - 1)), - )) - .await; - } - } - } - } - - async fn download_chunk( - client: Client, - url: String, - start: u64, - end: u64, - file: Arc>>, - progress_bar: Option, - headers: &[String], - force_download: bool, - should_log: bool, - ) -> Result<()> { - let mut req = client - .get(&url) - .header("Range", format!("bytes={}-{}", start, end)); - - for header in headers { - if let Some(idx) = header.find(':') { - let (name, value) = header.split_at(idx); - let value = value[1..].trim(); - req = req.header(name.trim(), value); - } - } - - let resp = req.send().await?; - - if resp.status() != StatusCode::PARTIAL_CONTENT && resp.status() != StatusCode::OK { - if !force_download { - anyhow::bail!("Server does not support Range requests"); - } else if should_log { - println!("Server does not support Range requests, ignoring..."); - } - } - - let mut stream = resp.bytes_stream(); - let mut position = start; - let mut total_bytes = 0; - let expected_bytes = end - start + 1; - - while let Some(chunk_result) = stream.next().await { - let chunk = chunk_result?; - let chunk_size = chunk.len() as u64; - - total_bytes += chunk_size; - if total_bytes > expected_bytes { - let remaining = expected_bytes - (total_bytes - chunk_size); - let mut writer = file.lock().await; - writer.seek(SeekFrom::Start(HEADER_SIZE as u64 + position))?; - writer.write_all(&chunk[..remaining as usize])?; - - let tracker = ProgressTracker { - bar: progress_bar.clone(), - }; - tracker.increment(remaining); - break; - } - - let mut writer = file.lock().await; - writer.seek(SeekFrom::Start(HEADER_SIZE as u64 + position))?; - writer.write_all(&chunk)?; - drop(writer); - - position += chunk_size; - let tracker = ProgressTracker { - bar: progress_bar.clone(), - }; - tracker.increment(chunk_size); - } - - Ok(()) - } - - async fn get_file_info(&self) -> Result<(u64, Option, String)> { - let mut req = self.client.head(&self.config.url); - - for header in &self.config.headers { - if let Some(idx) = header.find(':') { - let (name, value) = header.split_at(idx); - let value = value[1..].trim(); - req = req.header(name.trim(), value); - } - } - - let resp = req.send().await?; - - let accepts_ranges = resp - .headers() - .get("accept-ranges") - .and_then(|v| v.to_str().ok()) - .map(|v| v.contains("bytes")) - .unwrap_or(false); - - if !accepts_ranges { - let range_check = self - .client - .get(&self.config.url) - .header("Range", "bytes=0-0") - .send() - .await?; - - if range_check.status() != StatusCode::PARTIAL_CONTENT { - if !self.config.force_download { - anyhow::bail!( - "Server does not support Range requests, cannot continue with parallel download" - ); - } else if self.config.should_log() { - println!("Server does not support Range requests, ignoring..."); - } - } - } - - let file_size = if let Some(content_length) = resp.headers().get("content-length") { - content_length.to_str()?.parse()? - } else { - anyhow::bail!("Could not determine file size") - }; - - let etag = if let Some(etag_header) = resp.headers().get("etag") { - etag_header.to_str()?.to_string() - } else { - format!( - "no-etag-{}", - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() - ) - }; - - let filename = self.extract_filename_from_response(&resp); - - Ok((file_size, filename, etag)) - } - - fn extract_filename_from_response(&self, resp: &reqwest::Response) -> Option { - if let Some(disposition) = resp.headers().get("content-disposition") { - if let Ok(disposition_str) = disposition.to_str() { - if let Some(filename) = Self::parse_content_disposition(disposition_str) { - return Some(filename); - } - } - } - - Self::extract_filename_from_url(&self.config.url) - } - - fn parse_content_disposition(disposition: &str) -> Option { - if let Some(idx) = disposition.find("filename=") { - let start = idx + 9; - let mut end = disposition.len(); - - if disposition.as_bytes().get(start) == Some(&b'"') { - let quoted_name = &disposition[start + 1..]; - if let Some(quote_end) = quoted_name.find('"') { - return Some(quoted_name[..quote_end].to_string()); - } - } else { - if let Some(semicolon) = disposition[start..].find(';') { - end = start + semicolon; - } - return Some(disposition[start..end].to_string()); - } - } - None - } - - fn extract_filename_from_url(url: &str) -> Option { - if let Ok(parsed_url) = Url::parse(url) { - let path = parsed_url.path(); - if let Some(path_filename) = Path::new(path).file_name() { - if let Some(filename_str) = path_filename.to_str() { - if !filename_str.is_empty() { - if let Ok(decoded) = urlencoding::decode(filename_str) { - return Some(decoded.to_string()); - } - } - } - } - } - None - } -} - -struct ResumeManager { - header: HydraHeader, - file_path: String, -} - -impl ResumeManager { - fn try_from_file( - path: &str, - file_size: u64, - etag: &str, - url: &str, - chunk_size: u32, - force_new: bool, - resume_only: bool, - ) -> Result { - if force_new { - if Path::new(path).exists() { - std::fs::remove_file(path)?; - } - - return Self::create_new_file(path, file_size, etag, url, chunk_size); - } - - if let Ok(file) = File::open(path) { - let mut reader = BufReader::new(file); - match HydraHeader::read_from_file(&mut reader) { - Ok(header) => { - let current_url_hash = Sha256::digest(url.as_bytes()); - - let url_matches = header.url_hash == current_url_hash.as_slice(); - let size_matches = header.file_size == file_size; - - if url_matches && size_matches { - return Ok(Self { - header, - file_path: path.to_string(), - }); - } - - if resume_only { - anyhow::bail!( - "Existing file is not compatible and resume_only option is active" - ); - } - - std::fs::remove_file(path)?; - } - Err(e) => { - if resume_only { - return Err(anyhow::anyhow!("Could not read file to resume: {}", e)); - } - - std::fs::remove_file(path)?; - } - } - } else if resume_only { - anyhow::bail!("File not found and resume_only option is active"); - } - - Self::create_new_file(path, file_size, etag, url, chunk_size) - } - - fn create_new_file( - path: &str, - file_size: u64, - etag: &str, - url: &str, - chunk_size: u32, - ) -> Result { - let header = HydraHeader::new(file_size, etag, url, chunk_size); - let file = File::create(path)?; - file.set_len(HEADER_SIZE as u64 + file_size)?; - - let mut writer = BufWriter::new(file); - header.write_to_file(&mut writer)?; - writer.flush()?; - - Ok(Self { - header, - file_path: path.to_string(), - }) - } - - fn get_incomplete_chunks(&self) -> Vec<(u64, u64)> { - self.header.get_incomplete_chunks() - } - - fn set_chunk_complete(&mut self, chunk_index: usize) -> Result<()> { - self.header.set_chunk_complete(chunk_index)?; - - let file = OpenOptions::new().write(true).open(&self.file_path)?; - let mut writer = BufWriter::new(file); - - let bitmap_offset = 5 + 1 + 8 + 32 + 32 + 4 + 4; - writer.seek(SeekFrom::Start(bitmap_offset as u64))?; - - let bitmap_bytes = self.header.chunks_bitmap.as_raw_slice(); - writer.write_all(bitmap_bytes)?; - writer.flush()?; - - Ok(()) - } - - fn is_download_complete(&self) -> bool { - self.header.is_download_complete() - } - - fn finalize_download(&self) -> Result<()> { - if !self.is_download_complete() { - anyhow::bail!("Download is not complete"); - } - - let temp_path = format!("{}.tmp", self.file_path); - let source = File::open(&self.file_path)?; - let dest = File::create(&temp_path)?; - - let mut reader = BufReader::with_capacity(FINALIZE_BUFFER_SIZE, source); - let mut writer = BufWriter::with_capacity(FINALIZE_BUFFER_SIZE, dest); - - reader.seek(SeekFrom::Start(HEADER_SIZE as u64))?; - - std::io::copy(&mut reader, &mut writer)?; - writer.flush()?; - drop(writer); - - match std::fs::rename(&temp_path, &self.file_path) { - Ok(_) => Ok(()), - Err(_) => { - let _ = std::fs::remove_file(&self.file_path); - std::fs::rename(&temp_path, &self.file_path)?; - Ok(()) - } - } - } -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = CliArgs::parse(); - - let config = DownloadConfig { - url: args.url.clone(), - output_path: args.output, - num_connections: args.connections, - chunk_size: args.chunk_size * 1024 * 1024, - buffer_size: args.buffer_size * 1024 * 1024, - verbose: args.verbose, - silent: args.silent, - log: args.log, - force_new: args.force_new, - resume_only: args.resume_only, - headers: args.header, - force_download: args.force_download, - }; - - if config.force_new && config.resume_only { - eprintln!("Error: --force-new and --resume-only options cannot be used together"); - std::process::exit(1); - } - - let downloader = Downloader { - client: Client::new(), - config, - }; - - if downloader.config.should_log() { - println!( - "Starting download with {} connections, chunk size: {}MB, buffer: {}MB", - downloader.config.num_connections, args.chunk_size, args.buffer_size - ); - println!("URL: {}", args.url); - - if downloader.config.force_new { - println!("Forcing new download, ignoring existing files"); - } else if downloader.config.resume_only { - println!("Only resuming existing download"); - } else { - println!("Resuming download if possible"); - } - } - - downloader.download().await?; - - Ok(()) -} diff --git a/scripts/postinstall.cjs b/scripts/postinstall.cjs index b7099027..fc3f69dd 100644 --- a/scripts/postinstall.cjs +++ b/scripts/postinstall.cjs @@ -2,6 +2,7 @@ const { default: axios } = require("axios"); const util = require("node:util"); const fs = require("node:fs"); const path = require("node:path"); +const { spawnSync } = require("node:child_process"); const exec = util.promisify(require("node:child_process").exec); @@ -46,11 +47,79 @@ const downloadLudusavi = async () => { }); }; +const downloadAria2WindowsAndLinux = async () => { + const file = + process.platform === "win32" + ? "aria2-1.37.0-win-64bit-build1.zip" + : "aria2-1.37.0-1-x86_64.pkg.tar.zst"; + + const downloadUrl = + process.platform === "win32" + ? `https://github.com/aria2/aria2/releases/download/release-1.37.0/${file}` + : "https://archlinux.org/packages/extra/x86_64/aria2/download/"; + + console.log(`Downloading ${file}...`); + + const response = await axios.get(downloadUrl, { responseType: "stream" }); + + const stream = response.data.pipe(fs.createWriteStream(file)); + + stream.on("finish", async () => { + console.log(`Downloaded ${file}, extracting...`); + + if (process.platform === "win32") { + await exec(`npx extract-zip ${file}`); + console.log("Extracted. Renaming folder..."); + + fs.mkdirSync("aria2"); + fs.copyFileSync( + path.join(file.replace(".zip", ""), "aria2c.exe"), + "aria2/aria2c.exe" + ); + fs.rmSync(file.replace(".zip", ""), { recursive: true }); + } else { + await exec(`tar --zstd -xvf ${file} usr/bin/aria2c`); + console.log("Extracted. Copying binary file..."); + fs.mkdirSync("aria2"); + fs.copyFileSync("usr/bin/aria2c", "aria2/aria2c"); + fs.rmSync("usr", { recursive: true }); + } + + console.log(`Extracted ${file}, removing compressed downloaded file...`); + fs.rmSync(file); + }); +}; + +const copyAria2Macos = async () => { + console.log("Checking if aria2 is installed..."); + + const isAria2Installed = spawnSync("which", ["aria2c"]).status; + + if (isAria2Installed != 0) { + console.log("Please install aria2"); + console.log("brew install aria2"); + return; + } + + console.log("Copying aria2 binary..."); + fs.mkdirSync("aria2"); + await exec(`cp $(which aria2c) aria2/aria2c`); +}; + +const copyAria2 = () => { + const aria2Path = + process.platform === "win32" ? "aria2/aria2c.exe" : "aria2/aria2c"; + + if (fs.existsSync(aria2Path)) { + console.log("Aria2 already exists, skipping download..."); + return; + } + if (process.platform == "darwin") { + copyAria2Macos(); + } else { + downloadAria2WindowsAndLinux(); + } +}; + +copyAria2(); downloadLudusavi(); - -if (process.platform !== "win32") { - const binariesPath = path.join(__dirname, "..", "binaries"); - - fs.chmodSync(path.join(binariesPath, "7zz"), 0o755); - fs.chmodSync(path.join(binariesPath, "7zzs"), 0o755); -} diff --git a/src/locales/ar/translation.json b/src/locales/ar/translation.json index e1511d57..87463eef 100644 --- a/src/locales/ar/translation.json +++ b/src/locales/ar/translation.json @@ -189,7 +189,7 @@ "download_error_gofile_quota_exceeded": "لقد تجاوزت الحصة الشهرية لـ Gofile. يرجى الانتظار حتى إعادة تعيين الحصة.", "download_error_real_debrid_account_not_authorized": "حساب Real-Debrid الخاص بك غير مصرح له بإجراء تنزيلات جديدة. يرجى مراجعة إعدادات الحساب والمحاولة مرة أخرى.", "download_error_not_cached_in_real_debrid": "هذا التنزيل غير متوفر على Real-Debrid وجلب حالة التنزيل من Real-Debrid غير متاح حاليًا.", - "download_error_not_cached_in_torbox": "هذا التنزيل غير متوفر على Torbox وجلب حالة التنزيل من Torbox غير متاح حاليًا.", + "download_error_not_cached_in_torbox": "هذا التنزيل غير متوفر على TorBox وجلب حالة التنزيل من TorBox غير متاح حاليًا.", "game_removed_from_favorites": "تمت إزالة اللعبة من المفضلة", "game_added_to_favorites": "تمت إضافة اللعبة إلى المفضلة" }, @@ -330,7 +330,7 @@ "delete_theme_description": "سيؤدي هذا إلى حذف السمة {{theme}}", "cancel": "إلغاء", "appearance": "المظهر", - "enable_torbox": "تفعيل Torbox", + "enable_torbox": "تفعيل TorBox", "torbox_description": "TorBox هي خدمة seedbox متميزة تنافس أفضل الخوادم في السوق.", "torbox_account_linked": "تم ربط حساب TorBox", "real_debrid_account_linked": "تم ربط حساب Real-Debrid", diff --git a/src/locales/cs/translation.json b/src/locales/cs/translation.json index 3f478f95..f7dad801 100644 --- a/src/locales/cs/translation.json +++ b/src/locales/cs/translation.json @@ -194,7 +194,7 @@ "download_error_gofile_quota_exceeded": "Překročili jste vaši měsíční GoFile kvótu. Prosím vyčkejte na resetování kvóty.", "download_error_real_debrid_account_not_authorized": "Váš Real-Debrid účet není autorizován pro vytváření nových stahování. Prosím zkontrolujte nastavení vašeho účtu a zkuste to znovu.", "download_error_not_cached_in_real_debrid": "Toto stahování není dostupné na Real-Debrid a získávání informací o stahování z Real-Debrid není zatím dostupné.", - "download_error_not_cached_in_torbox": "Toto stahování není dostupné na Torbox a získávání informací o stahování z Torbox není zatím dostupné.", + "download_error_not_cached_in_torbox": "Toto stahování není dostupné na TorBox a získávání informací o stahování z TorBox není zatím dostupné.", "game_removed_from_favorites": "Hra odebrána z oblíbených", "game_added_to_favorites": "Hra přidána do oblíbených", "automatically_extract_downloaded_files": "Automaticky rozbalit stažené soubory" diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 8b3893b3..5dd0fe6b 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -194,7 +194,7 @@ "download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.", "download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.", "download_error_not_cached_in_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.", - "download_error_not_cached_in_torbox": "This download is not available on Torbox and polling download status from Torbox is not yet available.", + "download_error_not_cached_in_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", "game_removed_from_favorites": "Game removed from favorites", "game_added_to_favorites": "Game added to favorites", "automatically_extract_downloaded_files": "Automatically extract downloaded files" @@ -338,7 +338,7 @@ "delete_theme_description": "This will delete the theme {{theme}}", "cancel": "Cancel", "appearance": "Appearance", - "enable_torbox": "Enable Torbox", + "enable_torbox": "Enable TorBox", "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", "torbox_account_linked": "TorBox account linked", "create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 469ae45f..e69d999e 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -191,7 +191,7 @@ "download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de Gofile. Por favor espera a que se reinicie la cuota.", "download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nueva descargas. Por favor, revisa los ajustes de tu cuenta e intenta de nuevo.", "download_error_not_cached_in_real_debrid": "Esta descarga no está disponible en Real-Debrid y el estado de descarga del sondeo de Real-Debrid aún no está disponible.", - "download_error_not_cached_in_torbox": "Esta descarga no está disponible en Torbox y el estado de descarga del sondeo aún no está disponible.", + "download_error_not_cached_in_torbox": "Esta descarga no está disponible en TorBox y el estado de descarga del sondeo aún no está disponible.", "game_added_to_favorites": "Juego añadido a favoritos", "game_removed_from_favorites": "Juego removido de favoritos" }, @@ -326,7 +326,7 @@ "editor_tab_code": "Código", "editor_tab_info": "Info", "editor_tab_save": "Guardar", - "enable_torbox": "Habilitar Torbox", + "enable_torbox": "Habilitar TorBox", "error_importing_theme": "Error al importar el tema", "import_theme": "Importar tema", "import_theme_description": "Vas a importar el tema {{theme}} desde la tienda de temas", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 41c58ebf..983b404e 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -183,7 +183,7 @@ "download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde a cota resetar.", "download_error_real_debrid_account_not_authorized": "Sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique sua assinatura e tente novamente.", "download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", - "download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível.", + "download_error_not_cached_in_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", "game_removed_from_favorites": "Jogo removido dos favoritos", "game_added_to_favorites": "Jogo adicionado aos favoritos", "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados" @@ -325,7 +325,7 @@ "delete_theme_description": "Isso irá deletar o tema {{theme}}", "cancel": "Cancelar", "appearance": "Aparência", - "enable_torbox": "Habilitar Torbox", + "enable_torbox": "Habilitar TorBox", "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", "torbox_account_linked": "Conta do TorBox vinculada", "create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index 3649afd7..680d9408 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -176,7 +176,7 @@ "download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde o reset da cota.", "download_error_real_debrid_account_not_authorized": "A sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique a sua assinatura e tente novamente.", "download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", - "download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível.", + "download_error_not_cached_in_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", "game_removed_from_favorites": "Jogo removido dos favoritos", "game_added_to_favorites": "Jogo adicionado aos favoritos" }, @@ -321,7 +321,7 @@ "delete_theme_description": "Isto irá apagar o tema {{theme}}", "cancel": "Cancelar", "appearance": "Aparência", - "enable_torbox": "Ativar Torbox", + "enable_torbox": "Ativar TorBox", "torbox_description": "TorBox é um serviço de seedbox premium sendo um dos melhores servidores do mercado.", "torbox_account_linked": "Conta do TorBox associada", "real_debrid_account_linked": "Conta Real-Debrid associada", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index b4544367..f9e19be6 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -194,7 +194,7 @@ "download_error_gofile_quota_exceeded": "Вы превысили месячную квоту Gofile. Пожалуйста, подождите, пока квота не будет восстановлена.", "download_error_real_debrid_account_not_authorized": "Ваш аккаунт Real-Debrid не авторизован для осуществления новых загрузок. Пожалуйста, проверьте настройки учетной записи и повторите попытку.", "download_error_not_cached_in_real_debrid": "Эта загрузка недоступна на Real-Debrid, и получение статуса загрузки с Real-Debrid пока недоступно.", - "download_error_not_cached_in_torbox": "Эта загрузка недоступна на Torbox, и получить статус загрузки с Torbox пока невозможно.", + "download_error_not_cached_in_torbox": "Эта загрузка недоступна на TorBox, и получить статус загрузки с TorBox пока невозможно.", "game_added_to_favorites": "Игра добавлена в избранное", "game_removed_from_favorites": "Игра удалена из избранного", "automatically_extract_downloaded_files": "Автоматическая распаковка загруженных файлов" @@ -338,7 +338,7 @@ "delete_theme_description": "Это приведет к удалению темы {{theme}}", "cancel": "Отменить", "appearance": "Внешний вид", - "enable_torbox": "Включить Torbox", + "enable_torbox": "Включить TorBox", "torbox_description": "TorBox - это ваш премиум-сервис, конкурирующий даже с лучшими серверами на рынке.", "torbox_account_linked": "Аккаунт TorBox привязан", "real_debrid_account_linked": "Аккаунт Real-Debrid привязан", diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index f671ed06..5a153e6a 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -194,7 +194,7 @@ "download_error_gofile_quota_exceeded": "Gofile aylık kotanızı doldurdunuz. Kotanın yenilenmesini bekleyin.", "download_error_real_debrid_account_not_authorized": "Real-Debrid hesabınız yeni indirme işlemleri yapmak için yetkilendirilmemiş. Lütfen hesap ayarlarınızı kontrol edip tekrar deneyin.", "download_error_not_cached_in_real_debrid": "Bu indirme Real-Debrid üzerinde mevcut değil ve Real-Debrid'den indirme durumu henüz sorgulanamıyor.", - "download_error_not_cached_in_torbox": "Bu indirme Torbox'ta mevcut değil ve Torbox'tan indirme durumu henüz sorgulanamıyor.", + "download_error_not_cached_in_torbox": "Bu indirme TorBox'ta mevcut değil ve TorBox'tan indirme durumu henüz sorgulanamıyor.", "game_removed_from_favorites": "Oyun favorilerden silindi", "game_added_to_favorites": "Oyun favorilere eklendi", "automatically_extract_downloaded_files": "Yüklenmiş dosyaları otomatik olarak çıkart" @@ -338,7 +338,7 @@ "delete_theme_description": "Bu {{theme}} temasını silecektir", "cancel": "İptal", "appearance": "Görünüm", - "enable_torbox": "Torbox'u etkinleştir", + "enable_torbox": "TorBox'u etkinleştir", "torbox_description": "TorBox, piyasadaki en iyi sunucularla bile rekabet edebilen premium seedbox hizmetinizdir.", "torbox_account_linked": "TorBox hesabı bağlandı", "create_real_debrid_account": "Henüz bir Real-Debrid hesabınız yoksa buraya tıklayın", diff --git a/src/main/main.ts b/src/main/main.ts index b9a37b00..f47b01a8 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,4 +1,4 @@ -import { DownloadManager, Ludusavi, startMainLoop } from "./services"; +import { Aria2, DownloadManager, Ludusavi, startMainLoop } from "./services"; import { RealDebridClient } from "./services/download/real-debrid"; import { HydraApi } from "./services/hydra-api"; import { uploadGamesBatch } from "./services/library-sync"; @@ -20,6 +20,8 @@ export const loadState = async () => { await import("./events"); + Aria2.spawn(); + if (userPreferences?.realDebridApiToken) { RealDebridClient.authorize(userPreferences.realDebridApiToken); } diff --git a/src/main/services/index.ts b/src/main/services/index.ts index 30b502f5..164b7b8d 100644 --- a/src/main/services/index.ts +++ b/src/main/services/index.ts @@ -11,3 +11,4 @@ export * from "./cloud-sync"; export * from "./7zip"; export * from "./game-files-manager"; export * from "./common-redist-manager"; +export * from "./aria2"; diff --git a/src/renderer/src/assets/icons/torbox.webp b/src/renderer/src/assets/icons/torbox.webp deleted file mode 100644 index 68d68531..00000000 Binary files a/src/renderer/src/assets/icons/torbox.webp and /dev/null differ diff --git a/src/renderer/src/hooks/use-feature.ts b/src/renderer/src/hooks/use-feature.ts index d601eae6..d4727105 100644 --- a/src/renderer/src/hooks/use-feature.ts +++ b/src/renderer/src/hooks/use-feature.ts @@ -2,7 +2,7 @@ import { useEffect, useState, useCallback } from "react"; enum Feature { CheckDownloadWritePermission = "CHECK_DOWNLOAD_WRITE_PERMISSION", - Torbox = "TORBOX", + TorBox = "TORBOX", Nimbus = "NIMBUS", NimbusPreview = "NIMBUS_PREVIEW", } diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index fa0be02a..ff3c0156 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -32,8 +32,6 @@ import { XCircleIcon, } from "@primer/octicons-react"; -import torBoxLogo from "@renderer/assets/icons/torbox.webp"; - export interface DownloadGroupProps { library: LibraryGame[]; title: string; @@ -320,20 +318,7 @@ export function DownloadGroup({ />
- {game.download?.downloader === Downloader.TorBox ? ( - - TorBox - TorBox - - ) : ( - - {DOWNLOADER_NAME[game.download!.downloader]} - - )} + {DOWNLOADER_NAME[game.download!.downloader]}
diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx index c06f280b..f10e267e 100644 --- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx @@ -87,14 +87,14 @@ export function DownloadSettingsModal({ return Downloader.Hydra; } - if (availableDownloaders.includes(Downloader.TorBox)) { - return Downloader.TorBox; - } - if (availableDownloaders.includes(Downloader.RealDebrid)) { return Downloader.RealDebrid; } + if (availableDownloaders.includes(Downloader.TorBox)) { + return Downloader.TorBox; + } + return availableDownloaders[0]; }, [] diff --git a/src/renderer/src/pages/settings/settings-torbox.tsx b/src/renderer/src/pages/settings/settings-torbox.tsx index bbbb44ee..610dc942 100644 --- a/src/renderer/src/pages/settings/settings-torbox.tsx +++ b/src/renderer/src/pages/settings/settings-torbox.tsx @@ -16,7 +16,7 @@ const TORBOX_URL = torBoxReferralCode : "https://torbox.app"; const TORBOX_API_TOKEN_URL = "https://torbox.app/settings"; -export function SettingsTorbox() { +export function SettingsTorBox() { const userPreferences = useAppSelector( (state) => state.userPreferences.value ); diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index 391742b8..325c2e17 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -3,7 +3,6 @@ import { useTranslation } from "react-i18next"; import { SettingsRealDebrid } from "./settings-real-debrid"; import { SettingsGeneral } from "./settings-general"; import { SettingsBehavior } from "./settings-behavior"; -import torBoxLogo from "@renderer/assets/icons/torbox.webp"; import { SettingsDownloadSources } from "./settings-download-sources"; import { SettingsContextConsumer, @@ -14,7 +13,7 @@ import { useFeature, useUserDetails } from "@renderer/hooks"; import { useMemo } from "react"; import "./settings.scss"; import { SettingsAppearance } from "./aparence/settings-appearance"; -import { SettingsTorbox } from "./settings-torbox"; +import { SettingsTorBox } from "./settings-torbox"; export default function Settings() { const { t } = useTranslation("settings"); @@ -23,7 +22,7 @@ export default function Settings() { const { isFeatureEnabled, Feature } = useFeature(); - const isTorboxEnabled = isFeatureEnabled(Feature.Torbox); + const isTorBoxEnabled = isFeatureEnabled(Feature.TorBox); const categories = useMemo(() => { const categories = [ @@ -34,19 +33,10 @@ export default function Settings() { tabLabel: t("appearance"), contentTitle: t("appearance"), }, - ...(isTorboxEnabled + ...(isTorBoxEnabled ? [ { - tabLabel: ( - <> - TorBox{" "} - Torbox - - ), + tabLabel: "TorBox", contentTitle: "TorBox", }, ] @@ -60,7 +50,7 @@ export default function Settings() { { tabLabel: t("account"), contentTitle: t("account") }, ]; return categories; - }, [userDetails, t, isTorboxEnabled]); + }, [userDetails, t, isTorBoxEnabled]); return ( @@ -84,7 +74,7 @@ export default function Settings() { } if (currentCategoryIndex === 4) { - return ; + return ; } if (currentCategoryIndex === 5) { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 54827ce3..c9d62207 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -54,7 +54,7 @@ export enum AuthPage { export enum DownloadError { NotCachedInRealDebrid = "download_error_not_cached_in_real_debrid", - NotCachedInTorbox = "download_error_not_cached_in_torbox", + NotCachedInTorBox = "download_error_not_cached_in_torbox", GofileQuotaExceeded = "download_error_gofile_quota_exceeded", RealDebridAccountNotAuthorized = "download_error_real_debrid_account_not_authorized", NotCachedInHydra = "download_error_not_cached_in_hydra", diff --git a/src/types/download.types.ts b/src/types/download.types.ts index 6bc15fe0..004d8f27 100644 --- a/src/types/download.types.ts +++ b/src/types/download.types.ts @@ -22,7 +22,7 @@ export interface DownloadProgress { download: Download; } -/* Torbox */ +/* TorBox */ export interface TorBoxUser { id: number; email: string;