fast_down_gui/core/
app.rs1use crate::{
2 core::{DownloadEvent, TaskSet, apply_progress_diff},
3 fmt::format_size,
4 persist::{self, Database},
5 ui::{self, DownloadConfig, EntryData, GeneralConfig, MainWindow},
6 utils::LogErr,
7};
8use auto_launch::AutoLaunch;
9use slint::{Model, SharedString, Weak};
10use std::process::exit;
11
12#[derive(Clone)]
13pub struct App {
14 pub db: Database,
15 pub task_set: TaskSet<i32>,
16 pub ui: Weak<MainWindow>,
17}
18
19impl App {
20 pub fn update_ui_row<F>(&self, gid: i32, mutator: F)
21 where
22 F: FnOnce(usize, &mut EntryData) + Send + 'static,
23 {
24 let _ = self.ui.upgrade_in_event_loop(move |ui| {
25 let list_model = ui.get_all_list();
26 for (row, mut data) in list_model.iter().enumerate() {
27 if data.gid == gid {
28 mutator(row, &mut data);
29 list_model.set_row_data(row, data);
30 break;
31 }
32 }
33 });
34 }
35
36 pub fn create_download_handler(
38 &self,
39 gid: i32,
40 ) -> impl FnMut(DownloadEvent) + Send + Sync + 'static {
41 let app = self.clone();
42 let mut file_size = 0;
43 move |event| match event {
44 DownloadEvent::Info(info) => {
45 file_size = info.file_size;
46 let _ = app
47 .db
48 .init_entry(gid, *info.clone())
49 .log_err("数据库插入条目失败");
50 app.update_ui_row(gid, move |_, data| {
51 data.status = ui::Status::Running;
52 data.filename = info.file_name.into();
53 data.path = info.file_path.to_string_lossy().as_ref().into();
54 data.total = format_size(info.file_size as f64).into();
55 });
56 }
57 DownloadEvent::Progress(p) => {
58 app.db.update_entry(gid, p.progress.clone(), p.elapsed);
59 app.update_ui_row(gid, move |_, data| {
60 data.downloaded = p.downloaded;
61 data.speed = p.speed;
62 data.avg_speed = p.avg_speed;
63 data.percentage = p.percentage;
64 data.remaining_time = p.remaining_time;
65 data.remaining_size = p.remaining_size;
66 data.time = p.time;
67 if file_size > 0 {
68 data.progress = apply_progress_diff(&data.progress, &p.progress, file_size);
69 }
70 });
71 }
72 DownloadEvent::Flushing => {
73 app.update_ui_row(gid, move |_, data| {
74 data.error = "文件内容已可用,但请勿关机,等待落盘中".into();
75 });
76 }
77 DownloadEvent::FlushError(e) => {
78 app.update_ui_row(gid, move |_, data| {
79 data.status = ui::Status::Error;
80 data.error = e;
81 });
82 }
83 DownloadEvent::End { is_cancelled } => {
84 let db_status = if is_cancelled {
85 persist::Status::Paused
86 } else {
87 persist::Status::Completed
88 };
89 let ui_status = if is_cancelled {
90 ui::Status::Paused
91 } else {
92 ui::Status::Completed
93 };
94 app.db.update_status(gid, db_status);
95 app.update_ui_row(gid, move |_, data| {
96 data.status = ui_status;
97 data.error = SharedString::default();
98 });
99 }
100 }
101 }
102
103 pub fn set_config(
104 &self,
105 download_config: DownloadConfig,
106 general_config: GeneralConfig,
107 auto: Option<&AutoLaunch>,
108 ) {
109 self.task_set
110 .set_concurrency(general_config.max_concurrency as usize);
111 self.db.set_download_config(&download_config);
112 self.db.set_general_config(&general_config);
113 if let Some(auto) = auto {
114 if general_config.auto_start {
115 let _ = auto.enable().log_err("启用开机自启失败");
116 } else {
117 let _ = auto.disable().log_err("禁用开机自启失败");
118 }
119 }
120 let _ = self.ui.upgrade_in_event_loop(move |ui| {
121 ui.set_download_config(download_config);
122 ui.set_general_config(general_config);
123 });
124 }
125
126 pub fn exit(&self) {
127 let db = self.db.clone();
128 let fut = tokio::task::spawn_blocking(move || db.flush_force_sync());
129 let task_set = self.task_set.clone();
130 task_set.cancel_all();
131 tokio::spawn(async move {
132 task_set.join().await;
133 fut.await.unwrap().unwrap();
134 exit(0);
135 });
136 }
137}