eureka_manager_cli/commands/download/
manga.rs

1use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
2
3use actix::Addr;
4use clap::Args;
5use eureka_mmanager::{
6    download::{
7        cover::CoverDownloadMessage, manga::MangaDownloadMessage, state::DownloadMessageState,
8    },
9    history::service::messages::is_in::IsInMessage,
10    prelude::*,
11};
12use indicatif::ProgressBar;
13use log::{info, trace};
14use mangadex_api_types_rust::RelationshipType;
15use uuid::Uuid;
16
17use crate::commands::{AsyncRun, AsyncRunContext};
18
19#[derive(Debug, Args)]
20pub struct MangaDownloadArgs {
21    /// Manga ids
22    #[arg(long = "id")]
23    pub ids: Vec<Uuid>,
24    #[arg(long)]
25    pub id_text_file: Vec<PathBuf>,
26}
27
28impl MangaDownloadArgs {
29    pub fn get_ids(&self) -> Vec<Uuid> {
30        let mut ids = self.ids.clone();
31        self.id_text_file
32            .iter()
33            .map(|e| (e, File::open(e)))
34            .flat_map(|(path, res)| match res {
35                Ok(file) => Some(id_list_txt_reader::IdListTxtReader::new(BufReader::new(
36                    file,
37                ))),
38                Err(err) => {
39                    log::error!("Cannot open the {} file: {}", path.to_string_lossy(), err);
40                    None
41                }
42            })
43            .flat_map(|file| file.flat_map(|s| Uuid::from_str(&s)))
44            .for_each(|id| {
45                ids.push(id);
46            });
47        ids.dedup();
48        ids
49    }
50}
51
52impl AsyncRun for MangaDownloadArgs {
53    async fn run(&self, ctx: AsyncRunContext) -> anyhow::Result<()> {
54        let ids = self.get_ids();
55        let mut progress = ProgressBar::new(ids.len() as u64);
56        progress = ctx.progress.add(progress);
57        trace!("Downloading {} titles with their cover", ids.len());
58
59        for id in ids {
60            let manager = ctx.manager.clone();
61            let task = async move {
62                trace!("Downloading title {id}");
63                let dirs =
64                    <Addr<DownloadManager> as GetManagerStateData>::get_dir_options(&manager)
65                        .await?;
66                let cover = {
67                    let manga_manager =
68                        <Addr<DownloadManager> as GetManager<MangaDownloadManager>>::get(&manager)
69                            .await?;
70                    let mut task = manga_manager
71                        .send(
72                            MangaDownloadMessage::new(id).state(DownloadMessageState::Downloading),
73                        )
74                        .await?;
75                    let data = task.wait().await?.await?;
76                    info!(
77                        "downloaded title {} = {:?}",
78                        data.id,
79                        data.attributes.title.values().next()
80                    );
81                    data.find_first_relationships(RelationshipType::CoverArt)
82                        .ok_or(anyhow::Error::msg(format!(
83                            "Cannot find the title {} cover art",
84                            id
85                        )))?
86                        .clone()
87                };
88                if !dirs
89                    .send(IsInMessage(HistoryEntry::new(
90                        cover.id,
91                        RelationshipType::CoverArt,
92                    )))
93                    .await?
94                {
95                    trace!("Downloading {} cover art", cover.id);
96                    let cover_manager =
97                        <Addr<DownloadManager> as GetManager<CoverDownloadManager>>::get(&manager)
98                            .await?;
99                    let mut task = cover_manager
100                        .send(
101                            CoverDownloadMessage::new(cover.id)
102                                .state(DownloadMessageState::Downloading),
103                        )
104                        .await?;
105                    task.wait().await?.await?;
106                    info!("Downloaded {} cover art", cover.id);
107                }
108                Ok::<_, anyhow::Error>(())
109            };
110            if let Err(err) = task.await {
111                log::error!("{}", err);
112            }
113            progress.inc(1);
114        }
115        progress.finish();
116        ctx.progress.remove(&progress);
117        Ok(())
118    }
119}