feat(api): add genre list endpoint

- Add GENRE_LIST endpoint constant
- Add GenreInfo model with id, name, color, slug, path
- Add get_genres() method to client
- Add get_genres Tauri command
This commit is contained in:
vicrodh committed 2026-02-02 08:44:42 -06:00
1 parent b6c723d3df
commit 8e31717908
5 files changed
+71 -1

No files matched your search

+27
View File
@@ -413,6 +413,33 @@ impl QobuzClient {
Ok(serde_json::from_value(albums.clone())?)
}
/// Get list of genres
pub async fn get_genres(&self, parent_id: Option<u64>) -> Result<Vec<GenreInfo>> {
let url = endpoints::build_url(paths::GENRE_LIST);
let mut query: Vec<(&str, String)> = vec![];
if let Some(pid) = parent_id {
query.push(("parent_id", pid.to_string()));
}
let response: Value = self
.http
.get(&url)
.header("X-App-Id", self.app_id().await?)
.query(&query)
.send()
.await?
.json()
.await?;
let genres = response
.get("genres")
.and_then(|g| g.get("items"))
.ok_or_else(|| ApiError::ApiResponse("No genres in response".to_string()))?;
Ok(serde_json::from_value(genres.clone())?)
}
/// Get track by ID
pub async fn get_track(&self, track_id: u64) -> Result<Track> {
let url = endpoints::build_url(paths::TRACK_GET);
+3
View File
@@ -40,6 +40,9 @@ pub mod paths {
// Label
pub const LABEL_GET: &str = "/label/get";
// Genre
pub const GENRE_LIST: &str = "/genre/list";
// Catalog (combined search)
pub const CATALOG_SEARCH: &str = "/catalog/search";
}
+25 -1
View File
@@ -289,13 +289,37 @@ pub struct LabelDetail {
pub albums_count: Option<u32>,
}
/// Genre model
/// Genre model (basic, used in album responses)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Genre {
pub id: u64,
pub name: String,
}
/// Genre info with full details (from genre/list endpoint)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenreInfo {
pub id: u64,
pub name: String,
#[serde(default)]
pub color: Option<String>,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub path: Option<Vec<u64>>,
}
/// Genre list response from API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenreListResponse {
pub genres: GenreListContainer,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenreListContainer {
pub items: Vec<GenreInfo>,
}
/// Search results container
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResults {
+15
View File
@@ -393,3 +393,18 @@ pub async fn get_label(
.await
.map_err(|e| e.to_string())
}
/// Get list of genres for filtering
#[tauri::command]
pub async fn get_genres(
parent_id: Option<u64>,
state: State<'_, AppState>,
) -> Result<Vec<crate::api::models::GenreInfo>, String> {
log::info!("Command: get_genres parent_id={:?}", parent_id);
let client = state.client.lock().await;
client
.get_genres(parent_id)
.await
.map_err(|e| e.to_string())
}
+1
View File
@@ -492,6 +492,7 @@ pub fn run() {
commands::search_all,
commands::get_album,
commands::get_featured_albums,
commands::get_genres,
commands::get_track,
commands::get_artist,
commands::get_artist_detail,