use std::{collections::HashMap, error::Error, fmt::format, fs::File, io::Write}; use regex::Regex; use reqwest::header::{HOST, ORIGIN, REFERER, USER_AGENT}; use sobjects::CloudPlaylists; 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(()) }*/ 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)) } 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) } 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?; println!("Resp: {}", resp); 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() ) ) } 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#"