Skip to main content

fast_down_gui/core/
app.rs

1use 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    /// 创建下载过程中的事件处理器
37    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}