Skip to content

`utils` features Annoucement (MangadexApi 2.1.0)

Compare
Choose a tag to compare
@tonymushah tonymushah released this 12 Jun 22:57
· 855 commits to main since this release

Hello everybody,

After the realease of special-eureka, i made some testing (mostly reading πŸ˜…) but i had some limitations that annoys me when using this SDK.

The first thing is the (non serialization of the types and schemas) but now it`s fixed :

  • mangadex-api-types-rust 0.3.4 has two feature flags : non_exhaustive (enabled by default) that enable #[non_exhaustive] for enums and specta for the specta. Useful for the ones who is building a typesafe tauri app.
  • mangadex-api-schema 0.3.2 has three features flags : non_exhaustive (enabled by default) that enable #[non_exhaustive] for structs and schemas ,specta for the specta, and serialize that enable serde serialization

The second thing is the new features flags :

  • legacy-auth and legacy-account for the old Mangadex login system. Since 5.9.0, Mangadex switched to OAuth for their login system. These involve the MangaDexClient::auth() and MangaDexClient::account() removed by default.
  • utils for some utilities that i already imagined.

The utils features

For now, this allows you to download chapters and cover much faster.
I mean code faster.

When enabled, this unlock the new function in the client : MangaDexClient::download() allows you to download chapters and cover images

Examples :

Retrieving chapter pages

Old way

// Imports used for downloading the pages to a file.
// They are not used because we're just printing the raw bytes.
// use std::fs::File;
// use std::io::Write;

use reqwest::Url;
use uuid::Uuid;

use mangadex_api::v5::MangaDexClient;

# async fn run() -> anyhow::Result<()> {
let client = MangaDexClient::default();

let chapter_id = Uuid::new_v4();

let at_home = client
    .at_home()
    .server()
    .chapter_id(&chapter_id)
    .build()?
    .send()
    .await?;

let http_client = reqwest::Client::new();

// Original quality. Use `.data.attributes.data_saver` for smaller, compressed images.
let page_filenames = at_home.chapter.data;
for filename in page_filenames {
    // If using the data-saver option, use "/data-saver/" instead of "/data/" in the URL.
    let page_url = at_home
        .base_url
        .join(&format!(
            "/{quality_mode}/{chapter_hash}/{page_filename}",
            quality_mode = "data",
            chapter_hash = at_home.chapter.hash,
            page_filename = filename
        ))
        .unwrap();

    let res = http_client.get(page_url).send().await?;
    // The data should be streamed rather than downloading the data all at once.
    let bytes = res.bytes().await?;

    // This is where you would download the file but for this example,
    // we're just printing the raw data.
    // let mut file = File::create(&filename)?;
    // let _ = file.write_all(&bytes);
    println!("Chunk: {:?}", bytes);
}

# Ok(())
# }

The new way

    use crate::{utils::download::chapter::DownloadMode, MangaDexClient};
    use anyhow::{Ok, Result};
    /// used for file exporting
    use std::{
        fs::{create_dir_all, File},
        io::Write,
    };

    /// It's from this manga called [`The Grim Reaper Falls In Love With A Human`](https://mangadex.org/title/be2efc56-1669-4e42-9f27-3bd232bca8ea/the-grim-reaper-falls-in-love-with-a-human)
    ///
    /// [Chapter 1 English](https://mangadex.org/chapter/2b4e39a5-fba0-4055-a176-8b7e19faacdb) by [`Kredim`](https://mangadex.org/group/0b870e54-c75f-4d2e-8068-c40f939135fd/kredim)
    #[tokio::main]
    async fn main() -> Result<()> {
        let output_dir = "your-output-dir";
        let client = MangaDexClient::default();
        let chapter_id = uuid::Uuid::parse_str("32b229f6-e9bf-41a0-9694-63c11191704c")?;
        let chapter_files = client
            /// We use the download builder
            .download()
            /// Chapter id (accept uuid::Uuid)
            .chapter(chapter_id)
            /// You also use `DownloadMode::Normal` if you want some the original quality
            /// 
            /// Default : Normal
            .mode(DownloadMode::DataSaver)
            /// Enable the [`The MangaDex@Home report`](https://api.mangadex.org/docs/retrieving-chapter/#the-mangadexhome-report-endpoint) if true 
            /// 
            /// Default : false
            .report(true)
            /// Something that i don`t really know about 
            /// 
            /// More details at : https://api.mangadex.org/docs/retrieving-chapter/#basics
            .force_port_443(false)
            .build()?
            .execute()
            .await?;
        create_dir_all(format!("{}{}", output_dir, chapter_id))?;
        for (filename, bytes) in chapter_files {
            let mut file: File =
                File::create(format!("{}{}/{}", output_dir, chapter_id, filename))?;
            file.write_all(&bytes)?;
        }
        Ok(())
    }

