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 let catalog = cells[0].text();
38 let catalog = if catalog != "N/A" {
39 Some(catalog)
40 } else {
41 None
42 };
43
44 let title = parse_multi_language(&cells[2]);
46
47 let release_date = cells[3].text();
49 let release_date = parse_date(release_date.trim())?;
50
51 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}