15 个稳定版本

3.3.0 2024 年 4 月 22 日
3.2.0 2024 年 3 月 11 日
3.1.0 2024 年 2 月 5 日
3.0.2 2023 年 12 月 25 日
0.2.1 2020 年 12 月 29 日

#84 in 网页开发

Download history 345/week @ 2024-04-27 59/week @ 2024-05-04 37/week @ 2024-05-11 81/week @ 2024-05-18 133/week @ 2024-05-25 86/week @ 2024-06-01 98/week @ 2024-06-08 38/week @ 2024-06-15 481/week @ 2024-06-22 179/week @ 2024-06-29 135/week @ 2024-07-06 296/week @ 2024-07-13 175/week @ 2024-07-20 328/week @ 2024-07-27 192/week @ 2024-08-03 212/week @ 2024-08-10

每月下载量 937
2 crates 中使用

MIT/Apache

1MB
28K SLoC

mangadex-api

Rust Crates.io Documentation Crates.io (recent)

重要

此 Git 仓库仅是从 gondolyr/mangadex-api 分支的 fork,但由于项目和 crate 已被删除,所以我将现在维护此 crate 以供 special-eurekaeureka-manager 使用

mangadex-api crate 提供了一个方便、高级的包装,客户端 用于 MangaDex API,使用 Rust 编写。

它涵盖了 其文档 中涵盖的所有公共端点。

文档(docs.rs)

旧仓库 main 分支)

请注意,由于 MangaDex 仍在测试中,此 SDK 可能会经历突然的重大更改(很多)。

免责声明

mangadex-apiMangaDex 无关。

目录

工作区详细信息

需求

返回顶部

如何安装

返回顶部

mangadex-api 添加到您的依赖项

[dependencies]
# ...
# Types and schemas are always required
mangadex-api-types-rust = "0.9"
mangadex-api-schema-rust = "0.9"
mangadex-api = "3.3"

如果您正在使用 cargo-edit,请运行

cargo add mangadex-api

依赖项解释

依赖项 用途 包含
anyhow 捕获意外错误。 always
mangadex-api-types-rust Mangadex API 的枚举和静态数据 always
mangadex-api-schema-rust Mangadex API 使用的类型 always
clap 演示库功能的示例 开发构建
derive_builder 方便为 API 端点构建器生成设置器。 always
fake 为单元测试生成随机数据。 开发构建
futures 异步请求处理。 always
reqwest MangaDex API 发送 HTTP 请求。 always
serde 将 HTTP 响应体序列化为结构体。 always
serde_json 为单元测试创建 JSON 对象。 开发构建
serde_qs HTTP 请求的查询字符串序列化。 always
thiserror 自定义错误处理。 always
time 处理时间字段的便利类型。 always
tokio 异步运行时,用于处理示例中的 futures 以及章节报告中的 utils 功能(仅在 tokio-mutli-threadrw-mutli-thread 中)。 开发构建 + utils 功能
url 方便的 Url 类型,用于验证和包含 URL。 always
uuid 方便的 Uuid 类型,用于验证和包含请求和响应的 UUID。还用于为测试随机生成 UUID。 always
wiremock HTTP 模拟以测试 MangaDex API 开发构建

功能

返回顶部

默认情况下不包含所有功能。要启用它们,请将以下任何一个添加到您的项目 Cargo.toml 文件中。

  • multi-thread

    启用 MangaDexClient 的线程安全,但代价是操作稍微昂贵。

  • legacy-auth 已弃用

    启用 SDK 中使用 < 5.9.0 登录系统。请访问 Mangadex Discord 获取更多信息

  • utils

    启用使用 MangaDexClient::download()。允许您在不流泪和长代码的情况下下载章节或封面图片。

  • tokio-multi-thread

    启用对 tokio::sync::Mutex 的使用,而不是 futures::lock::Mutex

  • rw-mutli-thread

    在客户端中启用对 tokio::sync::RwLock 的使用,而不是 futures::lock::Mutex。如果您希望实现灵活的并发多线程,这可能很有用。

  • legacy-user-delete 已弃用

    启用使用用户删除端点。

  • oauth (默认启用)

    启用使用 MangaDex API 5.9.0 中引入的全新 OAuth 2.0 登录。

    快速提示:oauth 功能使用 个人客户端 方法,这意味着您需要注册一个个人客户端并等待其验证。更多详情请参阅 此处

  • custom_list_v2 : 启用即将推出的自定义列表系统。请注意,这些端点尚未在 api.mangadex.org 上部署,但您可以在 api.mangadex.dev(他们的实时开发API)上使用它们。更多信息请参阅 MangaDex 论坛上的 Follows/CustomList API Changelog - BREAKING CHANGES

