anni_vgmdb/
client.rs

1use crate::models::{AlbumDetail, AlbumInfo, SearchResponse, SearchResult};
2use crate::utils::{parse_date, parse_multi_language};
3use crate::Result;
4use select::document::Document;
5use select::predicate::{Attr, Name, Predicate};
6use std::str::FromStr;
7
8#[derive(Default)]
9pub struct VGMClient {
10    client: reqwest::Client,
11}
12
13impl VGMClient {
14    pub async fn search_albums(&self, query: &str) -> Result<SearchResponse<'_>> {
15        let response = self
16            .client
17            .get(&format!("https://vgmdb.net/search?type=album&q={query}"))
18            .header("Cookie", "TODO")
19            .send()
20            .await?;
21        if response.url().path().starts_with("/album") {
22            Ok(SearchResponse::new(
23                self,
24                SearchResult::Album(AlbumDetail::from_str(&response.text().await?)?),
25            ))
26        } else {
27            let html = response.text().await?;
28            let document = Document::from(html.as_str());
29
30            let mut results = Vec::new();
31            for row in document.find(
32                Attr("id", "albumresults").descendant(Name("tr").and(Attr("rel", "rel_invalid"))),
33            ) {
34                let cells = row.find(Name("td")).collect::<Vec<_>>();
35
36                // 1. get catalog
37                let catalog = cells[0].text();
38                let catalog = if catalog != "N/A" {
39                    Some(catalog)
40                } else {
41                    None
42                };
43
44                // 2. get multi-language title
45                let title = parse_multi_language(&cells[2]);
46
47                // 3. get release date
48                let release_date = cells[3].text();
49                let release_date = parse_date(release_date.trim())?;
50
51                // 4. get album link
52                let link = cells[2]
53                    .find(Name("a"))
54                    .next()
55                    .unwrap()
56                    .attr("href")
57                    .unwrap()
58                    .to_string();
59
60                results.push(AlbumInfo {
61                    catalog,
62                    title,
63                    release_date,
64                    id: link
65                        .strip_prefix("https://vgmdb.net/album/")
66                        .unwrap()
67                        .to_string(),
68                });
69            }
70
71            Ok(SearchResponse::new(self, SearchResult::List(results)))
72        }
73    }
74
75    pub async fn album(&self, id: &str) -> Result<AlbumDetail> {
76        let response = self
77            .client
78            .get(&format!("https://vgmdb.net/album/{id}"))
79            .header("Cookie", "TODO")
80            .send()
81            .await?;
82        let html = response.text().await?;
83        AlbumDetail::from_str(html.as_str())
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use crate::client::VGMClient;
90
91    #[tokio::test]
92    async fn test_search() -> Result<(), Box<dyn std::error::Error>> {
93        let client = VGMClient::default();
94        let result = client.search_albums("BNEI-ML").await?;
95        println!("{:#?}", result);
96        Ok(())
97    }
98
99    #[tokio::test]
100    async fn test_album() -> Result<(), Box<dyn std::error::Error>> {
101        let client = VGMClient::default();
102        let result = client.search_albums("LACA-9356~7").await?;
103        let album = result.into_album(None).await?;
104        println!("{:#?}", album);
105        Ok(())
106    }
107}