DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
SoundCloudAPI.cpp
Go to the documentation of this file.
1/*
2 MIT License
3
4 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
5
6 Copyright 2022, 2023 Chris M. (RealTimeChris)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26/// SoundCloudAPI.cpp - Source file for the SoundCloud api related stuff.
27/// Aug 25, 2021
28/// https://discordcoreapi.com
29/// \file SoundCloudAPI.cpp
30
36
37namespace jsonifier {
38
39 template<> struct core<discord_core_api::discord_core_internal::data_class> {
40 using value_type = discord_core_api::discord_core_internal::data_class;
41 static constexpr auto parseValue =
42 createValue("artwork_url", &value_type::artworkUrl, "description", &value_type::description, "duration", &value_type::duration, "media", &value_type::mediaVal, "title",
43 &value_type::title, "track_authorization", &value_type::trackAuthorization, "avatar_url", &value_type::avatarUrl, "permalink_url", &value_type::viewUrl);
44 };
45
46 template<> struct core<discord_core_api::discord_core_internal::welcome_element> {
47 using value_type = discord_core_api::discord_core_internal::welcome_element;
48 static constexpr auto parseValue = createValue("data", &value_type::data, "hydratable", &value_type::hydratable, "tracks", &value_type::data);
49 };
50
51 template<> struct core<discord_core_api::discord_core_internal::welcome> {
52 using value_type = discord_core_api::discord_core_internal::welcome;
53 static constexpr auto parseValue = createValue(&value_type::data);
54 };
55
56 template<> struct core<discord_core_api::discord_core_internal::media> {
57 using value_type = discord_core_api::discord_core_internal::media;
58 static constexpr auto parseValue = createValue("transcodings", &value_type::transcodings);
59 };
60
61 template<> struct core<discord_core_api::discord_core_internal::second_download_url> {
62 using value_type = discord_core_api::discord_core_internal::second_download_url;
63 static constexpr auto parseValue = createValue("url", &value_type::url);
64 };
65
66 template<> struct core<discord_core_api::discord_core_internal::transcoding> {
67 using value_type = discord_core_api::discord_core_internal::transcoding;
68 static constexpr auto parseValue = createValue("preset", &value_type::preset, "url", &value_type::url);
69 };
70
71 template<> struct core<discord_core_api::discord_core_internal::raw_sound_cloud_song> {
72 using value_type = discord_core_api::discord_core_internal::raw_sound_cloud_song;
73 static constexpr auto parseValue = createValue("artwork_url", &value_type::artworkUrl, "description", &value_type::description, "duration", &value_type::duration, "media",
74 &value_type::mediaVal, "title", &value_type::title, "track_authorization", &value_type::trackAuthorization, "permalink_url", &value_type::viewUrl);
75 };
76
77 template<> struct core<discord_core_api::discord_core_internal::sound_cloud_search_results> {
78 using value_type = discord_core_api::discord_core_internal::sound_cloud_search_results;
79 static constexpr auto parseValue = createValue("collection", &value_type::collection);
80 };
81}
82
83namespace discord_core_api {
84
85 namespace discord_core_internal {
86
87 sound_cloud_request_builder::sound_cloud_request_builder(config_manager* configManagerNew) : https_client_core{ jsonifier::string{ configManagerNew->getBotToken() } } {};
88
89 inline search_type collectSearchType(jsonifier::string_view string, jsonifier::string& stringNew) {
90 if (string.find("soundcloud.com") != jsonifier::string::npos && string.find("/sets/") == jsonifier::string::npos) {
91 stringNew = string.substr(string.find("soundcloud.com/") + jsonifier::string{ "soundcloud.com/" }.size());
92 return search_type::single_song_with_id;
93 } else if (string.find("/sets/") != jsonifier::string::npos) {
94 stringNew = string.substr(string.find("soundcloud.com/") + jsonifier::string{ "soundcloud.com/" }.size());
95 return search_type::playlist;
96 } else {
97 stringNew = string;
98 return search_type::single_song_without_id;
99 }
100 }
101
102 jsonifier::vector<song> sound_cloud_request_builder::collectPlaylist(jsonifier::string_view songQuery) {
103 if (clientId == "") {
104 sound_cloud_request_builder::collectClientId();
105 }
106 try {
107 https_workload_data dataPackage{ https_workload_type::SoundCloud_Get_Search_Results };
108 dataPackage.baseUrl = "https://soundcloud.com/";
109 dataPackage.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
110 dataPackage.relativePath = songQuery;
111 dataPackage.workloadClass = https_workload_class::Get;
112 https_response_data returnData = submitWorkloadAndGetResult(std::move(dataPackage));
113 jsonifier::vector<song> resultsFinal{};
114 auto findValue = returnData.responseData.find("window.__sc_hydration = ");
115 if (findValue != jsonifier::string::npos) {
116 returnData.responseData = returnData.responseData.substr(findValue + jsonifier::string{ "window.__sc_hydration = " }.size(),
117 returnData.responseData.find("</script>") -
118 (returnData.responseData.find("window.__sc_hydration = ") + jsonifier::string{ "window.__sc_hydration = " }.size()));
119 }
120 welcome resultsNew{};
121 parser.parseJson(resultsNew, returnData.responseData);
122 jsonifier::string avatarUrl{};
123 jsonifier::string collectionString{ "tracks?ids=" };
124 for (auto& value: resultsNew.data) {
125 if (value.data.getType() == jsonifier::json_type::Object) {
126 auto newObject = value.data.operator jsonifier::raw_json_data::object_type();
127 avatarUrl = newObject["avatar_url"].operator jsonifier::string();
128 if (value.hydratable == "playlist") {
129 auto newerObject = value.data.operator jsonifier::raw_json_data::object_type();
130 for (auto& [key, valueNew]: newerObject) {
131 if (key == "tracks") {
132 auto newArray = valueNew.operator jsonifier::raw_json_data::array_type();
133 uint32_t currentIndex{};
134 auto arraySize = newArray.size();
135 for (auto& newValue: newArray) {
136 newObject = newValue.operator jsonifier::raw_json_data::object_type();
137 collectionString += jsonifier::toString(newValue.operator jsonifier::raw_json_data::object_type()["id"].operator uint64_t());
138 if (currentIndex < arraySize - 1) {
139 collectionString += "%2C";
140 }
141 ++currentIndex;
142 }
143 collectionString += "&client_id=" + clientId + "&%5Bobject%20Object%5D=&app_version=" + appVersion + "&app_locale=en";
144 }
145 }
146 }
147 }
148 }
149 https_workload_data dataPackage02{ https_workload_type::SoundCloud_Get_Search_Results };
150 dataPackage02.baseUrl = "https://api-v2.soundcloud.com/";
151 dataPackage02.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
152 dataPackage02.relativePath = collectionString;
153 dataPackage02.workloadClass = https_workload_class::Get;
154 returnData = submitWorkloadAndGetResult(std::move(dataPackage02));
155 jsonifier::vector<jsonifier::raw_json_data> resultsNewer{};
156 parser.parseJson(resultsNewer, returnData.responseData);
157 for (auto& value: resultsNewer) {
158 song results{};
159 if (value.getType() == jsonifier::json_type::Object) {
160 auto newObject = value.operator jsonifier::raw_json_data::object_type();
161 avatarUrl = newObject["avatar_url"].operator jsonifier::string();
162 if (newObject["title"].operator jsonifier::string() == "") {
163 continue;
164 }
165 bool isItFound{};
166 for (auto& valueNew: newObject["media"].operator jsonifier::raw_json_data::object_type()["transcodings"].operator jsonifier::raw_json_data::array_type()) {
167 if (valueNew.operator jsonifier::raw_json_data::object_type()["preset"].operator jsonifier::string() == "opus_0_0") {
168 isItFound = true;
169 results.firstDownloadUrl = valueNew.operator jsonifier::raw_json_data::object_type()["url"].operator jsonifier::string();
170 results.songId = valueNew.operator jsonifier::raw_json_data::object_type()["url"].operator jsonifier::string();
171 }
172 }
173 if (isItFound) {
174 jsonifier::string newString = newObject["title"].operator jsonifier::string();
175 if (newString.size() > 0) {
176 if (newString.size() > 256) {
177 newString = newString.substr(0, 256);
178 }
179 results.songTitle = utf8MakeValid(newString);
180 }
181 newString = newObject["description"].operator jsonifier::string();
182 if (newString.size() > 0) {
183 if (newString.size() > 256) {
184 newString = newString.substr(0, 256);
185 }
186 results.description = utf8MakeValid(newString);
187 results.description += "...";
188 }
189 newString = newObject["artwork_url"].operator jsonifier::string();
190 if (newString.size() > 0) {
191 results.thumbnailUrl = utf8MakeValid(newString);
192 }
193 results.viewUrl = newObject["permalink_url"].operator jsonifier::string();
194 results.duration = time_stamp::convertMsToDurationString(static_cast<uint64_t>(newObject["duration"].operator uint64_t()));
195 results.firstDownloadUrl +=
196 "?client_id=" + sound_cloud_request_builder::clientId + "&track_authorization=" + newObject["track_authorization"].operator jsonifier::string();
197 if (newObject["artwork_url"].operator jsonifier::string().find("-") != jsonifier::string::npos) {
198 jsonifier::string newerString =
199 newObject["artwork_url"].operator jsonifier::string().substr(0, newObject["artwork_url"].operator jsonifier::string().findLastOf("-") + 1);
200 newerString += "t500x500.jpg";
201 results.thumbnailUrl = newerString;
202 } else if (avatarUrl.find("-") != jsonifier::string::npos) {
203 jsonifier::string newerString = avatarUrl.substr(0, avatarUrl.findLastOf("-") + 1);
204 newerString += "t500x500.jpg";
205 results.thumbnailUrl = newerString;
206 }
207 results.type = song_type::SoundCloud;
208 resultsFinal.emplace_back(results);
209 }
210 }
211 }
212 return resultsFinal;
213 } catch (const https_error& error) {
214 message_printer::printError<print_message_type::https>("sound_cloud_request_builder::collectSearchResults() Error: " + jsonifier::string{ error.what() });
215 }
216 return {};
217 }
218
219 song sound_cloud_request_builder::collectSingleResult(jsonifier::string_view songQuery) {
220 if (clientId == "") {
221 sound_cloud_request_builder::collectClientId();
222 }
223 try {
224 https_workload_data dataPackage{ https_workload_type::SoundCloud_Get_Search_Results };
225 dataPackage.baseUrl = "https://soundcloud.com/";
226 dataPackage.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
227 dataPackage.relativePath = songQuery;
228 dataPackage.workloadClass = https_workload_class::Get;
229 https_response_data returnData = submitWorkloadAndGetResult(std::move(dataPackage));
230 song results{};
231 auto findValue = returnData.responseData.find("window.__sc_hydration = ");
232 if (findValue != jsonifier::string::npos) {
233 returnData.responseData = returnData.responseData.substr(findValue + jsonifier::string{ "window.__sc_hydration = " }.size(),
234 returnData.responseData.find("</script>") -
235 (returnData.responseData.find("window.__sc_hydration = ") + jsonifier::string{ "window.__sc_hydration = " }.size()));
236 }
237 welcome resultsNew{};
238 parser.parseJson(resultsNew, returnData.responseData);
239 jsonifier::string avatarUrl{};
240
241 for (auto& value: resultsNew.data) {
242 if (value.data.getType() == jsonifier::json_type::Object) {
243 auto newObject = value.data.operator jsonifier::raw_json_data::object_type();
244 avatarUrl = newObject["avatar_url"].operator jsonifier::string();
245 if (value.hydratable == "sound") {
246 if (newObject["title"].operator jsonifier::string() == "") {
247 continue;
248 }
249 bool isItFound{};
250 for (auto& valueNew:
251 newObject["media"].operator jsonifier::raw_json_data::object_type()["transcodings"].operator jsonifier::raw_json_data::array_type()) {
252 if (valueNew.operator jsonifier::raw_json_data::object_type()["preset"].operator jsonifier::string() == "opus_0_0") {
253 isItFound = true;
254 results.firstDownloadUrl = valueNew.operator jsonifier::raw_json_data::object_type()["url"].operator jsonifier::string();
255 results.songId = valueNew.operator jsonifier::raw_json_data::object_type()["url"].operator jsonifier::string();
256 }
257 }
258 if (isItFound) {
259 jsonifier::string newString = newObject["title"].operator jsonifier::string();
260 if (newString.size() > 0) {
261 if (newString.size() > 256) {
262 newString = newString.substr(0, 256);
263 }
264 results.songTitle = utf8MakeValid(newString);
265 }
266 newString = newObject["description"].operator jsonifier::string();
267 if (newString.size() > 0) {
268 if (newString.size() > 256) {
269 newString = newString.substr(0, 256);
270 }
271 results.description = utf8MakeValid(newString);
272 results.description += "...";
273 }
274 newString = newObject["artwork_url"].operator jsonifier::string();
275 if (newString.size() > 0) {
276 results.thumbnailUrl = utf8MakeValid(newString);
277 }
278 results.viewUrl = newObject["permalink_url"].operator jsonifier::string();
279 results.duration = time_stamp::convertMsToDurationString(static_cast<uint64_t>(newObject["duration"].operator uint64_t()));
280 results.firstDownloadUrl +=
281 "?client_id=" + sound_cloud_request_builder::clientId + "&track_authorization=" + newObject["track_authorization"].operator jsonifier::string();
282 if (newObject["artwork_url"].operator jsonifier::string().find("-") != jsonifier::string::npos) {
283 jsonifier::string newerString =
284 newObject["artwork_url"].operator jsonifier::string().substr(0, newObject["artwork_url"].operator jsonifier::string().findLastOf("-") + 1);
285 newerString += "t500x500.jpg";
286 results.thumbnailUrl = newerString;
287 } else if (avatarUrl.find("-") != jsonifier::string::npos) {
288 jsonifier::string newerString = avatarUrl.substr(0, avatarUrl.findLastOf("-") + 1);
289 newerString += "t500x500.jpg";
290 results.thumbnailUrl = newerString;
291 }
292 }
293 }
294 }
295 }
296 results.type = song_type::SoundCloud;
297 return results;
298 } catch (const https_error& error) {
299 message_printer::printError<print_message_type::https>("sound_cloud_request_builder::collectSearchResults() Error: " + jsonifier::string{ error.what() });
300 }
301 return {};
302 }
303
304 jsonifier::vector<song> sound_cloud_request_builder::collectSearchResults(jsonifier::string_view songQuery, uint64_t limit) {
305 if (clientId == "") {
306 sound_cloud_request_builder::collectClientId();
307 }
308 try {
309 jsonifier::string newString{};
310 jsonifier::vector<song> results{};
311 auto searchType = collectSearchType(songQuery, newString);
312 if (searchType == search_type::single_song_with_id) {
313 auto result = collectSingleResult(newString);
314 if (result.songId != "") {
315 results.emplace_back(result);
316 }
317 return results;
318 } else if (searchType == search_type::playlist) {
319 return collectPlaylist(newString);
320 }
321 https_workload_data dataPackage{ https_workload_type::SoundCloud_Get_Search_Results };
322 dataPackage.baseUrl = baseUrl02;
323 dataPackage.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
324 dataPackage.relativePath = "/search?q=" + urlEncode(songQuery) + "&facet=model&client_id=" + sound_cloud_request_builder::clientId;
325 dataPackage.workloadClass = https_workload_class::Get;
326 https_response_data returnData = submitWorkloadAndGetResult(std::move(dataPackage));
327 sound_cloud_search_results resultsNew{};
328 parser.parseJson(resultsNew, returnData.responseData);
329 for (auto& value: resultsNew.collection) {
330 song songNew{};
331 if (value.title == "") {
332 continue;
333 }
334 bool isItFound{};
335 for (auto& valueNew: value.mediaVal.transcodings) {
336 if (valueNew.preset == "opus_0_0") {
337 isItFound = true;
338 songNew.firstDownloadUrl = valueNew.url;
339 songNew.songId = valueNew.url;
340 }
341 }
342 if (isItFound) {
343 newString = value.title;
344 if (newString.size() > 0) {
345 if (newString.size() > 256) {
346 newString = newString.substr(0, 256);
347 }
348 songNew.songTitle = utf8MakeValid(newString);
349 }
350 newString = value.description;
351 if (newString.size() > 0) {
352 if (newString.size() > 256) {
353 newString = newString.substr(0, 256);
354 }
355 songNew.description = utf8MakeValid(newString);
356 songNew.description += "...";
357 }
358 newString = value.artworkUrl;
359 if (newString.size() > 0) {
360 songNew.thumbnailUrl = newString;
361 }
362 songNew.type = song_type::SoundCloud;
363 songNew.viewUrl = value.viewUrl;
364 songNew.duration = time_stamp::convertMsToDurationString(value.duration);
365 songNew.firstDownloadUrl += "?client_id=" + sound_cloud_request_builder::clientId + "&track_authorization=" + value.trackAuthorization;
366 if (songNew.thumbnailUrl.find("-") != jsonifier::string::npos) {
367 jsonifier::string newerString = songNew.thumbnailUrl.substr(0, songNew.thumbnailUrl.findLastOf("-") + 1);
368 newerString += "t500x500.jpg";
369 songNew.thumbnailUrl = newerString;
370 }
371 results.emplace_back(songNew);
372 if (results.size() >= limit) {
373 break;
374 }
375 }
376 }
377 return results;
378 } catch (const https_error& error) {
379 message_printer::printError<print_message_type::https>("sound_cloud_request_builder::collectSearchResults() Error: " + jsonifier::string{ error.what() });
380 }
381 return {};
382 }
383
384 song sound_cloud_request_builder::constructDownloadInfo(const song& songNew, uint64_t recursionDepth) {
385 try {
386 https_workload_data dataPackage01{ https_workload_type::SoundCloud_Get_Download_Links };
387 if (songNew.firstDownloadUrl.find(".com") != jsonifier::string::npos) {
388 dataPackage01.baseUrl = songNew.firstDownloadUrl.substr(0, songNew.firstDownloadUrl.find(".com") + 4);
389 dataPackage01.relativePath = songNew.firstDownloadUrl.substr(songNew.firstDownloadUrl.find(".com") + 4);
390 } else {
391 return {};
392 }
393 dataPackage01.workloadClass = https_workload_class::Get;
394 dataPackage01.headersToInsert["Connection"] = "Keep-Alive";
395 dataPackage01.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36";
396 https_response_data results = submitWorkloadAndGetResult(std::move(dataPackage01));
397 second_download_url downloadUrl{};
398 song newerSong{ songNew };
399 parser.parseJson(downloadUrl, results.responseData);
400 newerSong.secondDownloadUrl = downloadUrl.url;
401 if (newerSong.secondDownloadUrl.find("/playlist") != jsonifier::string::npos) {
402 https_workload_data dataPackage{ https_workload_type::SoundCloud_Get_Download_Links };
403 dataPackage.baseUrl = newerSong.secondDownloadUrl;
404 dataPackage.workloadClass = https_workload_class::Get;
405 https_response_data resultsNew = submitWorkloadAndGetResult(std::move(dataPackage));
406 jsonifier::string newString{ resultsNew.responseData };
407 newerSong.finalDownloadUrls.clear();
408 while (newString.find("#EXTINF:") != jsonifier::string::npos) {
409 jsonifier::string newString01 = "#EXTINF:";
410 jsonifier::string newString02 = newString.substr(newString.find("#EXTINF:") + newString01.size());
411 auto commandFind = newString02.find(",");
412 jsonifier::string newString00 = newString02.substr(0, commandFind);
413 jsonifier::string newString03 = newString02.substr(commandFind + 2, newString02.find("#EXTINF:") - (newString00.size() + 3));
414 newString = newString02.substr(commandFind);
415 if (newString03.find("#EXT-X-ENDLIST") != jsonifier::string::npos) {
416 newString03 = newString03.substr(0, newString03.find("#EXT-X-ENDLIST"));
417 }
418 jsonifier::string newString04 = newString03.substr(newString03.findFirstOf("1234567890"));
419 uint64_t firstNumber01 = jsonifier::strToUint64(newString04.substr(0, newString04.find("/")).data());
420 jsonifier::string newString05 = newString04.substr(newString04.find("/") + 1);
421 uint64_t secondNumber = jsonifier::strToUint64(newString05.substr(0, newString05.find("/")).data());
422 download_url downloadUrlNew{};
423 downloadUrlNew.urlPath = newString03;
424 downloadUrlNew.contentSize = secondNumber - firstNumber01;
425 newerSong.finalDownloadUrls.emplace_back(downloadUrlNew);
426 }
427 for (uint64_t x = 0; x < newerSong.finalDownloadUrls.size(); ++x) {
428 if (x == newerSong.finalDownloadUrls.size() - 1) {
429 newerSong.finalDownloadUrls.at(x).urlPath = newerSong.finalDownloadUrls.at(x).urlPath.substr(0, newerSong.finalDownloadUrls.at(x).urlPath.size() - 1);
430 }
431 }
432 for (auto& value: newerSong.finalDownloadUrls) {
433 newerSong.contentLength += value.contentSize;
434 }
435 } else {
436 https_workload_data dataPackage02{ https_workload_type::SoundCloud_Get_Search_Results };
437 dataPackage02.baseUrl = newerSong.secondDownloadUrl;
438 dataPackage02.workloadClass = https_workload_class::Get;
439 dataPackage02.headersToInsert["Connection"] = "Keep-Alive";
440 dataPackage02.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36";
441 auto headersNew = submitWorkloadAndGetResult(std::move(dataPackage02));
442 uint64_t valueBitRate{};
443 uint64_t valueLength{};
444 if (headersNew.responseHeaders.find("x-amz-meta-bitrate") != headersNew.responseHeaders.end()) {
445 valueBitRate = jsonifier::strToUint64(headersNew.responseHeaders.find("x-amz-meta-bitrate")->second.data());
446 }
447 if (headersNew.responseHeaders.find("x-amz-meta-duration") != headersNew.responseHeaders.end()) {
448 valueLength = jsonifier::strToUint64(headersNew.responseHeaders.find("x-amz-meta-duration")->second.data());
449 }
450 download_url downloadUrlNew{};
451 downloadUrlNew.contentSize = static_cast<uint64_t>(((valueBitRate * valueLength) / 8) - 193);
452 downloadUrlNew.urlPath = newerSong.secondDownloadUrl;
453 newerSong.finalDownloadUrls.emplace_back(downloadUrlNew);
454 }
455 return newerSong;
456 } catch (const https_error& error) {
457 if (recursionDepth <= 10) {
458 ++recursionDepth;
459 return constructDownloadInfo(songNew, recursionDepth);
460 } else {
461 message_printer::printError<print_message_type::https>("YouTubeRequestBuilder::constructDownloadInfo() Error: " + jsonifier::string{ error.what() });
462 return {};
463 }
464 }
465 }
466
467 song sound_cloud_request_builder::collectFinalSong(const song& songNew) {
468 return constructDownloadInfo(songNew, 0);
469 }
470
471 jsonifier::string sound_cloud_request_builder::collectClientId() {
472 jsonifier::string clientIdNew{};
473 try {
474 https_workload_data dataPackage02{ https_workload_type::SoundCloud_Get_Client_Id };
475 dataPackage02.baseUrl = baseUrl;
476 dataPackage02.relativePath = "/search?q=testValue";
477 dataPackage02.headersToInsert["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36";
478 dataPackage02.workloadClass = https_workload_class::Get;
479 https_response_data returnData = submitWorkloadAndGetResult(std::move(dataPackage02));
480 jsonifier::vector<jsonifier::string> assetPaths{};
481 jsonifier::string newString01 = "crossorigin src=";
482 jsonifier::string newerString{};
483 newerString = returnData.responseData;
484 if (newerString.find(newString01) != jsonifier::string::npos) {
485 jsonifier::string newString = newerString.substr(newerString.find(newString01) + newString01.size());
486 jsonifier::string newString02 = newString.substr(1, newString.find(".js") + 2);
487 assetPaths.emplace_back(newString02);
488 while (newString.find("crossorigin src=") != jsonifier::string::npos) {
489 jsonifier::string newString03 = newString.substr(1, newString.find(".js") + 2);
490 newString = newString.substr(newString.find("crossorigin src=") + newString01.size());
491 assetPaths.emplace_back(newString03);
492 }
493 https_workload_data dataPackage03{ https_workload_type::SoundCloud_Get_Client_Id };
494 dataPackage03.baseUrl = assetPaths[5];
495 dataPackage03.workloadClass = https_workload_class::Get;
496 https_response_data returnData02 = submitWorkloadAndGetResult(std::move(dataPackage03));
497 jsonifier::string newerString02{};
498 newerString02.insert(newerString02.begin(), returnData02.responseData.begin(), returnData02.responseData.end());
499
500 jsonifier::string newString03 =
501 newerString02.substr(newerString02.find("JSON.stringify({client_id:\"") + jsonifier::string_view{ "JSON.stringify({client_id:\"" }.size());
502
503 if (newString03.find("\",nonce:e.nonce}))))") != jsonifier::string::npos) {
504 clientIdNew = newString03.substr(0, newString03.find("\",nonce:e.nonce}))))"));
505 }
506 if (returnData02.responseCode != 200) {
507 message_printer::printError<print_message_type::https>("sound_cloud_api::collectClientID() Error: " +
508 jsonifier::toString(returnData.responseCode.operator uint64_t()) + jsonifier::string{ newerString02.data() });
509 }
510 }
511 } catch (const https_error& error) {
512 message_printer::printError<print_message_type::https>("sound_cloud_request_builder::collectClientId() Error" + jsonifier::string{ error.what() });
513 }
514 return clientIdNew;
515 }
516
517 sound_cloud_api::sound_cloud_api(config_manager* configManagerNew, const snowflake guildIdNew) : sound_cloud_request_builder{ configManagerNew } {
518 guildId = static_cast<snowflake>(guildIdNew);
519 if (sound_cloud_request_builder::clientId == "") {
520 sound_cloud_request_builder::clientId = collectClientId();
521 }
522 }
523
524 void sound_cloud_api::weFailedToDownloadOrDecode(const song& songNew, std::coroutine_handle<co_routine<void, false>::promise_type> threadHandle, uint64_t recursionDepth) {
525 std::this_thread::sleep_for(1s);
526 if (recursionDepth < 10) {
527 ++recursionDepth;
528 song songNewer = constructDownloadInfo(songNew, 0);
529 downloadAndStreamAudio(songNewer, threadHandle, recursionDepth);
530 } else {
531 discord_core_client::getVoiceConnection(guildId).skip(true);
532 }
533 }
534
535 bool sound_cloud_api::areWeWorking() {
536 return areWeWorkingBool.load(std::memory_order_acquire);
537 }
538
539 co_routine<void, false> sound_cloud_api::downloadAndStreamAudio(const song songNew, std::coroutine_handle<co_routine<void, false>::promise_type> threadHandle,
540 uint64_t currentReconnectTries) {
541 try {
542 areWeWorkingBool.store(true, std::memory_order_release);
543 if (currentReconnectTries == 0) {
544 threadHandle = co_await newThreadAwaitable<void, false>();
545 }
546 uint64_t counter{};
547 jsonifier::vector<https_workload_data> workloadVector{};
548 for (uint64_t x = 0; x < songNew.finalDownloadUrls.size(); ++x) {
549 https_workload_data dataPackage03{ https_workload_type::SoundCloud_Get_Search_Results };
550 if (counter < songNew.finalDownloadUrls.size()) {
551 jsonifier::string baseUrlNew =
552 songNew.finalDownloadUrls.at(x).urlPath.substr(0, jsonifier::string_view{ "https://cf-hls-opus-media.sndcdn.com/media/" }.size());
553 jsonifier::string relativeUrl =
554 songNew.finalDownloadUrls.at(x).urlPath.substr(jsonifier::string_view{ "https://cf-hls-opus-media.sndcdn.com/media/" }.size());
555 dataPackage03.baseUrl = baseUrlNew;
556 dataPackage03.relativePath = relativeUrl;
557 }
558 dataPackage03.workloadClass = https_workload_class::Get;
559 workloadVector.emplace_back(std::move(dataPackage03));
560 }
561 jsonifier::vector<jsonifier::string> buffer{};
562 ogg_demuxer demuxer{};
563 for (uint64_t x = 0; x < songNew.finalDownloadUrls.size(); ++x) {
564 https_response_data result{ submitWorkloadAndGetResult(std::move(workloadVector.at(x))) };
565 if (result.responseCode != 200) {
566 weFailedToDownloadOrDecode(songNew, threadHandle, currentReconnectTries);
567 areWeWorkingBool.store(false, std::memory_order_release);
568 co_return;
569 }
570
571 if (result.responseData.size() > 0) {
572 buffer.emplace_back(std::move(result.responseData));
573 demuxer.writeData({ buffer.back().data(), buffer.back().size() });
574 demuxer.proceedDemuxing();
575 }
576 if (threadHandle.promise().stopRequested()) {
577 areWeWorkingBool.store(false, std::memory_order_release);
578 co_return;
579 }
580 bool didWeReceive{ true };
581 do {
582 audio_frame_data frameData{};
583 didWeReceive = demuxer.collectFrame(frameData);
584 if (threadHandle.promise().stopRequested()) {
585 areWeWorkingBool.store(false, std::memory_order_release);
586 co_return;
587 }
588 if (frameData.currentSize != 0) {
589 discord_core_client::getSongAPI(guildId).audioDataBuffer.send(std::move(frameData));
590 }
591 } while (didWeReceive && !threadHandle.promise().stopRequested());
592 if (threadHandle.promise().stopRequested()) {
593 areWeWorkingBool.store(false, std::memory_order_release);
594 co_return;
595 }
596 std::this_thread::sleep_for(1ms);
597 }
598 areWeWorkingBool.store(false, std::memory_order_release);
599 discord_core_client::getVoiceConnection(guildId).skip(false);
600 audio_frame_data frameData{};
601 discord_core_client::getSongAPI(guildId).audioDataBuffer.send(std::move(frameData));
602 co_return;
603 } catch (const https_error& error) {
604 message_printer::printError<print_message_type::https>("sound_cloud_request_builder::downloadAndStreamAudio() Error: " + jsonifier::string{ error.what() });
605 weFailedToDownloadOrDecode(songNew, threadHandle, currentReconnectTries);
606 areWeWorkingBool.store(false, std::memory_order_release);
607 }
608 co_return;
609 }
610
611 jsonifier::vector<song> sound_cloud_api::searchForSong(jsonifier::string_view searchQuery, uint64_t limit) {
612 return collectSearchResults(searchQuery, limit);
613 }
614
615 jsonifier::string sound_cloud_request_builder::clientId{};
616 };
617}
The main namespace for the forward-facing interfaces.