Skip to main content

eureka_mmanager/download/
manga.rs

1pub mod messages;
2pub mod task;
3
4use std::{collections::HashMap, sync::Arc, time::Duration};
5
6use actix::{prelude::*, WeakAddr};
7use shrink_fit_wrapper::ShrinkFitWrapper;
8use tokio::sync::Notify;
9use uuid::Uuid;
10
11use crate::download::messages::StopTask;
12
13use self::task::MangaDownloadTask;
14
15use super::{
16    messages::{DropSingleTaskMessage, GetTaskMessage, StartDownload},
17    state::{DownloadManagerState, DownloadMessageState},
18    traits::{managers::TaskManager, task::AsyncState},
19};
20
21#[derive(Debug)]
22pub struct MangaDownloadManager {
23    state: Addr<DownloadManagerState>,
24    tasks: ShrinkFitWrapper<HashMap<Uuid, WeakAddr<MangaDownloadTask>>>,
25    notify: Arc<Notify>,
26}
27
28impl MangaDownloadManager {
29    pub fn new(state: Addr<DownloadManagerState>) -> Self {
30        Self {
31            state,
32            tasks: ShrinkFitWrapper::new(HashMap::new())
33                .set_shrink_duration_cycle(Duration::from_secs(6 * 60)),
34            notify: Arc::new(Notify::new()),
35        }
36    }
37}
38
39impl Actor for MangaDownloadManager {
40    type Context = Context<Self>;
41}
42
43#[derive(Debug, Clone, Copy)]
44pub struct MangaDownloadMessage {
45    id: Uuid,
46    // TODO Add cover_art download support
47    state: DownloadMessageState,
48}
49
50impl From<Uuid> for MangaDownloadMessage {
51    fn from(value: Uuid) -> Self {
52        Self::new(value)
53    }
54}
55
56impl From<MangaDownloadMessage> for Uuid {
57    fn from(value: MangaDownloadMessage) -> Self {
58        value.id
59    }
60}
61
62impl MangaDownloadMessage {
63    pub fn new(id: Uuid) -> Self {
64        Self {
65            id,
66            state: Default::default(),
67        }
68    }
69    pub fn state(self, state: DownloadMessageState) -> Self {
70        Self { state, ..self }
71    }
72}
73
74impl Message for MangaDownloadMessage {
75    type Result = Addr<MangaDownloadTask>;
76}
77
78impl TaskManager for MangaDownloadManager {
79    type DownloadMessage = MangaDownloadMessage;
80    type Task = MangaDownloadTask;
81    fn drop_task(&mut self, id: Uuid) {
82        if let Some(task) = self.tasks.get(&id) {
83            if task.upgrade().is_none() {
84                self.tasks.as_mut().remove(&id);
85            }
86        }
87        self.notify.notify_waiters();
88    }
89    fn state(&self) -> Addr<DownloadManagerState> {
90        self.state.clone()
91    }
92    fn notify(&self) -> Arc<Notify> {
93        self.notify.clone()
94    }
95    fn tasks(&self) -> Vec<Addr<Self::Task>> {
96        self.tasks
97            .values()
98            .flat_map(|task| task.upgrade())
99            .collect()
100    }
101    fn tasks_id(&self) -> Vec<Uuid> {
102        self.tasks
103            .iter()
104            .flat_map(|(id, tasks)| {
105                if tasks.upgrade().is_some() {
106                    Some(id)
107                } else {
108                    None
109                }
110            })
111            .copied()
112            .collect()
113    }
114    fn new_task(
115        &mut self,
116        msg: Self::DownloadMessage,
117        ctx: &mut Self::Context,
118    ) -> Addr<Self::Task> {
119        let task = {
120            match self.tasks.as_mut().entry(msg.id) {
121                std::collections::hash_map::Entry::Occupied(mut occupied_entry) => {
122                    let weak = occupied_entry.get_mut();
123                    if let Some(tsk) = weak.upgrade() {
124                        tsk
125                    } else {
126                        let tsk = Self::Task::new(msg.id, ctx.address()).start();
127                        let _weak = std::mem::replace(weak, tsk.downgrade());
128                        tsk
129                    }
130                }
131                std::collections::hash_map::Entry::Vacant(vacant_entry) => {
132                    let tsk = Self::Task::new(msg.id, ctx.address()).start();
133                    vacant_entry.insert(tsk.downgrade());
134                    tsk
135                }
136            }
137        };
138        let re_task = task.clone();
139        self.notify.notify_waiters();
140
141        if let DownloadMessageState::Downloading = msg.state {
142            let fut = async move {
143                let s = re_task.state().await?;
144                if !s.is_loading() {
145                    re_task.send(StartDownload).await?;
146                }
147                Ok::<_, actix::MailboxError>(())
148            }
149            .into_actor(self)
150            .map(|s, _, _| {
151                if let Err(er) = s {
152                    log::error!("{er}");
153                }
154            });
155            ctx.wait(fut)
156        }
157        task
158    }
159    fn get_task(&self, id: Uuid) -> Option<Addr<Self::Task>> {
160        self.tasks.get(&id).and_then(WeakAddr::upgrade)
161    }
162}
163
164impl Handler<MangaDownloadMessage> for MangaDownloadManager {
165    type Result = <MangaDownloadMessage as Message>::Result;
166    // TODO Add support for the DownloadState
167    fn handle(&mut self, msg: MangaDownloadMessage, ctx: &mut Self::Context) -> Self::Result {
168        self.new_task(msg, ctx)
169    }
170}
171
172impl Handler<DropSingleTaskMessage> for MangaDownloadManager {
173    type Result = <DropSingleTaskMessage as Message>::Result;
174    fn handle(&mut self, msg: DropSingleTaskMessage, _ctx: &mut Self::Context) -> Self::Result {
175        self.drop_task(msg.0);
176    }
177}
178
179impl Handler<GetTaskMessage<MangaDownloadTask>> for MangaDownloadManager {
180    type Result = <GetTaskMessage<MangaDownloadTask> as Message>::Result;
181    fn handle(
182        &mut self,
183        msg: GetTaskMessage<MangaDownloadTask>,
184        _ctx: &mut Self::Context,
185    ) -> Self::Result {
186        self.get_task(msg.into())
187    }
188}
189
190impl Drop for MangaDownloadManager {
191    fn drop(&mut self) {
192        self.tasks
193            .values()
194            .flat_map(|maybe_task| maybe_task.upgrade())
195            .for_each(|task| task.do_send(StopTask));
196    }
197}