aniline 0.3.0

A cli tool to search, play, download anime, create playlists and more
Documentation
use crate::AnilineError;
use crate::Client;
use regex::Regex;
use serde::Deserialize;

pub struct AllAnimeQueryResIterator {
    index: usize,
    inner: AllAnimeQueryRes,
}

#[derive(Deserialize, Debug)]
pub struct AllAnimeQueryRes {
    data: A99Data,
}

#[derive(Deserialize, Debug)]
struct A99Data {
    shows: A99Shows,
}

#[derive(Deserialize, Debug)]
struct A99Shows {
    edges: Vec<A99Entity>,
}

#[derive(Clone, Deserialize, Debug)]
pub struct A99Entity {
    #[serde(rename = "_id")]
    pub id: String,
    pub name: String,
    #[serde(rename = "availableEpisodes")]
    pub available_episodes: A99Episode,
}

#[derive(Clone, Deserialize, Debug)]
pub struct A99Episode {
    pub sub: usize,
}

impl AllAnimeQueryRes {
    pub fn search(query: &str, client: &Client) -> Result<AllAnimeQueryResIterator, AnilineError> {
        let url = format!(
            "https://allanime.site/allanimeapi?variables={{\"search\":{{\"allowAdult\":true,\"allowUnknown\":true,\"query\":\"{}\"}},\"limit\":40,\"page\":1,\"translationType\":\"{}\",\"countryOrigin\":\"ALL\"}}&extensions={{\"persistedQuery\":{{\"version\":1,\"sha256Hash\":\"9c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6\"}}}}",
            query, "sub"
        );
        let res = client.0.get(&url).send();
        let data = res?.json::<AllAnimeQueryRes>();
        Ok(AllAnimeQueryResIterator {
            index: 0,
            inner: data?,
        })
    }

    pub fn get_all_anime_video_id(
        anime: &A99Entity,
        episode: usize,
        client: &Client,
    ) -> Result<Option<(String, String)>, AnilineError> {
        let url = format!(
            "https://allanime.site/watch/{}/{}/episode-{}-sub",
            anime.id, anime.name, episode
        );
        let res = client.0.get(&url).send();
        let text = res?
            .text()?
            .replace("{", "\n")
            .replace("}", "\n")
            .replace("&referer=", "");
        let re =
            Regex::new(r#".*sourceUrl:\s*".*clock\?id=([^"]*)".*sourceName:\s*"([^"]*)""#).unwrap();
        let cap = re.captures(&text);
        Ok(cap.map(|c| (c[2].to_owned(), c[1].to_owned())))
    }

    pub fn get_video_urls(id: &str, client: &Client) -> Result<Videos, AnilineError> {
        let url = format!("https://blog.allanime.pro/apivtwo/clock.json?id={}", id);
        let res = client.0.get(&url).send();
        let videos = res?.json::<Videos>()?;
        Ok(videos)
    }
}

#[derive(Clone, Debug, Deserialize)]
pub struct Video {
    pub link: String,
    pub hls: Option<bool>,
    pub mp4: Option<bool>,
    #[serde(rename = "resolutionStr")]
    pub res: String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct Videos {
    pub links: Vec<Video>,
}

impl Iterator for AllAnimeQueryResIterator {
    type Item = A99Entity;

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.inner.data.shows.edges.get(self.index);
        self.index += 1;
        item.map(|x| x.clone())
    }
}