例如,要启用 multi-thread 功能,请将以下内容添加到您的 Cargo.toml 文件中

mangadex-api = { version = "3.2.0", features = ["multi-thread"] }

HTTP 客户端

返回顶部

mangadex_api::MangaDexClient 是异步的,使用 reqwest 作为 HTTP 客户端。

响应结构体

返回顶部

响应结构体可以在 mangadex-api-schema-rust 中找到,并包含 JSON 响应中的字段。

入门

返回顶部

此示例演示了如何获取随机漫画。

use mangadex_api::v5::MangaDexClient;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = MangaDexClient::default();

    let random_manga = client
        .manga()
        .random()
        .get()
        .send()
        .await?;

    println!("{:?}", random_manga);

    Ok(())
}

此示例演示了如何获取 Mangadex 人气标题

use mangadex_api::MangaDexClient;
use mangadex_api_schema_rust::v5::RelatedAttributes;
use mangadex_api_types_rust::{
    Language, MangaDexDateTime, MangaSortOrder, OrderDirection, ReferenceExpansionResource,
};
use time::{Duration, OffsetDateTime};
use url::Url;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = MangaDexClient::default();

    // Take the local date and put substract it with 30 days
    let created_at_since = OffsetDateTime::now_utc()
        .checked_sub(Duration::days(30))
        .unwrap();

    let created_at_since = MangaDexDateTime::new(&created_at_since);

    let res = client
        .manga()
        .get()
        // We pick up all manga that has been created during these last 30 days
        .created_at_since(created_at_since)
        // Mangadex Popular Titles is ordered by followedCount descending
        .order(MangaSortOrder::FollowedCount(OrderDirection::Descending))
        // We include the author data
        .include(ReferenceExpansionResource::Author)
        // We include the arstits data
        .include(ReferenceExpansionResource::Artist)
        .send()
        .await?;

    let not_found = String::from("Not found");
    // Just a simple index :3
    let mut index = 1;
    for manga in res.data {
        // Find the English title
        let title = manga
            .attributes
            .title
            .get(&Language::English)
            .unwrap_or(&not_found);
        println!("{index} ~ {title}");
        // Find the author name
        let author = manga
            .find_first_relationships(mangadex_api_types::RelationshipType::Author)
            .and_then(|e| {
                e.attributes.clone().map(|rel| match rel {
                    RelatedAttributes::Author(a) => a.name,
                    _ => not_found.clone(),
                })
            })
            .unwrap_or(not_found.clone());
        println!("\tAuthor: {author}");
        // Find the author name
        let artist = manga
            .find_first_relationships(mangadex_api_types::RelationshipType::Artist)
            .and_then(|e| {
                e.attributes.clone().map(|rel| match rel {
                    RelatedAttributes::Author(a) => a.name,
                    _ => not_found.clone(),
                })
            })
            .unwrap_or(not_found.clone());
        // Print the artist name if it's different of the author
        if artist != author {
            println!("\tArtist: {artist}");
        }
        // We generate the link that goes to the Mangadex page
        let title_link =
            Url::parse("https://mangadex.org/title/")?.join(manga.id.to_string().as_str())?;
        println!("\tLink: {title_link}");
        println!();
        index += 1;
    }
    println!("Done :3");
    Ok(())
}

使用自定义 reqwest 客户端

返回顶部

默认情况下,mangadex_api::MangaDexClient 将使用默认的 reqwest::Client 设置。

您可以提供自己的 reqwest::Client 来自定义选项,例如请求超时。

use reqwest::Client;

use mangadex_api::v5::MangaDexClient;

# async fn run() -> anyhow::Result<()> {
let reqwest_client = Client::builder()
    .timeout(std::time::Duration::from_secs(10))
    .build()?;

