下载非内核

This commit is contained in:
zhongluofeng
2026-07-22 18:26:26 +08:00
parent 1e31ee8da9
commit f7d3c13f35
24 changed files with 2609 additions and 2532 deletions
+177
View File
@@ -0,0 +1,177 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// 任务状态
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TaskStatus {
/// 排队等待(并发数已满)
Queued,
/// 下载中
Active,
/// 已暂停
Paused,
/// 已完成
Complete,
/// 错误
Error,
}
/// 下载分段(多线程 Range 下载 / 断点续传用)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Segment {
/// 分段索引
pub index: u32,
/// 起始字节(含)
pub start: u64,
/// 结束字节(含)
pub end: u64,
/// 已下载字节
pub completed: u64,
}
impl Segment {
/// 该分段总长度
pub fn len(&self) -> u64 {
self.end.saturating_sub(self.start) + 1
}
/// 是否已完成
pub fn is_done(&self) -> bool {
self.completed >= self.len()
}
}
/// 下载任务
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DownloadTask {
/// 任务 ID(自增 hex 字符串)
pub id: String,
/// 下载地址
pub url: String,
/// 文件名
pub filename: String,
/// 保存目录(绝对路径)
pub dir: String,
/// 状态
pub status: TaskStatus,
/// 文件总大小(字节),0=未知
pub total_size: u64,
/// 已下载字节
pub completed_size: u64,
/// 当前下载速度 bytes/s
pub speed: u64,
/// 服务器是否支持断点续传
pub supports_resume: bool,
/// 分段信息
#[serde(default)]
pub segments: Vec<Segment>,
/// 错误信息
#[serde(default)]
pub error: Option<String>,
/// 创建时间(Unix 时间戳,毫秒)
pub created_at: i64,
/// 自定义请求头(Cookie / Referer 等)
#[serde(default)]
pub headers: HashMap<String, String>,
}
impl DownloadTask {
/// 文件完整路径(最终文件名)
pub fn file_path(&self) -> std::path::PathBuf {
std::path::PathBuf::from(&self.dir).join(&self.filename)
}
/// 下载临时文件路径(下载未完成时使用,完成后重命名为 file_path)
pub fn temp_file_path(&self) -> std::path::PathBuf {
std::path::PathBuf::from(&self.dir).join(format!("{}.thingdl", self.filename))
}
/// 更新已下载总量(聚合所有分段)
pub fn recalc_completed(&mut self) {
if self.segments.is_empty() {
return;
}
self.completed_size = self.segments.iter().map(|s| s.completed).sum();
}
}
/// 下载设置
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DownloaderSettings {
/// 下载目录
#[serde(default = "default_download_dir")]
pub download_dir: String,
/// 最大同时下载数
#[serde(default = "default_max_concurrent")]
pub max_concurrent: u32,
/// 单任务最大连接数(多线程分段数)
#[serde(default = "default_max_connections")]
pub max_connections: u32,
/// 断点续传
#[serde(default = "default_true")]
pub continue_download: bool,
/// 全局速度限制 KB/s(0=不限)
#[serde(default)]
pub global_speed_limit: u64,
/// 扩展 HTTP API 端口
#[serde(default = "default_extension_port")]
pub extension_port: u16,
/// 扩展认证密钥(空=不认证)
#[serde(default)]
pub extension_secret: String,
/// 删除任务时是否同时删除已下载的文件
#[serde(default)]
pub delete_files_on_remove: bool,
}
fn default_max_concurrent() -> u32 {
5
}
fn default_max_connections() -> u32 {
8
}
fn default_true() -> bool {
true
}
fn default_extension_port() -> u16 {
16800
}
fn default_download_dir() -> String {
dirs::download_dir()
.or_else(|| {
std::env::var("USERPROFILE")
.ok()
.map(|p| std::path::PathBuf::from(p).join("Downloads"))
})
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| "downloads".to_string())
}
impl Default for DownloaderSettings {
fn default() -> Self {
Self {
download_dir: default_download_dir(),
max_concurrent: default_max_concurrent(),
max_connections: default_max_connections(),
continue_download: true,
global_speed_limit: 0,
extension_port: default_extension_port(),
extension_secret: String::new(),
delete_files_on_remove: false,
}
}
}
/// HEAD/Range 探测结果
pub struct ProbeResult {
/// 文件大小(字节),None=未知
pub total_size: Option<u64>,
/// 是否支持 Range 请求
pub supports_resume: bool,
/// 从 Content-Disposition 或 URL 推断的文件名
pub filename: Option<String>,
}