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