117 lines
3.9 KiB
Rust
117 lines
3.9 KiB
Rust
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<dyn std::error::Error>> {
|
|
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<Option<String>, Box<dyn Error>> {
|
|
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<CloudPlaylists, Box<dyn Error>> {
|
|
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<Option<String>, Box<dyn Error>> {
|
|
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<Option<String>, Box<dyn Error>> {
|
|
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#"<script .+src="https://.+""#).unwrap().find_iter(&resp) {
|
|
let script_block = cap.as_str();
|
|
if let Some(m) = rg.find(script_block) {
|
|
urls.push(&m.as_str()[5..m.as_str().len()-1]);
|
|
}
|
|
}
|
|
|
|
let rg = Regex::new(r#""client_id=[a-zA-Z0-9]+""#).unwrap();
|
|
|
|
for i in 0..urls.len() {
|
|
let url = urls.get(i).unwrap().to_string();
|
|
let resp = reqwest::get(&url)
|
|
.await?
|
|
.text()
|
|
.await?;
|
|
|
|
if let Some(m) = rg.find(&resp) {
|
|
return Ok(Some(m.as_str()[11..m.as_str().len()-1].to_string()));
|
|
}
|
|
}
|
|
Ok(None)
|
|
} |