添加记录上次关闭窗口大小/默认窗口大小

spark-store-private/spark-store#9
This commit is contained in:
柚子
2025-03-07 15:08:45 +08:00
parent e0ad5b62df
commit d440de22c7
25 changed files with 427 additions and 325 deletions

View File

@@ -1,14 +1,16 @@
use crate::handlers::server::get_json_server_url;
use crate::models::download::{
DownloadTask, DownloadTaskResponse, InstallStatus, InstallTask, ResponseStatus,
};
use crate::utils::{aria2::Aria2Client, UA};
use std::collections::HashMap;
use std::sync::Mutex;
use std::net::TcpListener;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::net::TcpListener;
use crate::models::download::{DownloadTask, DownloadTaskResponse, InstallStatus, InstallTask, ResponseStatus};
use crate::handlers::server::get_json_server_url;
use crate::utils::{UA, aria2::Aria2Client};
use std::sync::Mutex;
use super::format::{format_size, format_speed, format_icon_url};
use super::format::{format_icon_url, format_size, format_speed};
pub struct DownloadManager {
download_queue: Mutex<HashMap<String, DownloadTask>>,
@@ -28,7 +30,7 @@ impl DownloadManager {
aria2_started: Arc::new(AtomicBool::new(false)),
aria2_port: Arc::new(Mutex::new(5144)),
aria2_pid: Arc::new(Mutex::new(None)),
installing: Arc::new(AtomicBool::new(false)), // 初始化为 false
installing: Arc::new(AtomicBool::new(false)), // 初始化为 false
last_get_downloads: Arc::new(Mutex::new(None)),
}
}
@@ -67,59 +69,68 @@ impl DownloadManager {
let tasks_clone: Vec<DownloadTask>;
let aria2_started;
let port;
{
// 使用作用域限制锁的生命周期
let downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
tasks_clone = downloads.values().cloned().collect();
aria2_started = self.aria2_started.load(Ordering::SeqCst);
}
// 如果 aria2 未启动,直接返回队列中的任务
if !aria2_started {
return Ok(tasks_clone.into_iter().map(|task| DownloadTaskResponse {
category: task.category,
pkgname: task.pkgname,
filename: task.filename,
status: ResponseStatus::Error, // 如果 aria2 未启动,标记为错误状态
icon: task.icon,
name: task.name,
progress: 0.0,
speed: None,
size: None,
}).collect());
return Ok(tasks_clone
.into_iter()
.map(|task| DownloadTaskResponse {
category: task.category,
pkgname: task.pkgname,
filename: task.filename,
status: ResponseStatus::Error, // 如果 aria2 未启动,标记为错误状态
icon: task.icon,
name: task.name,
progress: 0.0,
speed: None,
size: None,
})
.collect());
}
// 获取端口(在单独的作用域中获取锁)
{
port = *self.aria2_port.lock().map_err(|e| e.to_string())?;
}
// 创建 Aria2Client 实例
let aria2_client = Aria2Client::new("127.0.0.1", port, None);
// 获取所有活动中的下载任务
let active_downloads = aria2_client.tell_active().await
let active_downloads = aria2_client
.tell_active()
.await
.map_err(|e| format!("获取活动下载任务失败: {}", e))?;
// 获取所有等待中的下载任务最多100个
let waiting_downloads = aria2_client.tell_waiting(0, 100).await
let waiting_downloads = aria2_client
.tell_waiting(0, 100)
.await
.map_err(|e| format!("获取等待中下载任务失败: {}", e))?;
// 获取所有已停止的下载任务最多100个
let stopped_downloads = aria2_client.tell_stopped(0, 100).await
let stopped_downloads = aria2_client
.tell_stopped(0, 100)
.await
.map_err(|e| format!("获取已停止下载任务失败: {}", e))?;
// 创建一个映射,用于存储 GID 到 aria2 任务状态的映射
let mut aria2_tasks = HashMap::new();
// 处理活动中的下载任务
for task in active_downloads {
if let Some(gid) = task["gid"].as_str() {
aria2_tasks.insert(gid.to_string(), (task, ResponseStatus::Downloading));
}
}
// 处理等待中的下载任务
for task in waiting_downloads {
if let Some(gid) = task["gid"].as_str() {
@@ -131,7 +142,7 @@ impl DownloadManager {
aria2_tasks.insert(gid.to_string(), (task, status));
}
}
// 处理已停止的下载任务
for task in stopped_downloads {
if let Some(gid) = task["gid"].as_str() {
@@ -139,15 +150,14 @@ impl DownloadManager {
// 当任务完成时,将其加入到安装队列
let task_info = {
let downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
downloads.values()
.find(|t| t.gid == gid)
.cloned()
downloads.values().find(|t| t.gid == gid).cloned()
};
if let Some(task_info) = task_info {
let task_id = format!("{}/{}", task_info.category, task_info.pkgname);
let should_process = {
let mut install_queue = self.install_queue.lock().map_err(|e| e.to_string())?;
let mut install_queue =
self.install_queue.lock().map_err(|e| e.to_string())?;
if !install_queue.contains_key(&task_id) {
let install_task = InstallTask {
category: task_info.category.clone(),
@@ -174,16 +184,16 @@ impl DownloadManager {
aria2_tasks.insert(gid.to_string(), (task, status));
}
}
// 将队列中的任务与 aria2 任务状态结合,生成响应
let mut result = Vec::new();
// 获取安装队列(在生成响应之前)
let install_queue = self.install_queue.lock().map_err(|e| e.to_string())?;
for task in tasks_clone {
let task_id = format!("{}/{}", task.category, task.pkgname);
let mut response = DownloadTaskResponse {
category: task.category.clone(),
pkgname: task.pkgname.clone(),
@@ -195,7 +205,7 @@ impl DownloadManager {
speed: None,
size: None,
};
// 首先检查是否在安装队列中
if let Some(install_task) = install_queue.get(&task_id) {
response.status = match install_task.status {
@@ -208,25 +218,29 @@ impl DownloadManager {
// 如果不在安装队列中,则检查下载状态
if let Some((aria2_task, status)) = aria2_tasks.get(&task.gid) {
response.status = status.clone();
// 计算进度(百分比)
if let (Some(completed_length), Some(total_length)) = (
aria2_task["completedLength"].as_str().and_then(|s| s.parse::<f64>().ok()),
aria2_task["totalLength"].as_str().and_then(|s| s.parse::<f64>().ok())
aria2_task["completedLength"]
.as_str()
.and_then(|s| s.parse::<f64>().ok()),
aria2_task["totalLength"]
.as_str()
.and_then(|s| s.parse::<f64>().ok()),
) {
if total_length > 0.0 {
let progress = ((completed_length / total_length) * 100.0) as f32;
response.progress = (progress * 100.0).round() / 100.0;
}
}
// 获取下载速度
if let Some(download_speed) = aria2_task["downloadSpeed"].as_str() {
if let Ok(speed) = download_speed.parse::<u64>() {
response.speed = Some(format_speed(speed));
}
}
// 获取文件大小
if let Some(total_length) = aria2_task["totalLength"].as_str() {
if let Ok(size) = total_length.parse::<u64>() {
@@ -235,10 +249,10 @@ impl DownloadManager {
}
}
}
result.push(response);
}
Ok(result)
}
@@ -263,7 +277,8 @@ impl DownloadManager {
"--dir=/tmp/spark-store", // 设置下载目录为 /tmp/spark-store
])
.spawn()
.map_err(|e| format!("启动 aria2 失败: {}", e)).unwrap();
.map_err(|e| format!("启动 aria2 失败: {}", e))
.unwrap();
// 保存进程 ID
if let Ok(mut pid_guard) = self.aria2_pid.lock() {
@@ -274,17 +289,26 @@ impl DownloadManager {
}
// 添加下载任务
pub async fn add_download(&self, category: String, pkgname: String, filename: String, name: String) -> Result<(), String> {
pub async fn add_download(
&self,
category: String,
pkgname: String,
filename: String,
name: String,
) -> Result<(), String> {
// 检查并启动 aria2如果还没启动
if !self.aria2_started.load(Ordering::SeqCst) {
self.start_aria2();
}
let task_id = format!("{}/{}", category, pkgname);
// 获取metalink文件URL和内容在获取锁之前完成
let json_server_url = get_json_server_url();
let metalink_url = format!("{}{}/{}/{}.metalink", json_server_url, category, pkgname, filename);
let metalink_url = format!(
"{}{}/{}/{}.metalink",
json_server_url, category, pkgname, filename
);
// 发送请求获取metalink文件
let client = reqwest::Client::new();
@@ -294,33 +318,31 @@ impl DownloadManager {
.send()
.await
.map_err(|e| format!("获取metalink文件失败: {}", e))?;
// 检查响应状态
if !response.status().is_success() {
return Err(format!("获取metalink文件失败: HTTP {}", response.status()));
}
// 获取metalink文件内容
let metalink_content = response.bytes()
.await
.map_err(|e| format!("读取metalink内容失败: {}", e))?;
let metalink_content = response
.bytes()
.await
.map_err(|e| format!("读取metalink内容失败: {}", e))?;
// 创建Aria2Client并添加下载任务
let port = *self.aria2_port.lock().map_err(|e| e.to_string())?;
let aria2_client = Aria2Client::new("127.0.0.1", port, None);
// 使用Aria2Client添加metalink下载
let gids = aria2_client.add_metalink(
&metalink_content,
None,
None
)
.await
.map_err(|e| format!("添加下载任务失败: {}", e))?;
let gids = aria2_client
.add_metalink(&metalink_content, None, None)
.await
.map_err(|e| format!("添加下载任务失败: {}", e))?;
// 获取一次锁,完成所有状态更新操作
let mut downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
// 检查任务是否已存在
if downloads.contains_key(&task_id) {
return Ok(());
@@ -333,19 +355,19 @@ impl DownloadManager {
filename,
name,
icon: format_icon_url(&category, &pkgname),
gid: gids[0].clone()
gid: gids[0].clone(),
};
// 添加任务到队列
downloads.insert(task_id, task);
Ok(())
}
// 暂停下载任务
pub async fn pause_download(&self, category: String, pkgname: String) -> Result<(), String> {
let task_id = format!("{}/{}", category, pkgname);
// 获取任务信息,并在作用域结束时释放锁
let task_gid = {
let downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
@@ -354,27 +376,29 @@ impl DownloadManager {
None => return Err(format!("找不到下载任务: {}", task_id)),
}
};
// 如果 aria2 未启动,返回错误
if !self.aria2_started.load(Ordering::SeqCst) {
return Err("aria2 未启动".to_string());
}
// 创建 Aria2Client 实例
let port = *self.aria2_port.lock().map_err(|e| e.to_string())?;
let aria2_client = Aria2Client::new("127.0.0.1", port, None);
// 调用 aria2 的 pause 方法
aria2_client.pause(task_gid.as_str()).await
aria2_client
.pause(task_gid.as_str())
.await
.map_err(|e| format!("暂停下载任务失败: {}", e))?;
Ok(())
}
// 恢复下载任务
pub async fn resume_download(&self, category: String, pkgname: String) -> Result<(), String> {
let task_id = format!("{}/{}", category, pkgname);
// 获取任务信息,并在作用域结束时释放锁
let task_gid = {
let downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
@@ -383,27 +407,29 @@ impl DownloadManager {
None => return Err(format!("找不到下载任务: {}", task_id)),
}
};
// 如果 aria2 未启动,返回错误
if !self.aria2_started.load(Ordering::SeqCst) {
return Err("aria2 未启动".to_string());
}
// 创建 Aria2Client 实例
let port = *self.aria2_port.lock().map_err(|e| e.to_string())?;
let aria2_client = Aria2Client::new("127.0.0.1", port, None);
// 调用 aria2 的 unpause 方法
aria2_client.unpause(task_gid.as_str()).await
aria2_client
.unpause(task_gid.as_str())
.await
.map_err(|e| format!("恢复下载任务失败: {}", e))?;
Ok(())
}
// 取消下载任务
pub async fn cancel_download(&self, category: String, pkgname: String) -> Result<(), String> {
let task_id = format!("{}/{}", category, pkgname);
// 获取任务信息,并在作用域结束时释放锁
let task_gid = {
let mut downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
@@ -415,23 +441,25 @@ impl DownloadManager {
return Ok(());
}
task.gid.clone()
},
}
None => return Err(format!("找不到下载任务: {}", task_id)),
}
};
// 创建 Aria2Client 实例
let port = *self.aria2_port.lock().map_err(|e| e.to_string())?;
let aria2_client = Aria2Client::new("127.0.0.1", port, None);
// 调用 aria2 的 remove 方法
aria2_client.remove(task_gid.as_str()).await
aria2_client
.remove(task_gid.as_str())
.await
.map_err(|e| format!("取消下载任务失败: {}", e))?;
// 从队列中移除任务
let mut downloads = self.download_queue.lock().map_err(|e| e.to_string())?;
downloads.remove(&task_id);
Ok(())
}
@@ -442,9 +470,7 @@ impl DownloadManager {
if let Ok(pid_guard) = self.aria2_pid.lock() {
if let Some(pid) = *pid_guard {
// 使用 kill 命令终止特定的进程
if let Ok(output) = Command::new("kill")
.arg(pid.to_string())
.output() {
if let Ok(output) = Command::new("kill").arg(pid.to_string()).output() {
if output.status.success() {
println!("成功关闭 aria2 (PID: {})", pid);
} else {
@@ -467,7 +493,8 @@ impl DownloadManager {
// 查找第一个等待安装的任务
let (task_id, task) = {
let mut install_queue = self.install_queue.lock().map_err(|e| e.to_string())?;
if let Some((id, task)) = install_queue.iter_mut()
if let Some((id, task)) = install_queue
.iter_mut()
.find(|(_, task)| matches!(task.status, InstallStatus::Queued))
.map(|(id, task)| (id.clone(), task.clone()))
{
@@ -512,7 +539,7 @@ impl DownloadManager {
// 实际执行安装的方法
async fn install_package(&self, task: &InstallTask) -> Result<(), String> {
println!("开始安装包: {}", task.filepath);
// 移除可能存在的引号
let filepath = task.filepath.trim_matches('"');
@@ -546,4 +573,4 @@ impl DownloadManager {
false
}
}
}
}