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 {id} cover art"
84                        )))?
85                        .clone()
86                };
87                if !dirs
88                    .send(IsInMessage(HistoryEntry::new(
89                        cover.id,
90                        RelationshipType::CoverArt,
91                    )))
92                    .await?
93                {
94                    trace!("Downloading {} cover art", cover.id);
95                    let cover_manager =
96                        <Addr<DownloadManager> as GetManager<CoverDownloadManager>>::get(&manager)
97                            .await?;
98                    let mut task = cover_manager
99                        .send(
100                            CoverDownloadMessage::new(cover.id)
101                                .state(DownloadMessageState::Downloading),
102                        )
103                        .await?;
104                    task.wait().await?.await?;
105                    info!("Downloaded {} cover art", cover.id);
106                }
107                Ok::<_, anyhow::Error>(())
108            };
109            if let Err(err) = task.await {
110                log::error!("{err}");
111            }
112            progress.inc(1);
113        }
114        progress.finish();
115        ctx.progress.remove(&progress);
116        Ok(())
117    }
118}