let client = MangaDexClient::new(reqwest_client);
# Ok(())
# }

按标题搜索漫画

返回顶部

参考:https://api.mangadex.org/swagger.html#/Manga/get-search-manga

use mangadex_api::v5::MangaDexClient;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = MangaDexClient::default();

    let manga_results = client
        .manga()
        .get()
        .title("full metal")
        .send()
        .await?;

    println!("manga results = {:?}", manga_results);
    Ok(())
}

使用参考扩展按标题搜索漫画

返回顶部

每次获取都将包括所有关系,但仅包含最小信息,例如关系类型和ID。参考扩展将包括请求中添加的类型的结果中的完整JSON对象。

在下面的示例中,关系列表中的任何相关作者都会在结果中提供详细的信息,例如作者的名字、传记和网站。

参考文献

use mangadex_api::v5::schema::RelatedAttributes;
use mangadex_api::v5::MangaDexClient;
// use mangadex_api_types_rust::{ReferenceExpansionResource, RelationshipType};
use mangadex_api_types::{ReferenceExpansionResource, RelationshipType};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = MangaDexClient::default();

    let manga_results = client
        .manga()
        .get()
        .title("full metal")
        .include(&ReferenceExpansionResource::Author)
        .send()
        .await?;

    println!("manga results = {:?}", manga_results);

    let authors = manga_results.data.iter().filter_map(|manga| {
        manga
            .relationships
            .iter()
            .find(|&rel| rel.type_ == RelationshipType::Author)
    });

    for author in authors {
        if let Some(RelatedAttributes::Author(author_attributes)) = &author.attributes {
            println!("{} - {}", author.id, author_attributes.name);
        }
    }
    Ok(())
}

下载章节页面

返回顶部

参考文献: https://api.mangadex.org/docs/reading-chapter/

使用旧方法

// 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 uuid::Uuid;

use mangadex_api::v5::MangaDexClient;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = MangaDexClient::default();

    // Yeah, i'm a [`100 girlfriend`](https://mangadex.org/title/efb4278c-a761-406b-9d69-19603c5e4c8b/the-100-girlfriends-who-really-really-really-really-really-love-you) simp and what! >:)
    let chapter_id = Uuid::parse_str("f2a09509-3c09-4371-a810-ecb99242bd90")?;

    let at_home = client
        .at_home()
        .server()
        .id(chapter_id)
        .get()
        .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(())
}

使用 utils 功能

基于 (filename, Result<bytes>) 向量

如果您想要处理每个响应错误,则不建议使用此方法。

use anyhow::Result;
use mangadex_api::{utils::download::chapter::DownloadMode, MangaDexClient};
/// 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()?
        .download_element_vec()
        .await?;
    create_dir_all(format!("{}{}", output_dir, chapter_id))?;
    for (filename, bytes_) in chapter_files {
        if let Ok(bytes) = bytes_ {
            let mut file: File =
                File::create(format!("{}{}/{}", output_dir, chapter_id, filename))?;
            file.write_all(&bytes)?;
        } else if let Err(e) = bytes_ {
            eprintln!("{}", e);
        }
    }
    Ok(())
}

通过 tokio-stream

使用 tokio-stream,您可以为每个响应结果进行处理。

不带检查器
use anyhow::Result;
use mangadex_api::{utils::download::chapter::DownloadMode, MangaDexClient};
use std::{
    fs::{create_dir_all, File},
    io::Write,
};
use tokio::pin;
use tokio_stream::StreamExt;

