Skip to content

Releases: tonymushah/mangadex-api

mangadex v3.2.0 minor changes

11 Mar 18:18
d12b04e
Compare
Choose a tag to compare

mangadex-api v3.2 is here

Hello everyone!

I'm here to give you some changes logs about this realease:

Quick crate version overview

Crate Version
mangadex-api 3.2
mangadex-api-types-rust 0.8
mangadex-api-schema-rust 0.8
mangadex-api-input-types 0.4

That's it!
Have a nice day everyone!

mangadex v3.0.1 Patches

24 Dec 16:31
ac3ad1d
Compare
Choose a tag to compare

mangadex-api-types-rust v0.6.1

  • Added Language::get_langs to get an array of all local language
  • Added Tag::get_all_tags to get an array of all local tags

mangadex-api-schema-rust v0.6.1

  • Added mangadex-api-types-rust::Tag conversion to ApiObjectNoRelationship<TagAttributes>
  • Fixed Relationship conversion handler for Artist, Creator, Member, Leader

mangadex-api v3.0.1

  • Implemented send() to UnFollowCustomListBuilder

Happy Christmas everyone! πŸŽ…πŸŽ…
I hope you're enjoying your Christmas Eve well.

`mangadex-api` v3 is here πŸ₯³

07 Dec 11:18
Compare
Choose a tag to compare

Hello everyone,
I'm very happy and excited to announce to you:

The Release of mangadex-api v3

along with mangadex-api-types/schema-rust v0.6

First,
As mentioned in v3 annoucement, 3/4 (or maybe half) of the feature goal was added.

1- The custom_list_v2 feature for mangadex-api:

It's the support of the subscription system, announced in August 2023. (Ref: here).
There might be some missing endpoint in this feature (like /subscription,...) because it's not deployed yet on api.mangadex.org but on api.mangadex.dev (their public dev environment).
Even if it's not deployed yet, i want to make it stable in v3.1, so stay tuned for that.

2- The upload stabilization :

A days ago, i got the time to try and test the upload endpoints.
It works and the chapter chapter got uploaded to the Official "Test" Manga (the uploaded chapter is here), but it somehow contains some bugs (ref here)

Some discord note ![image](https://github.com/tonymushah/mangadex-api/assets/95529016/813a762d-0be1-4a3a-9314-947711cfad28)

The test/example code is available in mangadex-api/examples/upload.rs

3- mangadex_api_schema_rust::v5::Relationship can now convert to mangadex_api_schema_rust::ApiObjectNoRelationships

I added some TryFrom implementation to Relationship.
This allow you to not add a lot of boilerplate code that does the same thing (converting a relationship to ApiObject πŸ˜΅β€πŸ’«)
Note: This converstion only support :

4- tokio-multi-thread and rw-mutli-thread feature for mangadex-api

Sometimes, in a really big project, you often need a good concurent program.
To satisfy this demand, I added the rw-mutli-thread feature. Instead of using the futures::lock::Mutex in the multi-thread, it'll use tokio::sync::RwLock.
Note that all invoked requests the client data. Only login and refresh_token (for both legacy-auth and oauth) write the client data.
And tokio-multi-thread is just use tokio::sync::Mutex instead of

5- oauth feature (enabled by default)

A few weeks ago, Mangadex introduced personal-clients, the official (and also simple πŸ˜‚) way for 3rd party software and SDK to support authentification with the OAuth introduced last year.

There is a simple demo on the README but there is also a few examples at mangadex-api/examples:

6- the new syntax

As mentioned in the v3 annoucement, the SDK will got the breaking change feature.
Quick reminder:
if we have an endpoint like this:

PUT /manga/{id}
parameters
title: LocalizedString
version: int
authors: string[] (uuid)
year: int

the invocation should be like this:

/// I assume that you already declared a `MangaDexClient` somewhere.
client.
    // /manga/{id}
    .manga()
    .id(/* Some manga Id here */)
    // the HTTP method
    .put()
    // Parameters
    .version(3_u32)
    .title(/* some LocalizedString here */)
    .year(2023)
    .authors(vec![Uuid::new_v4()])
    // Send the request
    .send()
    .await?

In previous versions, you often need to build the request first, but now it's not required.

7- utils feature

The type alias DownloadElement have been changed from (String, Option<Bytes>) to (String, Result<Bytes>) where the String is for the filename and the Result<Bytes> is for the image bytes.

I also added a new module upload for upload utilities.

8- RateLimit handling

Please refer to #26 for more details

That's (maybe) it! πŸ™‚

If you want to use the new mangadex-api v3,
Just update your Cargo.toml

# ...
# Types and schemas are always required
mangadex-api-types-rust = "0.6"
mangadex-api-schema-rust = "0.6"
mangadex-api = { version = "3.0.0", feature = ["mutli-thread"]}
# Please note that `oauth` is enabled by default

Thanks for your amazing support ☺️.
See you in next versions.
@tonymushah

`utils` features Annoucement (MangadexApi 2.1.0)

12 Jun 22:57
Compare
Choose a tag to compare

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.