下载非内核
This commit is contained in:
@@ -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>,
|
||||
}
|
||||
Reference in New Issue
Block a user