#include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; struct Args { std::string downloader; std::string url; std::string output; std::string proxy = "http://127.0.0.1:7897"; uint32_t retry = 2; uint64_t retry_interval = 5; }; // 执行命令并返回退出码 int execute_command(const std::string& program, const std::vector& args) { try { // 构建命令字符串用于显示 std::string cmd_str = program; for (const auto& arg : args) { cmd_str += " " + arg; } fmt::print("执行命令: {}\n", cmd_str); // 构建命令行参数数组 std::vector argv; argv.push_back(program.c_str()); for (const auto& arg : args) { argv.push_back(arg.c_str()); } argv.push_back(nullptr); // 执行命令 int result = std::system(cmd_str.c_str()); // Windows 上直接返回,Linux/macOS 需要使用宏提取退出码 #ifdef _WIN32 return result; #else return WEXITSTATUS(result); #endif } catch (const std::exception& e) { fmt::print(stderr, "执行命令失败: {}\n", e.what()); return -1; } } // 调用外部下载器 int call_downloader(const std::string& downloader_path, const std::string& url, const std::string& output_dir, const std::string& proxy, bool resume) { std::vector args = {"dl", "-u", url, "--proxy", proxy, "-d", output_dir, "--skip-same"}; if (resume) { args.push_back("--continue"); } return execute_command(downloader_path, args); } // 带重试的下载函数 int download_with_retry(const Args& args) { uint32_t max_attempts = args.retry + 1; uint32_t attempt = 1; while (true) { fmt::print("\n=== 尝试 {}/{} ===\n", attempt, max_attempts); int status = call_downloader(args.downloader, args.url, args.output, args.proxy, false); if (status == 0) { // 成功 fmt::print("✅ 第{}次尝试成功\n", attempt); return status; } else if (attempt < max_attempts) { // 失败但还可重试 fmt::print("❌ 第{}次尝试失败,{}秒后重试...\n", attempt, args.retry_interval); std::this_thread::sleep_for(std::chrono::seconds(args.retry_interval)); attempt++; } else { // 达到最大重试次数 fmt::print(stderr, "❌ 已达到最大重试次数({}次),下载失败\n", max_attempts); return status; } } } int main(int argc, char** argv) { Args args; CLI::App app{"简单的下载器包装工具"}; // 添加命令行参数 app.add_option("-d,--downloader", args.downloader, "外部下载器程序的路径")->required()->check(CLI::ExistingFile); app.add_option("-u,--url", args.url, "远程文件的URL")->required(); app.add_option("-o,--output", args.output, "本地存储目录")->required()->check(CLI::ExistingDirectory); // proxy 参数(有默认值的字符串参数) app.add_option("-p,--proxy", args.proxy, "代理")->default_val("http://127.0.0.1:7897"); // retry 参数(有默认值的数值参数) app.add_option("-r,--retry", args.retry, "重试次数")->default_val(2)->check(CLI::NonNegativeNumber); // retry-interval 参数(有默认值的数值参数) app.add_option("-i,--retry-interval", args.retry_interval, "重试间隔(秒)")->default_val(5)->check(CLI::NonNegativeNumber); try { app.parse(argc, argv); } catch (const CLI::ParseError& e) { return app.exit(e); } // 打印URL(无论成功失败都会在最后打印) fmt::print("下载URL: {}\n", args.url); // CLI11 已经做了基本的验证,但我们可以再次验证 if (!fs::exists(args.downloader)) { fmt::print(stderr, "错误: 下载器文件不存在: {}\n", args.downloader); return 1; } if (!fs::exists(args.output)) { fmt::print(stderr, "错误: 输出目录不存在: {}\n", args.output); return 1; } if (!fs::is_directory(args.output)) { fmt::print(stderr, "错误: 输出路径不是目录: {}\n", args.output); return 1; } fmt::print("开始下载...\n"); fmt::print(" 下载器: {}\n", args.downloader); fmt::print(" 远程URL: {}\n", args.url); fmt::print(" 输出目录: {}\n", args.output); fmt::print(" 代理: {}\n", args.proxy); fmt::print(" 重试次数: {}\n", args.retry); fmt::print(" 重试间隔: {}秒\n", args.retry_interval); // 调用外部下载器,带有重试逻辑 int status = download_with_retry(args); // 无论成功失败,都打印URL fmt::print("\n下载URL: {}\n", args.url); // 判断下载是否成功 if (status == 0) { fmt::print("✅ 下载成功!\n"); return 0; } else { fmt::print(stderr, "❌ 下载失败,退出码: {}\n", status); return 1; } }