use std::error::Error; use regex::Regex; use reqwest::header::USER_AGENT; use sobjects::{CloudPlaylists, CloudTrack}; pub mod sobjects; const CHROME_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"; const SOUNDCLOUD_API_DOMAIN: &str = "api-v2.soundcloud.com"; const SOUNDCLOUD_DOMAIN: &str = "https://soundcloud.com"; // version: 1738322252 // likes: https://api-v2.soundcloud.com/users/774639751/likes?client_id=zFEmsF1cEZZQ92nRRXKOg7e6ibFR1L7c&limit=10&offset=0&linked_partitioning=1&app_version=1734537250&app_locale=en // playlists: https://api-v2.soundcloud.com/users/774639751/playlists_without_albums?client_id=zFEmsF1cEZZQ92nRRXKOg7e6ibFR1L7c&limit=10&offset=0&linked_partitioning=1&app_version=1734537250&app_locale=en /*#[tokio::main] async fn main() -> Result<(), Box> { let app_version = get_app().await.unwrap().unwrap(); let client_id = get_client_id().await.unwrap().unwrap(); let user_id: u64 = 774639751; println!("Playlists: {:#?}", get_playlists(user_id, client_id, app_version).await.unwrap()); Ok(()) }*/ pub async fn get_likes( user_id: u64, client_id: String, app_version: String, ) -> Result, Box> { let client = reqwest::Client::new(); let resp = client.get(format!("https://{}/users/{}/likes?client_id={}&limit=10&offset=0&linked_partitioning=1&app_version={}&app_locale=en", SOUNDCLOUD_API_DOMAIN, user_id, client_id, app_version) ) .header(USER_AGENT, CHROME_USER_AGENT) .send() .await? .text() .await?; Ok(Some(resp)) } pub async fn get_tracks( pl: Vec, client_id: String, app_version: String, ) -> Result, Box> { let ids = pl .iter() .map(|t| t.id.to_string()) .collect::>() .join(","); let client = reqwest::Client::new(); let resp = client .get(format!( "https://{}/tracks?ids={}&client_id={}&app_version={}&app_locale=en", SOUNDCLOUD_API_DOMAIN, ids, client_id, app_version )) .header(USER_AGENT, CHROME_USER_AGENT) .send() .await? .text() .await?; let tracks: Vec = serde_json::from_str(&resp)?; Ok(tracks) } pub async fn get_playlists( user_id: u64, client_id: String, app_version: String, ) -> Result> { let client = reqwest::Client::new(); let resp = client.get(format!("https://{}/users/{}/playlists_without_albums?client_id={}&limit=10&offset=0&linked_partitioning=1&app_version={}&app_locale=en", SOUNDCLOUD_API_DOMAIN, user_id, client_id, app_version)) .header(USER_AGENT, CHROME_USER_AGENT) .send() .await? .text() .await?; let playlists: CloudPlaylists = serde_json::from_str(&resp)?; Ok(playlists) } pub async fn get_app() -> Result, Box> { let client = reqwest::Client::new(); let resp = client .get(format!("{}/versions.json", SOUNDCLOUD_DOMAIN)) .header(USER_AGENT, CHROME_USER_AGENT) .send() .await? .text() .await?; let json: serde_json::Value = serde_json::from_str(&resp).expect("JSON was not well-formatted"); Ok(Some(json.get("app").unwrap().as_str().unwrap().to_string())) } pub async fn get_client_id() -> Result, Box> { let client = reqwest::Client::new(); let resp = client .get(format!("{}/soundcloud", SOUNDCLOUD_DOMAIN)) .header(USER_AGENT, CHROME_USER_AGENT) .send() .await? .text() .await?; let rg = Regex::new(r#"src=".+""#).unwrap(); let mut urls = Vec::new(); for cap in Regex::new(r#"