kopuz-utils 0.8.0

A modern, lightweight music player built with Rust and Dioxus.
Documentation
use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
use std::time::Duration;

pub async fn track_page_url(release_id: Option<&str>, artist: &str, title: &str) -> Option<String> {
    if let Some(id) = release_id {
        let id = id.trim();
        if !id.is_empty() {
            return Some(format!("https://musicbrainz.org/release/{id}"));
        }
    }

    let recording_id = search_recording_id(artist, title).await?;
    Some(format!("https://musicbrainz.org/recording/{recording_id}"))
}

async fn search_recording_id(artist: &str, title: &str) -> Option<String> {
    let title = title.trim();
    if title.is_empty() {
        return None;
    }

    let mut query = format!("recording:\"{}\"", escape_lucene(title));
    let artist = artist.trim();
    if !artist.is_empty() {
        query.push_str(&format!(" AND artist:\"{}\"", escape_lucene(artist)));
    }

    let url = format!(
        "https://musicbrainz.org/ws/2/recording?query={}&fmt=json&limit=1",
        utf8_percent_encode(&query, NON_ALPHANUMERIC)
    );

    let client = reqwest::Client::builder()
        .connect_timeout(Duration::from_secs(5))
        .timeout(Duration::from_secs(15))
        .build()
        .ok()?;
    let res = client
        .get(&url)
        .header(
            "User-Agent",
            concat!(
                "Kopuz/",
                env!("CARGO_PKG_VERSION"),
                " (https://github.com/owlenz/kopuz)"
            ),
        )
        .header("Accept", "application/json")
        .send()
        .await
        .ok()?;

    if !res.status().is_success() {
        return None;
    }

    let body = res.text().await.ok()?;
    let json: serde_json::Value = serde_json::from_str(&body).ok()?;
    json.get("recordings")?
        .as_array()?
        .first()?
        .get("id")?
        .as_str()
        .map(|s| s.to_string())
}

fn escape_lucene(input: &str) -> String {
    input.replace('\\', "\\\\").replace('"', "\\\"")
}