/// It's from this manga called [`Keiken Zumi na Kimi to, Keiken Zero na Ore ga, Otsukiai Suru Hanashi`](https://mangadex.org/title/1c8f0358-d663-4d60-8590-b5e82890a1e3/keiken-zumi-na-kimi-to-keiken-zero-na-ore-ga-otsukiai-suru-hanashi)
///
/// [Chapter 13 English](https://mangadex.org/chapter/250f091f-4166-4831-9f45-89ff54bf433b) by [`Galaxy Degen Scans`](https://mangadex.org/group/ab24085f-b16c-4029-8c05-38fe16592a85/galaxy-degen-scans)
#[tokio::main]
async fn main() -> Result<()> {
    let output_dir = "./test-outputs";
    let client = MangaDexClient::default();
    let chapter_id = uuid::Uuid::parse_str("250f091f-4166-4831-9f45-89ff54bf433b")?;
    create_dir_all(format!("{}/{}", output_dir, chapter_id))?;
    let download = 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/04-chapter/retrieving-chapter/) if true
        //
        // Default : false
        .report(true)
        // Something that i don`t really know about
        //
        // More details at : https://api.mangadex.org/docs/04-chapter/retrieving-chapter/
        .force_port_443(false)
        .build()?;
    let chapter_files = download.download_stream().await?;
    // `pin!` Required for iteration
    pin!(chapter_files);
    while let Some((data, index, total)) = chapter_files.next().await {
        let (filename, bytes_) = data;
        // Prin the progression in the standart output
        println!("{index} / {total} : {filename} ");
        if let Ok(bytes) = bytes_ {
            let mut file: File =
                File::create(format!("{}/{}/{}", output_dir, chapter_id, filename))?;
            file.write_all(&bytes)?;
            println!("downloaded");
        } else if let Err(e) = bytes_ {
            eprintln!("{e}");
        }
    }
    Ok(())
}
带检查器

检查器是在获取响应之后、检索字节内容之前调用的函数。示例

    /// Some code here
    let download = client
        .download()
        .chapter(chapter_id)
        .mode(DownloadMode::DataSaver)
        .report(true)
        .build()?;
    let chapter_files = download
        .download_stream_with_checker(move |filename, response| {
            /// if this function return `true`, the current response will be skipped
            true
        })
        .await?;
    /// Some code here too

实际示例

如果已创建具有响应内容长度的文件,则检查器将返回 true

use anyhow::Result;
use mangadex_api::{utils::download::chapter::DownloadMode, MangaDexClient};
use std::{
    fs::{create_dir_all, File},
    io::Write,
};
use tokio::pin;
use tokio_stream::StreamExt;

/// It's from this manga called [`Keiken Zumi na Kimi to, Keiken Zero na Ore ga, Otsukiai Suru Hanashi`](https://mangadex.org/title/1c8f0358-d663-4d60-8590-b5e82890a1e3/keiken-zumi-na-kimi-to-keiken-zero-na-ore-ga-otsukiai-suru-hanashi)
///
/// [Chapter 13 English](https://mangadex.org/chapter/250f091f-4166-4831-9f45-89ff54bf433b) by [`Galaxy Degen Scans`](https://mangadex.org/group/ab24085f-b16c-4029-8c05-38fe16592a85/galaxy-degen-scans)
#[tokio::main]
async fn main() -> Result<()> {
    let output_dir = "./test-outputs";
    let client = MangaDexClient::default();
    let chapter_id = uuid::Uuid::parse_str("250f091f-4166-4831-9f45-89ff54bf433b")?;
    create_dir_all(format!("{}/{}", output_dir, chapter_id))?;
    let download = 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/04-chapter/retrieving-chapter/) if true
        //
        // Default : false
        .report(true)
        // Something that i don`t really know about
        //
        // More details at : https://api.mangadex.org/docs/04-chapter/retrieving-chapter/
        .force_port_443(false)
        .build()?;
    let chapter_files = download
        .download_stream_with_checker(move |filename, response| {
            let is_skip: bool = {
                // Get the response content length
                let content_length = match response.content_length() {
                    None => return false,
                    Some(d) => d,
                };
                // open the chapter image file
                File::open(format!(
                    "{}/{}/{}",
                    output_dir,
                    chapter_id,
                    filename.filename.clone()
                ))
                .map(|pre_file| {
                    pre_file
                        .metadata()
                        .map(|metadata| metadata.len() == content_length)
                        .unwrap_or(false)
                })
                .unwrap_or(false)
            };
            is_skip
        })
        .await?;
    // `pin!` Required for iteration
    pin!(chapter_files);
    while let Some((data, index, total)) = chapter_files.next().await {
        let (filename, bytes_) = data;
        // Prin the progression in the standart output
        println!("{index} / {total} : {filename} ");
        if let Ok(bytes) = bytes_ {
            let mut file: File =
                File::create(format!("{}/{}/{}", output_dir, chapter_id, filename))?;
            file.write_all(&bytes)?;
            println!("downloaded");
        } else if let Err(e) = bytes_ {
            eprintln!("{e}");
        }
    }
    Ok(())
}

