refactor(download manager): Fully separate & generic download manager

Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
quexeky
2025-01-02 12:16:17 +11:00
parent 6568faaf4f
commit cac612b176
15 changed files with 283 additions and 223 deletions

View File

@ -12,16 +12,9 @@ use std::{
use log::info;
use serde::Serialize;
use crate::downloads::download_agent::GameDownloadAgent;
use super::{application_download_error::ApplicationDownloadError, download_manager_builder::{CurrentProgressObject, DownloadAgent, DownloadableQueueStandin}, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, queue::Queue};
use super::{application_download_error::ApplicationDownloadError, download_manager_builder::{CurrentProgressObject, DownloadAgent}, downloadable_metadata::DownloadableMetadata, queue::Queue};
pub enum DownloadType {
Game,
Tool,
DLC,
Mod
}
pub enum DownloadManagerSignal {
/// Resumes (or starts) the DownloadManager
Go,
@ -91,22 +84,6 @@ pub struct DownloadManager {
progress: CurrentProgressObject,
command_sender: Sender<DownloadManagerSignal>,
}
impl<T: Downloadable> From<Arc<T>> for DownloadableQueueStandin {
fn from(value: Arc<T>) -> Self {
Self {
id: value.id(),
status: Mutex::from(DownloadStatus::Queued),
progress: value.progress().clone(),
}
}
}
impl Debug for DownloadableQueueStandin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DownloadableQueueStandin")
.field("id", &self.id)
.finish()
}
}
#[allow(dead_code)]
impl DownloadManager {
@ -126,40 +103,34 @@ impl DownloadManager {
pub fn queue_download(
&self,
id: String,
version: String,
target_download_dir: usize,
download: DownloadAgent
) -> Result<(), SendError<DownloadManagerSignal>> {
info!("Adding download id {}", id);
self.command_sender.send(DownloadManagerSignal::Queue(
id,
version,
target_download_dir,
))?;
info!("Adding download id {:?}", download.metadata());
self.command_sender.send(DownloadManagerSignal::Queue(download))?;
self.command_sender.send(DownloadManagerSignal::Go)
}
pub fn edit(&self) -> MutexGuard<'_, VecDeque<Arc<DownloadableQueueStandin>>> {
pub fn edit(&self) -> MutexGuard<'_, VecDeque<Arc<DownloadableMetadata>>> {
self.download_queue.edit()
}
pub fn read_queue(&self) -> VecDeque<Arc<DownloadableQueueStandin>> {
pub fn read_queue(&self) -> VecDeque<Arc<DownloadableMetadata>> {
self.download_queue.read()
}
pub fn get_current_download_progress(&self) -> Option<f64> {
let progress_object = (*self.progress.lock().unwrap()).clone()?;
Some(progress_object.get_progress())
}
pub fn rearrange_string(&self, id: String, new_index: usize) {
pub fn rearrange_string(&self, meta: &Arc<DownloadableMetadata>, new_index: usize) {
let mut queue = self.edit();
let current_index = get_index_from_id(&mut queue, id).unwrap();
let current_index = get_index_from_id(&mut queue, meta).unwrap();
let to_move = queue.remove(current_index).unwrap();
queue.insert(new_index, to_move);
self.command_sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
pub fn cancel(&self, id: String) {
pub fn cancel(&self, meta: Arc<DownloadableMetadata>) {
self.command_sender
.send(DownloadManagerSignal::Remove(id))
.send(DownloadManagerSignal::Remove(meta))
.unwrap();
}
pub fn rearrange(&self, current_index: usize, new_index: usize) {
@ -170,7 +141,7 @@ impl DownloadManager {
let needs_pause = current_index == 0 || new_index == 0;
if needs_pause {
self.command_sender
.send(DownloadManagerSignal::Cancel)
.send(DownloadManagerSignal::Stop)
.unwrap();
}
@ -203,20 +174,23 @@ impl DownloadManager {
.unwrap();
self.terminator.join()
}
pub fn uninstall_application(&self, id: String) {
pub fn uninstall_application(&self, meta: Arc<DownloadableMetadata>) {
self.command_sender
.send(DownloadManagerSignal::Uninstall(id))
.send(DownloadManagerSignal::Uninstall(meta))
.unwrap();
}
pub fn get_sender(&self) -> Sender<DownloadManagerSignal> {
self.command_sender.clone()
}
}
/// Takes in the locked value from .edit() and attempts to
/// get the index of whatever id is passed in
fn get_index_from_id(
queue: &mut MutexGuard<'_, VecDeque<Arc<DownloadableQueueStandin>>>,
id: String,
queue: &mut MutexGuard<'_, VecDeque<Arc<DownloadableMetadata>>>,
meta: &Arc<DownloadableMetadata>,
) -> Option<usize> {
queue
.iter()
.position(|download_agent| download_agent.id == id)
.position(|download_agent| download_agent == meta)
}

View File

@ -12,19 +12,11 @@ use log::{error, info};
use tauri::{AppHandle, Emitter};
use crate::{
db::{set_application_status, ApplicationStatus, ApplicationTransientStatus, Database}, download_manager::{download_manager::{DownloadStatus, DownloadType}, generate_downloadable::generate_downloadable}, downloads::download_agent::{self, GameDownloadAgent}, library::{
on_game_complete, push_application_update, QueueUpdateEvent,
QueueUpdateEventQueueData, StatsUpdateEvent,
}, state::GameStatusManager, DB
};
download_manager::{download_manager::DownloadStatus, generate_downloadable::generate_downloadable}, library::{QueueUpdateEvent, QueueUpdateEventQueueData, StatsUpdateEvent}}
;
use super::{application_download_error::ApplicationDownloadError, download_manager::{DownloadManager, DownloadManagerSignal, DownloadManagerStatus}, download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject, queue::Queue};
pub struct DownloadableQueueStandin {
pub id: String,
pub status: Mutex<DownloadStatus>,
pub progress: Arc<ProgressObject>,
}
pub type DownloadAgent = Arc<Box<dyn Downloadable + Send + Sync>>;
pub type CurrentProgressObject = Arc<Mutex<Option<Arc<ProgressObject>>>>;
@ -163,12 +155,12 @@ impl DownloadManagerBuilder {
DownloadManagerSignal::Error(e) => {
self.manage_error_signal(e);
}
//DownloadManagerSignal::UpdateUIQueue => {
// self.push_ui_queue_update();
//}
//DownloadManagerSignal::UpdateUIStats(kbs, time) => {
// self.push_ui_stats_update(kbs, time);
//}
DownloadManagerSignal::UpdateUIQueue => {
self.push_ui_queue_update();
}
DownloadManagerSignal::UpdateUIStats(kbs, time) => {
self.push_ui_stats_update(kbs, time);
}
DownloadManagerSignal::Finish => {
self.stop_and_wait_current_download();
return Ok(());
@ -239,8 +231,7 @@ impl DownloadManagerBuilder {
download_agent.on_incomplete(&app_handle);
},
Err(e) => {
download_agent.on_error(&app_handle);
error!("error while managing download: {}", e);
download_agent.on_error(&app_handle, e.clone());
sender.send(DownloadManagerSignal::Error(e)).unwrap();
},
}
@ -272,7 +263,7 @@ impl DownloadManagerBuilder {
info!("Got signal Error");
let current_agent = self.current_download_agent.clone().unwrap();
current_agent.on_error(&self.app_handle);
current_agent.on_error(&self.app_handle, error.clone());
self.stop_and_wait_current_download();
self.remove_and_cleanup_front_download(&current_agent.metadata());
@ -310,6 +301,28 @@ impl DownloadManagerBuilder {
self.manage_cancel_signal(meta);
download_agent.on_uninstall(&self.app_handle);
}
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
let event_data = StatsUpdateEvent { speed: kbs, time };
self.app_handle.emit("update_stats", event_data);
}
fn push_ui_queue_update(&self) {
let registry = &self.download_agent_registry;
let queue_objs = registry
.iter()
.map(|(key, val)| QueueUpdateEventQueueData {
meta: DownloadableMetadata::clone(&key),
status: val.status(),
progress: val.progress().get_progress()
})
.collect();
let event_data = QueueUpdateEvent {
queue: queue_objs,
status: self.status.lock().unwrap().clone(),
};
self.app_handle.emit("update_queue", event_data);
}
}
/*
// Refactored to consolidate this type. It's a monster.
@ -317,6 +330,33 @@ pub type DownloadAgent = Arc<Mutex<Box<dyn Downloadable + Send + Sync>>>;
impl DownloadManagerBuilder {
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
let event_data = StatsUpdateEvent { speed: kbs, time };
self.app_handle.emit("update_stats", event_data).unwrap();
}
fn push_ui_queue_update(&self) {
let queue = self.download_queue.read();
let queue_objs: Vec<QueueUpdateEventQueueData> = queue
.iter()
.map(|interface| QueueUpdateEventQueueData {
id: interface.id.clone(),
status: interface.status.lock().unwrap().clone(),
progress: interface.progress.get_progress(),
})
.collect();
let status_handle = self.status.lock().unwrap();
let status = status_handle.clone();
drop(status_handle);
let event_data = QueueUpdateEvent {
queue: queue_objs,
status,
};
self.app_handle.emit("update_queue", event_data).unwrap();
}
fn uninstall_application(&mut self, id: String) {
// Removes the download if it's in the queue
self.manage_remove_download_from_queue(id.clone());
@ -606,34 +646,6 @@ impl DownloadManagerBuilder {
.unwrap();
}
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
let event_data = StatsUpdateEvent { speed: kbs, time };
self.app_handle.emit("update_stats", event_data).unwrap();
}
fn push_ui_queue_update(&self) {
let queue = self.download_queue.read();
let queue_objs: Vec<QueueUpdateEventQueueData> = queue
.iter()
.map(|interface| QueueUpdateEventQueueData {
id: interface.id.clone(),
status: interface.status.lock().unwrap().clone(),
progress: interface.progress.get_progress(),
})
.collect();
let status_handle = self.status.lock().unwrap();
let status = status_handle.clone();
drop(status_handle);
let event_data = QueueUpdateEvent {
queue: queue_objs,
status,
};
self.app_handle.emit("update_queue", event_data).unwrap();
}
fn stop_and_wait_current_download(&self) {
self.set_status(DownloadManagerStatus::Paused);
if let Some(current_flag) = &self.active_control_flag {
@ -816,4 +828,4 @@ impl DownloadManagerBuilder {
.unwrap();
}
}
*/
*/

View File

@ -1,18 +1,19 @@
use std::sync::Arc;
use std::sync::{mpsc::Sender, Arc};
use tauri::AppHandle;
use super::{
application_download_error::ApplicationDownloadError, download_thread_control_flag::DownloadThreadControl, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject
application_download_error::ApplicationDownloadError, download_manager::{DownloadManagerSignal, DownloadStatus}, download_thread_control_flag::DownloadThreadControl, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject
};
pub trait Downloadable: Send + Sync {
fn download(&self) -> Result<bool, ApplicationDownloadError>;
fn progress(&self) -> Arc<ProgressObject>;
fn control_flag(&self) -> DownloadThreadControl;
fn status(&self) -> DownloadStatus;
fn metadata(&self) -> Arc<DownloadableMetadata>;
fn on_initialised(&self, app_handle: &AppHandle);
fn on_error(&self, app_handle: &AppHandle);
fn on_error(&self, app_handle: &AppHandle, error: ApplicationDownloadError);
fn on_complete(&self, app_handle: &AppHandle);
fn on_incomplete(&self, app_handle: &AppHandle);
fn on_cancelled(&self, app_handle: &AppHandle);

View File

@ -1,4 +1,25 @@
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DownloadableMetadata {
id: String,
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
pub enum DownloadType {
Game,
Tool,
DLC,
Mod
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
pub struct DownloadableMetadata {
pub id: String,
pub version: String,
pub download_type: DownloadType
}
impl DownloadableMetadata {
pub fn new(id: String, version: String, download_type: DownloadType) -> Self {
Self {
id,
version,
download_type
}
}
}

View File

@ -34,11 +34,11 @@ impl Queue {
}
/// Either inserts `interface` at the specified index, or appends to
/// the back of the deque if index is greater than the length of the deque
pub fn insert(&self, interface: DownloadableMetadata, index: usize) {
pub fn insert(&self, interface: Arc<DownloadableMetadata>, index: usize) {
if self.read().len() > index {
self.append(interface);
} else {
self.edit().insert(index, Arc::new(interface));
self.edit().insert(index, interface);
}
}
pub fn append(&self, interface: Arc<DownloadableMetadata>) {