#!/usr/bin/env runts -- --allow-env --allow-import --allow-read --allow-write --allow-net // Reference: // - https://docs.deno.com/examples/command_line_arguments/ // - https://jsr.io/@chiba/wget/1.0.0/mod.ts import { parseArgs } from "jsr:@std/cli/parse-args"; import { clearLastLine, formatHumanTime, formatPercent, formatSize2, log, printLastLine, } from "https://hatter.ink/script/get/@1/deno-commons-mod.ts"; function getOutputFilename(filename: string): string { const original = filename; for (let i = 1; i < Number.MAX_SAFE_INTEGER; i++) { try { Deno.statSync(filename); // deno-lint-ignore no-explicit-any } catch (e: any) { if (e && e.name === "NotFound") { break; } throw e; } filename = `${original}.${i}`; } return filename; } function getFilenameFromUrl(url: string): string { const array = new URL(url).pathname.split("/"); let filename = array[array.length - 1].trim(); if (!filename) { filename = "index.html"; } try { return decodeURIComponent(filename); } catch (e) { return filename; } } function getOutputFilenameFromUrl(url: string): string { return getOutputFilename(getFilenameFromUrl(url)); } function getEnvironmentProxy(): string | undefined { return Deno.env.get("ALL_PROXY") || Deno.env.get("HTTPS_PROXY") || Deno.env.get("HTTP_PROXY"); } const args = Deno.args; const flags = parseArgs(Deno.args, { boolean: ["help", "no-proxy"], string: ["proxy", "output"], }); if (args.length === 0) { log.info(`wget.ts --help for help`); Deno.exit(1); } if (flags.help) { console.log(`wget.ts - download file wget.ts [--proxy socks5h://ip:port] [--no-proxy] [--output filename] `); Deno.exit(0); } if (flags._.length === 0) { log.error("Require URL."); Deno.exit(1); } const url = flags._[0] as string; const outputFilename = flags.output || getOutputFilenameFromUrl(url); const startTime = new Date().getTime(); let totalLength: number = -2; log.info(`Start download file: ${url} --> ${outputFilename}`); const stateFileInterVal = setInterval(async () => { const costTime = new Date().getTime() - startTime; if (totalLength === -2) { await printLastLine( `Waiting download, time: ${formatHumanTime(costTime)}`, ); return; } const fileInfo = await Deno.stat(outputFilename); const fileSize = fileInfo.size; const speed = Math.floor(fileSize / (costTime / 1000)); await printLastLine( `Downloading file, total: ${formatSize2(totalLength)}, size: ${ formatSize2(fileSize) }, percent: ${formatPercent(fileSize, totalLength)}, speed: ${ formatSize2(speed) }, time: ${formatHumanTime(costTime)}`, ); }, 1000); const outputFileWritable = Deno.createSync(outputFilename).writable; let init = undefined; const noProxy = flags["no-proxy"]; const proxy = flags.proxy || getEnvironmentProxy(); if (proxy && !noProxy) { log.info(`Using proxy: ${proxy}`); init = { client: Deno.createHttpClient({ proxy: { url: proxy, }, }), }; } const response = await fetch(url, init); const contentLength = response.headers.get("content-length"); totalLength = -1; if (contentLength !== null) { try { totalLength = parseInt(contentLength, 10); } catch (_e) { // IGNORE } } await response.body?.pipeTo(outputFileWritable); clearInterval(stateFileInterVal); await clearLastLine(); const costTime = new Date().getTime() - startTime; const fileInfo = await Deno.stat(outputFilename); const fileSize = fileInfo.size; log.success( `Download finished, size: ${formatSize2(fileSize)}, time: ${ formatHumanTime(costTime) }`, ); // @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250323T215914+08:00.MEUCIQDMzVu+oh/6ES4XhxQX // sIMB7e2ApNkqkROUqf10xgZqVQIgdSARi8Wg4WDWHVL2YB8WsMYZEJKafSTSt5Za+hy7dcY=