datpiff 0.1.11

Rust library for DatPiff
Documentation
use std::time::Duration;

use scraper::{ElementRef, Html, Selector};
use surf::{Client, Config, Url};
use urlencoding::encode;

use crate::{
    constants::BASE_URL,
    types::{Mixtape, MixtapeDetails},
};

pub struct Parser {
    client: Client,
}

impl Parser {
    pub fn new() -> Self {
        let client: Client = Config::new()
            .set_base_url(Url::parse(BASE_URL).unwrap())
            .set_timeout(Some(Duration::from_secs(5)))
            .try_into()
            .unwrap();
        Self { client }
    }
    pub async fn get_latest_mixtapes(&self) -> Result<Vec<Mixtape>, surf::Error> {
        let mut mixtapes: Vec<Mixtape> = Vec::new();
        let page = self.client.get("/").recv_string().await?;
        let document = Html::parse_document(&page);
        let selector = Selector::parse(".contentItemInner").unwrap();
        for (i, element) in document.select(&selector).enumerate() {
            if i > 7 {
                continue;
            }
            let mixtape = self.parse_mixtape(&element);
            if mixtape.is_some() {
                mixtapes.push(mixtape.unwrap());
            }
        }
        Ok(mixtapes)
    }

    pub async fn get_hot_mixtapes(&self) -> Result<Vec<Mixtape>, surf::Error> {
        let mut mixtapes: Vec<Mixtape> = Vec::new();
        let page = self.client.get("/").recv_string().await?;
        let document = Html::parse_document(&page);
        let selector = Selector::parse(".contentItemInner").unwrap();
        for (i, element) in document.select(&selector).enumerate() {
            if i < 8 || i > 15 {
                continue;
            }
            let mixtape = self.parse_mixtape(&element);
            if mixtape.is_some() {
                mixtapes.push(mixtape.unwrap());
            }
        }
        Ok(mixtapes)
    }

    pub async fn get_exclusive_mixtapes(&self) -> Result<Vec<Mixtape>, surf::Error> {
        let mut mixtapes: Vec<Mixtape> = Vec::new();
        let page = self.client.get("/").recv_string().await?;
        let document = Html::parse_document(&page);
        let selector = Selector::parse(".contentItemInner").unwrap();
        for (i, element) in document.select(&selector).enumerate() {
            if i < 16 || i > 19 {
                continue;
            }
            let mixtape = self.parse_mixtape(&element);
            if mixtape.is_some() {
                mixtapes.push(mixtape.unwrap());
            }
        }
        Ok(mixtapes)
    }

    pub async fn get_top_mixtapes(&self) -> Result<Vec<Mixtape>, surf::Error> {
        let mut mixtapes: Vec<Mixtape> = Vec::new();
        let page = self.client.get("/").recv_string().await?;
        let document = Html::parse_document(&page);
        let selector = Selector::parse(".contentItemInner").unwrap();
        for (i, element) in document.select(&selector).enumerate() {
            if i < 28 {
                continue;
            }
            let mixtape = self.parse_mixtape(&element);
            if mixtape.is_some() {
                mixtapes.push(mixtape.unwrap());
            }
        }
        Ok(mixtapes)
    }

    pub async fn search_mixtapes(&self, query: &str) -> Result<Vec<Mixtape>, surf::Error> {
        let criteria = encode(query);
        let mut mixtapes: Vec<Mixtape> = Vec::new();
        let page = self
            .client
            .get(format!("/mixtapes-search.php?criteria={}", criteria))
            .recv_string()
            .await?;
        let document = Html::parse_document(&page);
        let selector = Selector::parse(".contentItemInner").unwrap();
        for element in document.select(&selector) {
            let mixtape = self.parse_mixtape(&element);
            if mixtape.is_some() {
                mixtapes.push(mixtape.unwrap());
            }
        }
        Ok(mixtapes)
    }

