sc.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. <?php
  2. class sc{
  3. public function __construct(){
  4. include "lib/nextpage.php";
  5. $this->nextpage = new nextpage("sc");
  6. $this->client_id = "ArYppSEotE3YiXCO4Nsgid2LLqJutiww";
  7. $this->user_id = "766585-580597-163310-929698";
  8. }
  9. public function getfilters($page){
  10. return [
  11. "type" => [
  12. "display" => "Type",
  13. "option" => [
  14. "any" => "Any type",
  15. "track" => "Tracks",
  16. "people" => "People",
  17. "album" => "Albums",
  18. "playlist" => "Playlists",
  19. "goplus" => "Go+ Tracks"
  20. ]
  21. ]
  22. ];
  23. }
  24. private function get($url, $get = []){
  25. $curlproc = curl_init();
  26. if($get !== []){
  27. $get = http_build_query($get);
  28. $url .= "?" . $get;
  29. }
  30. curl_setopt($curlproc, CURLOPT_URL, $url);
  31. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  32. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  33. ["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0",
  34. "Accept: application/json, text/javascript, */*; q=0.01",
  35. "Accept-Language: en-US,en;q=0.5",
  36. "Accept-Encoding: gzip",
  37. "Referer: https://soundcloud.com/",
  38. "Origin: https://soundcloud.com",
  39. "DNT: 1",
  40. "Connection: keep-alive",
  41. "Sec-Fetch-Dest: empty",
  42. "Sec-Fetch-Mode: cors",
  43. "Sec-Fetch-Site: same-site"]
  44. );
  45. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  46. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  47. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  48. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  49. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  50. $data = curl_exec($curlproc);
  51. if(curl_errno($curlproc)){
  52. throw new Exception(curl_error($curlproc));
  53. }
  54. curl_close($curlproc);
  55. return $data;
  56. }
  57. public function music($get){
  58. if($get["npt"]){
  59. $params = $this->nextpage->get($get["npt"], "music");
  60. $params = json_decode($params, true);
  61. $url = $params["url"];
  62. unset($params["url"]);
  63. }else{
  64. // normal search:
  65. // https://api-v2.soundcloud.com/search?q=freddie%20dredd&variant_ids=&facet=model&user_id=351062-302234-707916-795081&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=20&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en
  66. // soundcloud go+ search:
  67. // https://api-v2.soundcloud.com/search/tracks?q=freddie%20dredd&variant_ids=&filter.content_tier=SUB_HIGH_TIER&facet=genre&user_id=630591-269800-703400-765403&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=20&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en
  68. // tracks search:
  69. // https://api-v2.soundcloud.com/search/tracks?q=freddie%20dredd&variant_ids=&facet=genre&user_id=630591-269800-703400-765403&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=20&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en
  70. // users search:
  71. // https://api-v2.soundcloud.com/search/users?q=freddie%20dredd&variant_ids=&facet=place&user_id=630591-269800-703400-765403&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=20&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en
  72. // albums search:
  73. // https://api-v2.soundcloud.com/search/albums?q=freddie%20dredd&variant_ids=&facet=genre&user_id=630591-269800-703400-765403&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=20&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en
  74. // playlists search:
  75. // https://api-v2.soundcloud.com/search/playlists_without_albums?q=freddie%20dredd&variant_ids=&facet=genre&user_id=630591-269800-703400-765403&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=20&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en
  76. $search = $get["s"];
  77. $type = $get["type"];
  78. switch($type){
  79. case "any":
  80. $url = "https://api-v2.soundcloud.com/search";
  81. $params = [
  82. "q" => $search,
  83. "variant_ids" => "",
  84. "facet" => "model",
  85. "user_id" => $this->user_id,
  86. "client_id" => $this->client_id,
  87. "limit" => 20,
  88. "offset" => 0,
  89. "linked_partitioning" => 1,
  90. "app_version" => 1696577813,
  91. "app_locale" => "en"
  92. ];
  93. break;
  94. case "track":
  95. $url = "https://api-v2.soundcloud.com/search/tracks";
  96. $params = [
  97. "q" => $search,
  98. "variant_ids" => "",
  99. "facet_genre" => "",
  100. "user_id" => $this->user_id,
  101. "client_id" => $this->client_id,
  102. "limit" => 20,
  103. "offset" => 0,
  104. "linked_partitioning" => 1,
  105. "app_version" => 1696577813,
  106. "app_locale" => "en"
  107. ];
  108. break;
  109. case "people":
  110. $url = "https://api-v2.soundcloud.com/search/users";
  111. $params = [
  112. "q" => $search,
  113. "variant_ids" => "",
  114. "facet" => "place",
  115. "user_id" => $this->user_id,
  116. "client_id" => $this->client_id,
  117. "limit" => 20,
  118. "offset" => 0,
  119. "linked_partitioning" => 1,
  120. "app_version" => 1696577813,
  121. "app_locale" => "en"
  122. ];
  123. break;
  124. case "album":
  125. $url = "https://api-v2.soundcloud.com/search/albums";
  126. $params = [
  127. "q" => $search,
  128. "variant_ids" => "",
  129. "facet" => "genre",
  130. "user_id" => $this->user_id,
  131. "client_id" => $this->client_id,
  132. "limit" => 20,
  133. "offset" => 0,
  134. "linked_partitioning" => 1,
  135. "app_version" => 1696577813,
  136. "app_locale" => "en"
  137. ];
  138. break;
  139. case "playlist":
  140. $url = "https://api-v2.soundcloud.com/search/playlists_without_albums";
  141. $params = [
  142. "q" => $search,
  143. "variant_ids" => "",
  144. "facet" => "genre",
  145. "user_id" => $this->user_id,
  146. "client_id" => $this->client_id,
  147. "limit" => 20,
  148. "offset" => 0,
  149. "linked_partitioning" => 1,
  150. "app_version" => 1696577813,
  151. "app_locale" => "en"
  152. ];
  153. break;
  154. case "goplus":
  155. $url = "https://api-v2.soundcloud.com/search/tracks";
  156. $params = [
  157. "q" => $search,
  158. "variant_ids" => "",
  159. "filter.content_tier" => "SUB_HIGH_TIER",
  160. "facet" => "genre",
  161. "user_id" => $this->user_id,
  162. "client_id" => $this->client_id,
  163. "limit" => 20,
  164. "offset" => 0,
  165. "linked_partitioning" => 1,
  166. "app_version" => 1696577813,
  167. "app_locale" => "en"
  168. ];
  169. break;
  170. }
  171. }
  172. try{
  173. $json = $this->get($url, $params);
  174. }catch(Exception $error){
  175. throw new Exception("Failed to fetch JSON");
  176. }
  177. /*
  178. $handle = fopen("scraper/soundcloud.json", "r");
  179. $json = fread($handle, filesize("scraper/soundcloud.json"));
  180. fclose($handle);
  181. */
  182. $json = json_decode($json, true);
  183. if($json === null){
  184. throw new Exception("Failed to decode JSON");
  185. }
  186. $out = [
  187. "status" => "ok",
  188. "npt" => null,
  189. "song" => [],
  190. "playlist" => [],
  191. "author" => []
  192. ];
  193. /*
  194. Get next page
  195. */
  196. if(isset($json["next_href"])){
  197. $params["query_urn"] = $json["query_urn"];
  198. $params["offset"] = $params["offset"] + 20;
  199. $params["url"] = $url; // we will remove this later
  200. $out["npt"] =
  201. $this->nextpage->store(
  202. json_encode($params),
  203. "music"
  204. );
  205. }
  206. /*
  207. Scrape items
  208. */
  209. foreach($json["collection"] as $item){
  210. switch($item["kind"]){
  211. case "user":
  212. // parse author
  213. $out["author"][] = [
  214. "title" => $item["username"],
  215. "followers" => $item["followers_count"],
  216. "description" => trim($item["track_count"] . " songs. " . $this->limitstrlen($item["description"])),
  217. "thumb" => [
  218. "url" => $item["avatar_url"],
  219. "ratio" => "1:1"
  220. ],
  221. "url" => $item["permalink_url"]
  222. ];
  223. break;
  224. case "playlist":
  225. // parse playlist
  226. $description = [];
  227. $count = 0;
  228. foreach($item["tracks"] as $song){
  229. $count++;
  230. if(!isset($song["title"])){
  231. continue;
  232. }
  233. $description[] = $song["title"];
  234. }
  235. if(count($description) != 0){
  236. $description = trim($count . " songs. " . implode(", ", $description));
  237. }
  238. if(
  239. isset($item["artwork_url"]) &&
  240. !empty($item["artwork_url"])
  241. ){
  242. $thumb = [
  243. "ratio" => "1:1",
  244. "url" => $item["artwork_url"]
  245. ];
  246. }elseif(
  247. isset($item["tracks"][0]["artwork_url"]) &&
  248. !empty($item["tracks"][0]["artwork_url"])
  249. ){
  250. $thumb = [
  251. "ratio" => "1:1",
  252. "url" => $item["tracks"][0]["artwork_url"]
  253. ];
  254. }else{
  255. $thumb = [
  256. "ratio" => null,
  257. "url" => null
  258. ];
  259. }
  260. $out["playlist"][] = [
  261. "title" => $item["title"],
  262. "description" => $this->limitstrlen($description),
  263. "author" => [
  264. "name" => $item["user"]["username"],
  265. "url" => $item["user"]["permalink_url"],
  266. "avatar" => $item["user"]["avatar_url"]
  267. ],
  268. "thumb" => $thumb,
  269. "date" => strtotime($item["created_at"]),
  270. "duration" => $item["duration"] / 1000,
  271. "url" => $item["permalink_url"]
  272. ];
  273. break;
  274. case "track":
  275. if(stripos($item["monetization_model"], "TIER") === false){
  276. $stream = [
  277. "endpoint" => "audio_sc",
  278. "url" =>
  279. $item["media"]["transcodings"][0]["url"] .
  280. "?client_id=" . $this->client_id .
  281. "&track_authorization=" .
  282. $item["track_authorization"]
  283. ];
  284. }else{
  285. $stream = [
  286. "endpoint" => null,
  287. "url" => null
  288. ];
  289. }
  290. // parse track
  291. $out["song"][] = [
  292. "title" => $item["title"],
  293. "description" => $item["description"] == "" ? null : $this->limitstrlen($item["description"]),
  294. "url" => $item["permalink_url"],
  295. "views" => $item["playback_count"],
  296. "author" => [
  297. "name" => $item["user"]["username"],
  298. "url" => $item["user"]["permalink_url"],
  299. "avatar" => $item["user"]["avatar_url"]
  300. ],
  301. "thumb" => [
  302. "ratio" => "1:1",
  303. "url" => $item["artwork_url"]
  304. ],
  305. "date" => strtotime($item["created_at"]),
  306. "duration" => (int)$item["full_duration"] / 1000,
  307. "stream" => $stream
  308. ];
  309. break;
  310. }
  311. }
  312. return $out;
  313. }
  314. private function limitstrlen($text){
  315. return
  316. explode(
  317. "\n",
  318. wordwrap(
  319. str_replace(
  320. ["\n\r", "\r\n", "\n", "\r"],
  321. " ",
  322. $text
  323. ),
  324. 300,
  325. "\n"
  326. ),
  327. 2
  328. )[0];
  329. }
  330. }