添加收藏和下载队列

This commit is contained in:
柚子
2025-02-06 16:43:09 +08:00
parent b2f458f3b8
commit 56fa6a8a2d
29 changed files with 1284 additions and 19 deletions

43
src-tauri/Cargo.lock generated
View File

@@ -821,7 +821,16 @@ version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
"dirs-sys 0.4.1",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys 0.5.0",
]
[[package]]
@@ -832,10 +841,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"redox_users 0.4.6",
"windows-sys 0.48.0",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users 0.5.0",
"windows-sys 0.59.0",
]
[[package]]
name = "dispatch"
version = "0.2.0"
@@ -3168,6 +3189,17 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "redox_users"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom 0.2.15",
"libredox",
"thiserror 2.0.11",
]
[[package]]
name = "regex"
version = "1.11.1"
@@ -3709,6 +3741,7 @@ dependencies = [
name = "spark-store"
version = "4.9.9"
dependencies = [
"dirs 6.0.0",
"futures",
"lazy_static",
"pinyin",
@@ -3929,7 +3962,7 @@ checksum = "78f6efc261c7905839b4914889a5b25df07f0ff89c63fb4afd6ff8c96af15e4d"
dependencies = [
"anyhow",
"bytes",
"dirs",
"dirs 5.0.1",
"dunce",
"embed_plist",
"futures-util",
@@ -3979,7 +4012,7 @@ checksum = "8e950124f6779c6cf98e3260c7a6c8488a74aa6350dd54c6950fdaa349bca2df"
dependencies = [
"anyhow",
"cargo_toml",
"dirs",
"dirs 5.0.1",
"glob",
"heck 0.5.0",
"json-patch",
@@ -4501,7 +4534,7 @@ checksum = "d48a05076dd272615d03033bf04f480199f7d1b66a8ac64d75c625fc4a70c06b"
dependencies = [
"core-graphics 0.24.0",
"crossbeam-channel",
"dirs",
"dirs 5.0.1",
"libappindicator",
"muda",
"objc2",

View File

@@ -29,3 +29,4 @@ lazy_static = "1.5.0"
futures = "0.3.31"
pinyin = "0.10.0"
tauri-plugin-clipboard-manager = "2.2.0"
dirs = "6.0.0"

View File

@@ -0,0 +1,153 @@
use crate::models::download::{DownloadStatus, DownloadTask};
use tauri::State;
use std::sync::Mutex;
use std::collections::HashMap;
use crate::handlers::server::get_json_server_url;
use crate::utils::{format_icon_url, UA};
pub type DownloadQueue = Mutex<HashMap<String, DownloadTask>>;
#[tauri::command]
pub async fn get_downloads(queue: State<'_, DownloadQueue>) -> Result<Vec<DownloadTask>, String> {
let downloads = queue.lock().map_err(|e| e.to_string())?;
Ok(downloads.values().cloned().collect())
}
// 检查是否有正在下载的任务
fn has_downloading_task(downloads: &HashMap<String, DownloadTask>) -> bool {
downloads.values().any(|task| matches!(task.status, DownloadStatus::Downloading))
}
// 获取下一个等待下载的任务
fn get_next_queued_task(downloads: &mut HashMap<String, DownloadTask>) -> Option<String> {
downloads
.iter()
.find(|(_, task)| matches!(task.status, DownloadStatus::Queued))
.map(|(task_id, _)| task_id.clone())
}
// 开始下载下一个任务
fn start_next_download(downloads: &mut HashMap<String, DownloadTask>) {
if let Some(task_id) = get_next_queued_task(downloads) {
if let Some(task) = downloads.get_mut(&task_id) {
task.status = DownloadStatus::Downloading;
}
}
}
#[tauri::command]
pub async fn add_download(category: String, pkgname: String, filename:String, name:String, queue: State<'_, DownloadQueue>) -> Result<(), String> {
let task_id = format!("{}/{}", category, pkgname);
// 检查任务是否已存在
{
let downloads = queue.lock().map_err(|e| e.to_string())?;
if downloads.contains_key(&task_id) {
return Ok(());
}
}
// 获取metalink文件URL
let json_server_url = get_json_server_url();
let metalink_url = format!("{}{}/{}/{}.metalink", json_server_url, category, pkgname, filename);
// 发送请求获取metalink文件
let client = reqwest::Client::new();
let response = client
.get(&metalink_url)
.header("User-Agent", UA)
.send()
.await
.map_err(|e| format!("获取metalink文件失败: {}", e))?;
// 检查响应状态
if !response.status().is_success() {
return Err(format!("获取metalink文件失败: HTTP {}", response.status()));
}
let mut downloads = queue.lock().map_err(|e| e.to_string())?;
let initial_status = if has_downloading_task(&downloads) {
DownloadStatus::Queued
} else {
DownloadStatus::Downloading
};
// 创建下载任务
let task = DownloadTask {
category: category.clone(),
pkgname: pkgname.clone(),
filename,
name,
icon: format_icon_url(&category, &pkgname),
status: initial_status,
progress: 0.0,
speed: None,
size: None,
};
// 添加任务到队列
downloads.insert(task_id, task);
Ok(())
}
#[tauri::command]
pub async fn pause_download(category: String, pkgname: String, queue: State<'_, DownloadQueue>) -> Result<(), String> {
let mut downloads = queue.lock().map_err(|e| e.to_string())?;
let task_id = format!("{}/{}", category, pkgname);
if let Some(task) = downloads.get_mut(&task_id) {
let old_status = task.status.clone();
task.status = DownloadStatus::Paused;
if matches!(old_status, DownloadStatus::Downloading) {
start_next_download(&mut downloads);
}
}
Ok(())
}
#[tauri::command]
pub async fn resume_download(category: String, pkgname: String, queue: State<'_, DownloadQueue>) -> Result<(), String> {
let mut downloads = queue.lock().map_err(|e| e.to_string())?;
let task_id = format!("{}/{}", category, pkgname);
// 先检查任务是否存在且处于暂停状态
let should_resume = downloads
.get(&task_id)
.map(|task| matches!(task.status, DownloadStatus::Paused))
.unwrap_or(false);
if should_resume {
// 检查是否有其他正在下载的任务
let has_downloading = has_downloading_task(&downloads);
// 更新任务状态
if let Some(task) = downloads.get_mut(&task_id) {
task.status = if has_downloading {
DownloadStatus::Queued
} else {
DownloadStatus::Downloading
};
}
}
Ok(())
}
#[tauri::command]
pub async fn cancel_download(category: String, pkgname: String, queue: State<'_, DownloadQueue>) -> Result<(), String> {
let mut downloads = queue.lock().map_err(|e| e.to_string())?;
let task_id = format!("{}/{}", category, pkgname);
let was_downloading = downloads
.get(&task_id)
.map(|task| matches!(task.status, DownloadStatus::Downloading))
.unwrap_or(false);
downloads.remove(&task_id);
if was_downloading {
start_next_download(&mut downloads);
}
Ok(())
}

View File

@@ -0,0 +1,22 @@
use std::fs;
#[tauri::command]
pub fn save_text_file(filename: String, content: String) -> Result<(), String> {
let config_dir = dirs::config_dir().ok_or("无法获取配置目录")?;
let dir = config_dir.join(env!("CARGO_PKG_NAME"));
// 确保目录存在
fs::create_dir_all(&dir).map_err(|e| format!("创建目录失败: {}", e))?;
let file_path = dir.join(filename);
fs::write(file_path, content).map_err(|e| format!("写入文件失败: {}", e))
}
#[tauri::command]
pub fn read_text_file(filename: String) -> Result<String, String> {
let config_dir = dirs::config_dir().ok_or("无法获取配置目录")?;
let dir = config_dir.join(env!("CARGO_PKG_NAME"));
let file_path = dir.join(filename);
fs::read_to_string(file_path).map_err(|e| format!("读取文件失败: {}", e))
}

View File

@@ -1,4 +1,6 @@
pub mod category;
pub mod server;
pub mod app;
pub mod home;
pub mod home;
pub mod file;
pub mod download;

View File

@@ -7,6 +7,7 @@ pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_clipboard_manager::init())
.manage(handlers::download::DownloadQueue::default())
.invoke_handler(tauri::generate_handler![
handlers::category::get_all_categories,
handlers::category::get_category_apps,
@@ -19,6 +20,13 @@ pub fn run() {
handlers::home::get_home_links,
handlers::home::get_home_lists,
handlers::home::get_home_list_apps,
handlers::file::read_text_file,
handlers::file::save_text_file,
handlers::download::get_downloads,
handlers::download::add_download,
handlers::download::pause_download,
handlers::download::resume_download,
handlers::download::cancel_download,
utils::get_user_agent,
])
.run(tauri::generate_context!())

View File

@@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DownloadTask {
pub category: String,
pub pkgname: String,
pub filename: String,
pub status: DownloadStatus,
pub progress: f32,
pub icon: String,
pub name: String,
pub speed: Option<String>,
pub size: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum DownloadStatus {
Downloading,
Queued,
Paused,
Completed,
Error,
}

View File

@@ -1,3 +1,4 @@
pub mod category;
pub mod app;
pub mod home;
pub mod home;
pub mod download;

View File

@@ -30,6 +30,12 @@
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
],
"linux": {
"deb": {
"files": {},
"depends": ["aria2"]
}
}
}
}