    pub async fn get_mixtape(&self, id: &str) -> Result<MixtapeDetails, surf::Error> {
        let page = self
            .client
            .get(format!("{}/{}.html", BASE_URL, id))
            .recv_string()
            .await?;
        let document = Html::parse_document(&page);
        let selector = Selector::parse(".tracklist").unwrap();
        let ul = document.select(&selector).next().unwrap();
        let mut tracks: Vec<String> = Vec::new();

        for (i, item) in ul
            .select(&Selector::parse(".trackTitle").unwrap())
            .map(|title| title.text().next().unwrap().to_string())
            .collect::<Vec<_>>()
            .iter()
            .enumerate()
        {
            tracks.push(format!("{}. {}", i + 1, item));
        }

        let upload = self.parse_uploader(&document);
        let details = self.parse_mixtape_details(&document);

        Ok(MixtapeDetails {
            tracks,
            uploader: upload.uploader,
            added_at: upload.added_at,
            artist: details.artist,
            title: details.title,
            dj: details.dj,
            listens: details.listens,
            cover: details.cover,
            cover_back: details.cover_back,
        })
    }

    fn parse_uploader(&self, element: &Html) -> MixtapeDetails {
        let bar = element
            .select(&Selector::parse(".awardsBar").unwrap())
            .next()
            .unwrap();
        let by = bar
            .select(&Selector::parse("a").unwrap())
            .next()
            .unwrap()
            .text()
            .next()
            .unwrap();
        let added_at = bar
            .select(&Selector::parse(".charcoal").unwrap())
            .next()
            .unwrap()
            .text()
            .next()
            .unwrap();
        MixtapeDetails {
            uploader: by.to_string(),
            added_at: added_at.to_string(),
            artist: "".to_string(),
            title: "".to_string(),
            dj: "".to_string(),
            cover: "".to_string(),
            cover_back: "".to_string(),
            listens: "".to_string(),
            tracks: Vec::new(),
        }
    }

    fn parse_mixtape_details(&self, document: &Html) -> MixtapeDetails {
        let details = document
            .select(&Selector::parse(".tapeDetails").unwrap())
            .next()
            .unwrap();

        let title = details
            .select(&Selector::parse(".title").unwrap())
            .next()
            .unwrap()
            .text()
            .next()
            .unwrap()
            .to_string();
        let artist = details
            .select(&Selector::parse(".artist").unwrap())
            .next()
            .unwrap()
            .text()
            .next()
            .unwrap()
            .to_string();
        let dj = details
            .select(&Selector::parse(".dj").unwrap())
            .next()
            .unwrap()
            .text()
            .next()
            .unwrap()
            .to_string();
        let listens = details
            .select(&Selector::parse(".listens").unwrap())
            .next()
            .unwrap()
            .text()
            .next()
            .unwrap()
            .to_string();
        let cover = document
            .select(&Selector::parse(".tapeBG").unwrap())
            .next()
            .unwrap()
            .value()
            .attr("data-front")
            .unwrap()
            .to_string();
        let cover_back = document
            .select(&Selector::parse(".tapeBG").unwrap())
            .next()
            .unwrap()
            .value()
            .attr("data-back")
            .unwrap()
            .to_string();
        MixtapeDetails {
            tracks: Vec::new(),
            uploader: String::new(),
            added_at: String::new(),
            cover,
            cover_back,
            artist,
            title,
            dj,
            listens,
        }
    }

    fn parse_mixtape(&self, element: &ElementRef) -> Option<Mixtape> {
        let div = element.select(&Selector::parse(".artist").unwrap()).next();
        let a = element.select(&Selector::parse(".title").unwrap()).next();
        let span = element.select(&Selector::parse("span").unwrap()).next();
        if div == None || a == None {
            return None;
        }
        let artist = div.unwrap().text().collect::<Vec<_>>()[0];
        let title = a.unwrap().text().collect::<Vec<_>>()[0];
        let mut link = element
            .select(&Selector::parse("a").unwrap())
            .next()
            .unwrap()
            .value()
            .attr("href")
            .unwrap()
            .to_string();
        link.remove(0);

        let listens = span.unwrap().text().collect::<Vec<_>>()[0];

        let cover = element
            .select(&Selector::parse(r#"img[alt="Mixtape Cover"]"#).unwrap())
            .next()
            .unwrap()
            .value()
            .attr("src")
            .unwrap()
            .to_string();

        return Some(Mixtape {
            id: link.replace("/", "").replace(".html", ""),
            artist: artist.to_string(),
            title: title.to_string(),
            listens: listens.to_string(),
            link: format!("{}/{}", BASE_URL, link),
            cover: format!("http:{}", cover.replace("http:", "")),
        });
    }
}