下载漫画的主要封面图像

返回顶部

使用旧方法

虽然这个示例可以通过传递封面ID直接获取封面信息,但通常情况下,人们不会手头上有ID,所以最常见的方法是从漫画结果中获取。

如果您想要获取所有漫画的封面图片,则需要使用 封面列表端点 并使用 manga[] 查询参数。

// 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::v5::MangaDexClient;
use mangadex_api::CDN_URL;
// use mangadex_api_types_rust::RelationshipType;
use mangadex_api_types::RelationshipType;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = MangaDexClient::default();

    let manga_id = Uuid::new_v4();
    let manga = client.manga().id(manga_id).get().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().cover_id(cover_id).get().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(())
}

通过封面 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(())
    }

通过漫画 ID

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

/// 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 output_dir = String::from("test-outputs");
    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 bytes = bytes?;
    let mut file = File::create(format!("{}/{}", output_dir, filename))?;
    file.write_all(&bytes)?;
    println!("donwloaded :3");
    Ok(())
}

身份验证(通过 oauth 功能)

在复制示例代码之前,我建议您阅读Mangadex 认证部分

首先,在Mangadex 个人资料设置注册个人客户端,并等待工作人员批准。目前可能需要2到3天,所以请耐心等待 :>

经过一段时间,您现在可以通过 oauth 功能进行 login

登录

use mangadex_api::MangaDexClient;
use mangadex_api_schema::v5::oauth::ClientInfo;
use mangadex_api_types::{Password, Username};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut client = MangaDexClient::default();
    client
        .set_client_info(&ClientInfo {
            client_id: String::from("<SET YOUR CLIENT ID HERE>"),
            client_secret: String::from("<SET YOUR CLIENT INFO HERE>"),
        })
        .await?;
    let response = client
        .oauth()
        .login()
        .username(Username::parse("<YOUR USERNAME HERE>")?)
        .password(Password::parse("<YOUR PASSWORD HERE>")?)
        .send()
        .await?;
    /*
       println!("Access Token: {}", response.access_token);
    */
    println!("Expires in {} minutes", response.expires_in / 60);
    Ok(())
}

刷新您的令牌

您只需调用 mangadex_api::MangaDexClient::oauth().refresh()

    ...
    client
        .oauth()
        .refresh()
        .send()
        .await?;
    ...

示例

use mangadex_api::MangaDexClient;
// use mangadex_api_schema_rust::v5::oauth::ClientInfo;
// use mangadex_api_types_rust::{Password, Username};
use mangadex_api_schema::v5::oauth::ClientInfo;
use mangadex_api_types::{Password, Username};
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut client = MangaDexClient::default();

    // Register your client info
    client
        .set_client_info(&ClientInfo {
            client_id: String::from("<SET YOUR CLIENT ID HERE>"),
            client_secret: String::from("<SET YOUR CLIENT INFO HERE>"),
        })
        .await?;

    // Login to your account
    let response = client
        .oauth()
        .login()
        .username(Username::parse("<YOUR USERNAME HERE>")?)
        .password(Password::parse("<YOUR PASSWORD HERE>")?)
        .send()
        .await?;
    /*
       println!("Access Token: {}", response.access_token);
    */
    println!("Expires in {} minutes", response.expires_in / 60);
    // Wait until the token expires

    sleep(Duration::from_secs(<u64 as TryFrom<usize>>::try_from(
        response.expires_in,
    )?))
    .await;

    // Refresh the session token
    let response = client.oauth().refresh().send().await?;
    /*
       println!("Access Token: {}", response.access_token);
    */
    println!("Expires in {} minutes", response.expires_in / 60);
    Ok(())
}

许可证

返回顶部

许可于以下之一

任选其一。

贡献

返回顶部

除非您明确表示,否则您根据Apache-2.0许可证定义的任何旨在包含在作品中的有意贡献,都应如上所述双重许可,而无需任何额外条款或条件。

贡献者

返回顶部

我们欢迎所有人的贡献。有许多方式可以做出贡献,而CONTRIBUTING.md 文档解释了您如何做出贡献并开始。

依赖项

~5–20MB
~255K SLoC