Much more cleaner !

Retrieving cover image

Old way

// Imports used for downloading the cover to a file.
// They are not used because we're just printing the raw bytes.
// use std::fs::File;
// use std::io::Write;

use reqwest::Url;
use uuid::Uuid;

use mangadex_api::types::RelationshipType;
use mangadex_api::v5::MangaDexClient;
use mangadex_api::CDN_URL;

# async fn run() -> anyhow::Result<()> {
let client = MangaDexClient::default();

let manga_id = Uuid::new_v4();
let manga = client
    .manga()
    .get()
    .manga_id(&manga_id)
    .build()?
    .send()
    .await?;

let cover_id = manga
    .data
    .relationships
    .iter()
    .find(|related| related.type_ == RelationshipType::CoverArt)
    .expect("no cover art found for manga")
    .id;
let cover = client
    .cover()
    .get()
    .cover_id(&cover_id)
    .build()?
    .send()
    .await?;

// This uses the best quality image.
// To use smaller, thumbnail-sized images, append any of the following:
//
// - .512.jpg
// - .256.jpg
//
// For example, "https://uploads.mangadex.org/covers/8f3e1818-a015-491d-bd81-3addc4d7d56a/4113e972-d228-4172-a885-cb30baffff97.jpg.512.jpg"
let cover_url = Url::parse(&format!(
        "{}/covers/{}/{}",
        CDN_URL, manga_id, cover.data.attributes.file_name
    ))
    .unwrap();

let http_client = reqwest::Client::new();

let res = http_client.get(cover_url).send().await?;
// The data should be streamed rather than downloading the data all at once.
let bytes = res.bytes().await?;

// This is where you would download the file but for this example, we're just printing the raw data.
// let mut file = File::create(&filename)?;
// let _ = file.write_all(&bytes);
println!("Chunk: {:?}", bytes);
# Ok(())
# }

The new way

Via a cover id.

    use anyhow::Result;
    use uuid::Uuid;
    use crate::MangaDexClient;
    use std::{io::Write, fs::File};

    /// Download the volume 2 cover of [Lycoris Recoil](https://mangadex.org/title/9c21fbcd-e22e-4e6d-8258-7d580df9fc45/lycoris-recoil)
    #[tokio::main]
    async fn main() -> Result<()>{
        let cover_id : Uuid = Uuid::parse_str("0bc12ff4-3cec-4244-8582-965b8be496ea")?;
        let client : MangaDexClient = MangaDexClient::default();
        let (filename, bytes) = client.download().cover().build()?.via_cover_id(cover_id).await?;
        let mut file = File::create(format!("{}/{}", "your-output-dir", filename))?;
        file.write_all(&bytes)?;
        Ok(())
    }

Via a manga id.

    use anyhow::Result;
    use uuid::Uuid;
    use crate::MangaDexClient;
    use std::{io::Write, fs::File};

    /// Download the [Kimi tte Watashi no Koto Suki Nandesho?](https://mangadex.org/title/f75c2845-0241-4e69-87c7-b93575b532dd/kimi-tte-watashi-no-koto-suki-nandesho) cover
    /// 
    /// For test... of course :3
    #[tokio::main]
    async fn main() -> Result<()>{
        let manga_id : Uuid = Uuid::parse_str("f75c2845-0241-4e69-87c7-b93575b532dd")?;
        let client : MangaDexClient = MangaDexClient::default();
        let (filename, bytes) = client
            .download()
            .cover()
            /// you can use
            /// 
            /// ```rust
            /// .quality(CoverQuality::Size512)
            /// ``` for 512
            /// or
            /// ```rust
            /// .quality(CoverQuality::Size256)
            /// ``` for 256
            .build()?.via_manga_id(manga_id).await?;
        let mut file = File::create(format!("{}/{}", "test-outputs/covers", filename))?;
        file.write_all(&bytes)?;
        Ok(())
    }

Much more cleaner this way.

That's all for today.

If you have a suggestion or a request, just open an issue.