Skip to content

Commit 6a58ee3

Browse files
committed
client_id parsing
1 parent ba5bae3 commit 6a58ee3

File tree

2 files changed

+44
-17
lines changed

2 files changed

+44
-17
lines changed

src/download.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use reqwest::header::HeaderMap;
88

99

1010
// While creating files, certain characters are not allowed to be in the name, so we use this to delete them
11-
fn sanitize_song_name(input: &str) -> String {
11+
fn sanitize_song_name(input: &str, path: &PathBuf) -> String {
1212
let mut result = input
1313
.replace("\\u0026", "and"); // & -> and
1414
result = result.replace("\\u003c3", "ily"); // <3 -> ily
@@ -36,7 +36,7 @@ fn regex_get_first(regex: Regex, text: &str) -> Option<String> {
3636

3737
struct ThreadWatcher;
3838

39-
// If the function panics, remove one count from tthe thread count since the thread obviously isn't running
39+
// If the function panics, remove one count from the thread count since the thread obviously isn't running
4040
impl Drop for ThreadWatcher {
4141
fn drop(&mut self) {
4242
if thread::panicking() {
@@ -50,7 +50,7 @@ use std::thread;
5050
static GLOBAL_THREAD_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
5151

5252

53-
pub fn prepare_download(songs: Vec<String>, temp_dir: &mut PathBuf, download_dir: &mut PathBuf, threads: usize, is_track: bool) {
53+
pub fn prepare_download(songs: Vec<String>, temp_dir: &mut PathBuf, download_dir: &mut PathBuf, threads: usize, is_track: bool, client_id: String) {
5454
let max_threads = std::sync::atomic::AtomicUsize::new(0);
5555
max_threads.fetch_add(threads, Ordering::SeqCst);
5656
let req: Client = reqwest::blocking::ClientBuilder::new().use_rustls_tls().danger_accept_invalid_certs(true).build().unwrap();
@@ -64,6 +64,7 @@ pub fn prepare_download(songs: Vec<String>, temp_dir: &mut PathBuf, download_dir
6464
let song_wrapped = Arc::new(Mutex::new(song.clone()));
6565
let temp_dir_wrapped = Arc::new(Mutex::new(temp_dir.clone()));
6666
let download_dir_wrapped = Arc::new(Mutex::new(download_dir.clone()));
67+
let client_id_wrapped = Arc::new(Mutex::new(client_id.clone()));
6768
GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::Relaxed);
6869
logging(Severities::INFO,format!("Downloading {}",&song));
6970
thread::spawn(move || {
@@ -73,8 +74,9 @@ pub fn prepare_download(songs: Vec<String>, temp_dir: &mut PathBuf, download_dir
7374
let song_locked = song_wrapped.lock().unwrap();
7475
let mut temp_dir_locked = temp_dir_wrapped.lock().unwrap();
7576
let mut download_dir_locked = download_dir_wrapped.lock().unwrap();
77+
let client_id_locked = client_id_wrapped.lock().unwrap();
7678

77-
download(req_locked.clone(), song_locked.to_string(), &mut temp_dir_locked, &mut download_dir_locked, is_track);
79+
download(req_locked.clone(), song_locked.to_string(), &mut temp_dir_locked, &mut download_dir_locked, is_track, &client_id_locked);
7880
GLOBAL_THREAD_COUNT.fetch_sub(1, Ordering::Relaxed);
7981
});
8082
run = false;
@@ -102,7 +104,7 @@ fn count_mp3(root: PathBuf) -> u32 {
102104
}
103105

104106

105-
fn download(req: Client, song: String, temp_dir: &mut PathBuf, download_dir: &mut PathBuf, is_track: bool) {
107+
fn download(req: Client, song: String, temp_dir: &mut PathBuf, download_dir: &mut PathBuf, is_track: bool, client_id: &str) {
106108
let mut temp_dir = temp_dir.clone().to_owned();
107109
let mut download_dir = download_dir.clone().to_owned();
108110
let mut audio_file_nmbr_count: u32 = 0;
@@ -265,7 +267,24 @@ fn download(req: Client, song: String, temp_dir: &mut PathBuf, download_dir: &mu
265267
if let Some(hls) = capture.get(1) {
266268
let track_auth = track_auth.as_str();
267269
let hls = hls.as_str();
268-
let r = req.get(format!("{hls}?client_id=baLbCx2miy7TG4nunX9yTWklG3ecgeE9&track_authorization={track_auth}"))
270+
let mut headers = HeaderMap::new();
271+
headers.insert("Accept", "*/*".parse().unwrap());
272+
headers.insert("Accept-Language", "hu-HU,hu;q=0.9".parse().unwrap());
273+
headers.insert("Cache-Control", "no-cache".parse().unwrap());
274+
headers.insert("Connection", "keep-alive".parse().unwrap());
275+
headers.insert("Content-Type", "application/json".parse().unwrap());
276+
headers.insert("Origin", "https://soundcloud.com".parse().unwrap());
277+
headers.insert("Pragma", "no-cache".parse().unwrap());
278+
headers.insert("Referer", "https://soundcloud.com/".parse().unwrap());
279+
headers.insert("Sec-Fetch-Dest", "empty".parse().unwrap());
280+
headers.insert("Sec-Fetch-Mode", "cors".parse().unwrap());
281+
headers.insert("Sec-Fetch-Site", "same-site".parse().unwrap());
282+
headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36".parse().unwrap());
283+
headers.insert("sec-ch-ua", "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"".parse().unwrap());
284+
headers.insert("sec-ch-ua-mobile", "?0".parse().unwrap());
285+
headers.insert("sec-ch-ua-platform", "\"Windows\"".parse().unwrap());
286+
let r = req.get(format!("{hls}?client_id={client_id}&track_authorization={track_auth}"))
287+
.headers(headers.clone())
269288
.send().unwrap();
270289
if !r.status().is_success() {
271290
logging(Severities::ERROR, format!("Expected status code 200, got status code {} on song : {}",r.status(),&song));
@@ -276,7 +295,7 @@ fn download(req: Client, song: String, temp_dir: &mut PathBuf, download_dir: &mu
276295
logging(Severities::ERROR, format!("No download link found on song : {} | If this issue persists, please contact the developer",&song));
277296
return;
278297
}
279-
let r = req.get(&r[8..r.len()-2]).send().unwrap().text().unwrap();
298+
let r = req.get(&r[8..r.len()-2]).headers(headers).send().unwrap().text().unwrap();
280299
let re = Regex::new(r#"(https://cf-hls-media.sndcdn.com/media/.*?)\n"#).unwrap();
281300
let links = re.captures_iter(&r);
282301

@@ -301,7 +320,7 @@ fn download(req: Client, song: String, temp_dir: &mut PathBuf, download_dir: &mu
301320
}
302321
// mp3cat magic
303322
let mut arguments: Vec<String> = Vec::new();
304-
download_dir.push(format!("{}.mp3",sanitize_song_name(&song_name)));
323+
download_dir.push(format!("{}.mp3",sanitize_song_name(&song_name, &download_dir)));
305324

306325
let mut audio = 0;
307326
while audio < audio_file_nmbr_count {

src/main.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ use crate::logging::logging;
66
mod download;
77
mod logging;
88

9+
fn get_client_id() -> String {
10+
let req = reqwest::blocking::get("https://a-v2.sndcdn.com/assets/0-79b49120.js").unwrap().text().unwrap();
11+
let re = regex::Regex::new(r#"client_id:"(.*?)""#).unwrap();
12+
let r = re.captures(&req).unwrap().get(0).unwrap().as_str();
13+
r[11..r.len()-1].to_owned()
14+
}
15+
916
fn additional_argument_helper(args: &Vec<String>) -> (PathBuf,PathBuf) {
1017
let mut temp_dir: PathBuf = env::temp_dir();
1118
let mut download_dir: PathBuf = Path::new("./").to_path_buf();
@@ -90,7 +97,7 @@ fn trimming(track: String) -> String {
9097
splitted.get(0).unwrap().to_string()
9198
}
9299

93-
fn playlist_to_vec(req: reqwest::blocking::Client, dest: &mut Vec<String>, orig: Vec<String>) {
100+
fn playlist_to_vec(req: reqwest::blocking::Client, dest: &mut Vec<String>, orig: Vec<String>, client_id: &str) {
94101
// orig -> track ids
95102
use reqwest::header::HeaderMap;
96103
use regex::Regex;
@@ -119,7 +126,7 @@ fn playlist_to_vec(req: reqwest::blocking::Client, dest: &mut Vec<String>, orig:
119126
original.remove(0);
120127
temp = temp + 1;
121128
if temp == 10 {
122-
url.push_str("&client_id=0nr4Ys43jAqfn0VkGXfxTWh9d4NB0o54&[object Object]=&app_version=1694501791&app_locale=en");
129+
url.push_str(&format!("&client_id={client_id}&[object Object]=&app_version=1694501791&app_locale=en"));
123130
let r = req.get(url).headers(headers.clone()).send().unwrap().text().unwrap();
124131
for capture in reg.captures_iter(&r).map(|c| c.get(1)) {
125132
dest.push(capture.unwrap().as_str().to_string());
@@ -128,7 +135,7 @@ fn playlist_to_vec(req: reqwest::blocking::Client, dest: &mut Vec<String>, orig:
128135
temp = 0;
129136
}
130137
}
131-
url.push_str("&client_id=0nr4Ys43jAqfn0VkGXfxTWh9d4NB0o54&[object Object]=&app_version=1694501791&app_locale=en");
138+
url.push_str(&format!("&client_id={client_id}&[object Object]=&app_version=1694501791&app_locale=en"));
132139
let r = req.get(url).headers(headers.clone()).send().unwrap().text().unwrap();
133140
for capture in reg.captures_iter(&r).map(|c| c.get(1)) {
134141
dest.push(capture.unwrap().as_str().to_string());
@@ -141,6 +148,7 @@ fn main() {
141148
check_for_invalid_arguments(&args);
142149
let mut paths = additional_argument_helper(&args);
143150
paths.0.push("SCDownloader");
151+
let client_id: String = get_client_id();
144152
// We're safe the unwrap the args because we checked if the argument list of valid
145153
match args.get(1).unwrap().as_str() {
146154
"track" => {
@@ -155,7 +163,7 @@ fn main() {
155163
}
156164
let mut list: Vec<String> = Vec::new();
157165
list.push(trimming(arg2));
158-
prepare_download(list, &mut paths.0, &mut paths.1, 1, true);
166+
prepare_download(list, &mut paths.0, &mut paths.1, 1, true, client_id);
159167
},
160168
"playlist" | "album" => {
161169
let mut arg2 = args.get(2).unwrap().to_owned();
@@ -197,8 +205,8 @@ fn main() {
197205
}
198206
}
199207
let mut songs: Vec<String> = Vec::new();
200-
playlist_to_vec(req, &mut songs, list);
201-
prepare_download(songs, &mut paths.0, &mut paths.1, 3, false);
208+
playlist_to_vec(req, &mut songs, list, &client_id);
209+
prepare_download(songs, &mut paths.0, &mut paths.1, 3, false, client_id);
202210
},
203211
"artist" => {
204212
use regex::Regex;
@@ -230,10 +238,10 @@ fn main() {
230238
headers.insert("Sec-Fetch-User", "?1".parse().unwrap());
231239
headers.insert("Sec-GPC", "1".parse().unwrap());
232240
headers.insert("Connection", "keep-alive".parse().unwrap());
233-
let r = req.get(format!("https://soundcloud.com/{}",arg2)).headers(headers).send().unwrap().text().unwrap();
241+
let r = req.get(format!("https://soundcloud.com/{}",arg2)).headers(headers.clone()).send().unwrap().text().unwrap();
234242
let reg = Regex::new(r#"content="soundcloud://users:([0-9]*?)""#).unwrap();
235243
let uid = reg.captures(&r).unwrap().get(1).unwrap().as_str().to_owned();
236-
let r = req.get(format!("https://api-v2.soundcloud.com/users/{}/tracks?offset=0&limit=79999&representation=&client_id=TtbhBUaHqao06g1mUwVTxbjj8TSUkiCl&app_version=1694761046&app_locale=en",uid)).send().unwrap().text().unwrap();
244+
let r = req.get(format!("https://api-v2.soundcloud.com/users/{}/tracks?offset=0&limit=79999&representation=&client_id={client_id}&app_version=1694761046&app_locale=en",uid)).headers(headers).send().unwrap().text().unwrap();
237245
let reg = Regex::new(r#""permalink_url":"https://soundcloud\.com/((?:[a-zA-Z0-9-_]*?)/(?:[a-zA-Z0-9-_]*?))""#).unwrap();
238246
let mut list: Vec<String> = Vec::new();
239247
for a in reg.captures_iter(&r).map(|c| c.get(1)) {
@@ -245,7 +253,7 @@ fn main() {
245253
}
246254
}
247255
std::thread::sleep(std::time::Duration::from_secs(5));
248-
prepare_download(list, &mut paths.0, &mut paths.1, 3, false);
256+
prepare_download(list, &mut paths.0, &mut paths.1, 3, false, client_id);
249257
},
250258
_ => {
251259
exit(0);

0 commit comments

Comments
 (0)