convert_invert/internals/query/
query_manager.rs1use crate::internals::{
2 context::context_manager::Track, parsing::deserialize, search::search_manager::SearchItem,
3};
4use anyhow::{Context, bail};
5use spotify_rs::model::PlayableItem;
6
7#[derive(Debug, Clone)]
8pub struct QueryManager {
9 pub playlist_url: String,
10 pub search_timeout_secs: u8,
11 client_id: Option<String>,
12 client_secret: Option<String>,
13}
14
15impl QueryManager {
16 pub fn new(
17 playlist_url: impl Into<String>,
18 client_id: Option<String>,
19 client_secret: Option<String>,
20 ) -> Self {
21 Self::new_with_timeout(playlist_url, client_id, client_secret, 10)
22 }
23
24 pub fn new_with_timeout(
25 playlist_url: impl Into<String>,
26 client_id: Option<String>,
27 client_secret: Option<String>,
28 search_timeout_secs: u8,
29 ) -> Self {
30 let playlist_url = playlist_url.into();
31 QueryManager {
32 playlist_url,
33 search_timeout_secs,
34 client_id,
35 client_secret,
36 }
37 }
38 pub async fn fetch_playlist(self) -> anyhow::Result<Vec<Track>> {
39 let Some(client_id) = self.client_id else {
40 bail!("CLIENT_ID is required to fetch Spotify playlist");
41 };
42 let Some(client_secret) = self.client_secret else {
43 bail!("CLIENT_SECRET is required to fetch Spotify playlist");
44 };
45 let spotify = spotify_rs::ClientCredsClient::authenticate(client_id, client_secret)
46 .await
47 .context("Authenticate Spotify client credentials")?;
48
49 let playlist = spotify_rs::playlist(self.playlist_url)
50 .market("US")
51 .get(&spotify)
52 .await
53 .context("Fetch Spotify playlist")?;
54 let pl = playlist
55 .tracks
56 .get_all(&spotify)
57 .await
58 .context("Paginating")?
59 .into_iter()
60 .flatten()
61 .flat_map(|track| {
62 if let PlayableItem::Track(song) = track.track {
63 let Some(artist) = song.artists.first() else {
64 tracing::warn!(track = song.name, "Skipping Spotify track without artist");
65 return None;
66 };
67 Some(Track::Query(SearchItem::new(
68 song.id,
69 song.name,
70 song.album.name,
71 artist.name.clone(),
72 )))
73 } else {
74 None
75 }
76 })
77 .collect::<Vec<_>>();
78 tracing::info!(track_count = pl.len(), "Fetched Spotify playlist");
79 Ok(pl)
80 }
81 pub async fn run(&self) -> anyhow::Result<Vec<Track>> {
82 let data_string = include_str!("../parsing/sample.json");
83 let data: deserialize::Playlist =
84 serde_json::from_str(data_string).context("Deserializing")?;
85 let queries: Vec<SearchItem> = data.into();
86 let vals = queries.into_iter().map(Track::Query).collect();
87 Ok(vals)
88 }
89}