42 template<>
struct core<discord_core_api::discord_core_internal::user> {
43 using value_type = discord_core_api::discord_core_internal::user;
44 static constexpr auto parseValue = createValue(
"lockedSafetyMode", &value_type::lockedSafetyMode);
47 template<>
struct core<discord_core_api::discord_core_internal::request> {
48 using value_type = discord_core_api::discord_core_internal::request;
49 static constexpr auto parseValue = createValue(
"useSsl", &value_type::useSsl);
52 template<>
struct core<discord_core_api::discord_core_internal::you_tube_request_client> {
53 using value_type = discord_core_api::discord_core_internal::you_tube_request_client;
54 static constexpr auto parseValue = createValue<&value_type::clientName, &value_type::androidSdkVersion, &value_type::clientVersion, &value_type::hl, &value_type::gl,
55 &value_type::osName, &value_type::osVersion, &value_type::platform>();
58 template<>
struct core<discord_core_api::discord_core_internal::you_tube_request_context> {
59 using value_type = discord_core_api::discord_core_internal::you_tube_request_context;
60 static constexpr auto parseValue =
61 createValue(
"client", &value_type::client,
"captionParams", &value_type::captionParams,
"request", &value_type::requestVal,
"user", &value_type::userVal);
64 template<>
struct core<discord_core_api::discord_core_internal::you_tube_request> {
65 using value_type = discord_core_api::discord_core_internal::you_tube_request;
66 static constexpr auto parseValue = createValue(
"videoId", &value_type::videoId,
"contentCheckOk", &value_type::contentCheckOk,
"racyCheckOk", &value_type::racyCheckOk,
67 "context", &value_type::context,
"playlistId", &value_type::playlistId,
"params", &value_type::params);
70 template<>
struct core<discord_core_api::discord_core_internal::thumbnail_element> {
71 using value_type = discord_core_api::discord_core_internal::thumbnail_element;
72 static constexpr auto parseValue = createValue(
"url", &value_type::url,
"width", &value_type::width);
75 template<>
struct core<discord_core_api::discord_core_internal::video_details_thumbnail> {
76 using value_type = discord_core_api::discord_core_internal::video_details_thumbnail;
77 static constexpr auto parseValue = createValue(
"thumbnails", &value_type::thumbnails);
80 template<>
struct core<discord_core_api::discord_core_internal::video_details> {
81 using value_type = discord_core_api::discord_core_internal::video_details;
82 static constexpr auto parseValue = createValue(
"title", &value_type::title,
"videoId", &value_type::videoId,
"thumbnail", &value_type::thumbnail,
"shortDescription",
83 &value_type::shortDescription,
"lengthSeconds", &value_type::lengthSeconds);
86 template<>
struct core<discord_core_api::discord_core_internal::format> {
87 using value_type = discord_core_api::discord_core_internal::format;
88 static constexpr auto parseValue =
89 createValue(
"url", &value_type::url,
"mimeType", &value_type::mimeType,
"bitrate", &value_type::bitrate,
"contentLength", &value_type::contentLength);
92 template<>
struct core<discord_core_api::discord_core_internal::streaming_data> {
93 using value_type = discord_core_api::discord_core_internal::streaming_data;
94 static constexpr auto parseValue = createValue(
"adaptiveFormats", &value_type::adaptiveFormats);
97 template<>
struct core<discord_core_api::discord_core_internal::data> {
98 using value_type = discord_core_api::discord_core_internal::data;
99 static constexpr auto parseValue = createValue(
"streamingData", &value_type::streamingData,
"videoDetails", &value_type::videoDetails);
102 template<>
struct core<discord_core_api::discord_core_internal::video_renderer> {
103 using value_type = discord_core_api::discord_core_internal::video_renderer;
104 static constexpr auto parseValue = createValue(
"videoId", &value_type::videoId);
107 template<>
struct core<discord_core_api::discord_core_internal::video_renderer_type> {
108 using value_type = discord_core_api::discord_core_internal::video_renderer_type;
109 static constexpr auto parseValue = createValue(
"videoRenderer", &value_type::videoRenderer);
112 template<>
struct core<discord_core_api::discord_core_internal::item_section_renderer_contents> {
113 using value_type = discord_core_api::discord_core_internal::item_section_renderer_contents;
114 static constexpr auto parseValue = createValue(
"contents", &value_type::contents);
117 template<>
struct core<discord_core_api::discord_core_internal::item_section_renderer> {
118 using value_type = discord_core_api::discord_core_internal::item_section_renderer;
119 static constexpr auto parseValue = createValue(
"itemSectionRenderer", &value_type::itemSectionRendererContents);
122 template<>
struct core<discord_core_api::discord_core_internal::section_list_renderer> {
123 using value_type = discord_core_api::discord_core_internal::section_list_renderer;
124 static constexpr auto parseValue = createValue(
"contents", &value_type::contents);
127 template<>
struct core<discord_core_api::discord_core_internal::primary_contents> {
128 using value_type = discord_core_api::discord_core_internal::primary_contents;
129 static constexpr auto parseValue = createValue(
"sectionListRenderer", &value_type::sectionListRenderer);
132 template<>
struct core<discord_core_api::discord_core_internal::two_column_search_results_renderer> {
133 using value_type = discord_core_api::discord_core_internal::two_column_search_results_renderer;
134 static constexpr auto parseValue = createValue(
"primaryContents", &value_type::primaryContents);
137 template<>
struct core<discord_core_api::discord_core_internal::contents01> {
138 using value_type = discord_core_api::discord_core_internal::contents01;
139 static constexpr auto parseValue = createValue(
"twoColumnSearchResultsRenderer", &value_type::twoColumnSearchResultsRenderer);
142 template<>
struct core<discord_core_api::discord_core_internal::you_tube_search_results> {
143 using value_type = discord_core_api::discord_core_internal::you_tube_search_results;
144 static constexpr auto parseValue = createValue(
"contents", &value_type::contents);
150 namespace discord_core_internal {
152 you_tube_request_builder::you_tube_request_builder(config_manager* configManagerNew) : https_client_core{ jsonifier::string{ configManagerNew->getBotToken() } } {};
154 inline bool collectVideoIdFromSearchQuery(jsonifier::string_view url, jsonifier::string& stringNew) {
155 std::regex pattern(
"v=([a-zA-Z0-9_\\-]+)");
158 auto newUrl =
static_cast<std::string
>(url);
159 if (std::regex_search(newUrl, match, pattern)) {
160 stringNew = match[1].str();
167 song you_tube_request_builder::collectSingleResult(jsonifier::string_view searchQuery) {
170 songNew.type = song_type::YouTube;
171 songNew.songId = searchQuery;
172 songNew = constructDownloadInfo(songNew, 0);
176 jsonifier::vector<song> you_tube_request_builder::collectSearchResults(jsonifier::string_view searchQuery, uint64_t limit) {
177 https_workload_data dataPackage{ https_workload_type::YouTube_Get_Search_Results };
178 jsonifier::vector<song> searchResults{};
179 jsonifier::string theId{};
180 if (collectVideoIdFromSearchQuery(searchQuery, theId)) {
181 searchResults.emplace_back(collectSingleResult(theId));
182 return searchResults;
184 dataPackage.baseUrl = baseUrl;
185 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";
186 dataPackage.relativePath =
"/results?search_query=" + urlEncode(searchQuery);
187 dataPackage.workloadClass = https_workload_class::Get;
188 https_response_data returnData = submitWorkloadAndGetResult(std::move(dataPackage));
189 if (returnData.responseCode != 200) {
190 message_printer::printError<print_message_type::https>(
191 "you_tube_request_builder::collectSearchResults() error: " + jsonifier::toString(returnData.responseCode.operator uint64_t()) + returnData.responseData);
193 auto varInitFind = returnData.responseData.find(
"var ytInitialData = ");
194 if (varInitFind != jsonifier::string::npos) {
195 jsonifier::string newString00 =
"var ytInitialData = ";
196 jsonifier::string newString = returnData.responseData.substr(varInitFind + newString00.size());
197 jsonifier::string stringSequence =
";</script><script nonce=";
198 newString = newString.substr(0, newString.find(stringSequence));
199 you_tube_search_results you_tubeSearchResults{};
200 parser.parseJson(you_tubeSearchResults, newString);
201 for (
auto& value: parser.getErrors()) {
202 message_printer::printError<print_message_type::https>(value.reportError());
204 for (
auto& value: you_tubeSearchResults.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents) {
205 for (
auto& value02: value.itemSectionRendererContents.contents) {
206 if (value02.videoRenderer.videoId !=
"") {
208 songNew.type = song_type::YouTube;
209 songNew.songId = value02.videoRenderer.videoId;
210 searchResults.emplace_back(constructDownloadInfo(songNew, 0));
212 if (searchResults.size() >= limit) {
218 return searchResults;
221 song you_tube_request_builder::constructDownloadInfo(
const song& songNew, uint64_t currentRecursionDepth) {
222 https_response_data responseData{};
224 discord_core_api::discord_core_internal::you_tube_request requestData{};
225 requestData.videoId = songNew.songId;
226 https_workload_data dataPackage02{ https_workload_type::YouTube_Get_Search_Results };
227 dataPackage02.baseUrl =
"https://music.you_tube.com";
228 dataPackage02.headersToInsert[
"User-Agent"] =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
229 dataPackage02.headersToInsert[
"Origin"] =
"https://music.you_tube.com";
230 dataPackage02.headersToInsert[
"Content-Type"] =
"application/json; charset=utf-8";
231 dataPackage02.relativePath =
"/you_tubei/v1/player?key=AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w";
232 parser.serializeJson(requestData, dataPackage02.content);
233 dataPackage02.content =
234 "{\"context\":{\"client\":{\"clientName\":\"IOS\",\"clientVersion\":\"17.13.3\",\"hl\":\"en\"},\"user\":{\"lockedSafetyMode\":false},\"request\":{\"useSsl\":"
235 "true},\"captionParams\":{}},\"params\":{}}";
236 std::cout <<
"REQUEST CONTENT: " << dataPackage02.content << std::endl;
237 dataPackage02.workloadClass = https_workload_class::Post;
238 responseData = submitWorkloadAndGetResult(std::move(dataPackage02));
239 if (responseData.responseCode != 204 && responseData.responseCode != 201 && responseData.responseCode != 200) {
240 message_printer::printError<print_message_type::https>(
"you_tube_request_builder::constructDownloadInfo() 01 error: " +
241 jsonifier::toString(responseData.responseCode.operator uint64_t()) +
", " + responseData.responseData);
244 jsonifier::vector<format> potentialFormats{};
245 parser.parseJson(dataNew, responseData.responseData);
246 for (
auto& value: parser.getErrors()) {
247 message_printer::printError<print_message_type::https>(value.reportError());
249 for (
auto& value: dataNew.streamingData.adaptiveFormats) {
250 if (value.mimeType ==
"audio/webm; codecs=\"opus\"") {
251 potentialFormats.emplace_back(value);
254 uint64_t currentMax{};
255 int64_t maxIndex{
static_cast<int64_t
>(std::numeric_limits<uint64_t>::max()) };
256 for (uint64_t x = 0; x < potentialFormats.size(); ++x) {
257 if (potentialFormats.at(x).bitrate >
static_cast<int64_t
>(currentMax)) {
258 maxIndex =
static_cast<int64_t
>(x);
262 song newerSong{ songNew };
263 jsonifier::string downloadBaseUrl{};
264 newerSong.type = song_type::YouTube;
266 jsonifier::string thumbnailUrl{};
267 if (dataNew.videoDetails.thumbnail.thumbnails.size() > 0) {
268 uint32_t currentLargestThumbnailWidth{};
269 uint32_t currentThumbnailIndex{};
270 for (uint32_t x = 0; x < dataNew.videoDetails.thumbnail.thumbnails.size(); ++x) {
271 if (dataNew.videoDetails.thumbnail.thumbnails[x].width > currentLargestThumbnailWidth) {
272 currentThumbnailIndex = x;
275 thumbnailUrl = dataNew.videoDetails.thumbnail.thumbnails[currentThumbnailIndex].url;
277 auto httpsFind = potentialFormats[
static_cast<uint64_t
>(maxIndex)].url.find(
"https://");
278 auto videoPlaybackFind = potentialFormats[
static_cast<uint64_t
>(maxIndex)].url.find(
"/videoplayback?");
279 if (httpsFind != jsonifier::string::npos && videoPlaybackFind != jsonifier::string::npos) {
280 jsonifier::string newString00 =
"https://";
281 downloadBaseUrl = potentialFormats[
static_cast<uint64_t
>(maxIndex)].url.substr(httpsFind + newString00.size(), videoPlaybackFind - newString00.size());
283 auto& newString = dataNew.videoDetails.shortDescription;
284 if (newString.size() > 0) {
285 if (newString.size() > 256) {
286 newString = newString.substr(0, 256);
288 newerSong.description = utf8MakeValid(newString);
289 newerSong.description +=
"...";
291 jsonifier::string requestNew = potentialFormats[
static_cast<uint64_t
>(maxIndex)].url.substr(potentialFormats[
static_cast<uint64_t
>(maxIndex)].url.find(
".com") + 4);
292 newerSong.finalDownloadUrls.resize(3);
293 download_url downloadUrl01{};
294 downloadUrl01.contentSize = jsonifier::strToUint64(potentialFormats[
static_cast<uint64_t
>(maxIndex)].contentLength.data());
295 downloadUrl01.urlPath = downloadBaseUrl;
296 download_url downloadUrl02{};
297 downloadUrl02.contentSize = jsonifier::strToUint64(potentialFormats[
static_cast<uint64_t
>(maxIndex)].contentLength.data());
298 downloadUrl02.urlPath = requestNew;
299 newerSong.finalDownloadUrls.at(0) = downloadUrl01;
300 newerSong.finalDownloadUrls.at(1) = downloadUrl02;
301 newerSong.viewUrl = newerSong.firstDownloadUrl;
302 newerSong.duration = time_stamp::convertMsToDurationString(jsonifier::strToUint64(dataNew.videoDetails.lengthSeconds) * 1000);
303 newerSong.viewUrl =
"https://www.you_tube.com/watch?v=" + songNew.songId;
304 newerSong.contentLength = downloadUrl02.contentSize;
305 newerSong.thumbnailUrl = thumbnailUrl;
306 newerSong.songTitle = dataNew.videoDetails.title;
307 newerSong.type = song_type::YouTube;
310 }
catch (
const https_error& error) {
311 if (currentRecursionDepth <= 10) {
312 ++currentRecursionDepth;
313 return constructDownloadInfo(songNew, currentRecursionDepth);
315 message_printer::printError<print_message_type::https>(
"you_tube_request_builder::constructDownloadInfo() error: " + jsonifier::string{ error.what() });
321 song you_tube_request_builder::collectFinalSong(
const song& songNew) {
322 song newerSong{ songNew };
323 newerSong.firstDownloadUrl =
static_cast<jsonifier::string
>(baseUrl) +
"/watch?v=" + newerSong.songId +
"&hl=en";
324 newerSong = constructDownloadInfo(newerSong, 0);
328 you_tube_api::you_tube_api(config_manager* configManagerNew,
const snowflake guildIdNew) : you_tube_request_builder{ configManagerNew } {
329 guildId = guildIdNew;
332 void you_tube_api::weFailedToDownloadOrDecode(
const song& songNew, std::coroutine_handle<co_routine<void, false>::promise_type> threadHandle, uint64_t recursionDepth) {
333 std::this_thread::sleep_for(1s);
334 if (recursionDepth < 10) {
336 song songNewer = constructDownloadInfo(songNew, 0);
337 downloadAndStreamAudio(songNewer, threadHandle, recursionDepth);
339 discord_core_client::getVoiceConnection(guildId).skip(
true);
343 co_routine<void, false> you_tube_api::downloadAndStreamAudio(
const song songNew, std::coroutine_handle<co_routine<void, false>::promise_type> threadHandle,
344 uint64_t currentReconnectTries) {
346 areWeWorkingBool.store(
true, std::memory_order_release);
347 if (currentReconnectTries == 0) {
348 threadHandle =
co_await newThreadAwaitable<void, false>();
350 if (songNew.type != song_type::YouTube) {
351 message_printer::printError<print_message_type::general>(
"Failed to have the correct song type.");
354 uint64_t intervalCount{ songNew.contentLength / (1024ULL * 1024ULL) + 1ULL };
355 uint64_t remainder{ songNew.contentLength % (1024ULL * 1024ULL) };
356 uint64_t currentStart{};
357 uint64_t currentEnd{ intervalCount > 1 ? (1024ULL * 1024ULL) : remainder };
358 jsonifier::vector<https_workload_data> workloadVector{};
359 for (uint64_t x = 0; x < intervalCount; ++x) {
360 https_workload_data workloadData{ https_workload_type::YouTube_Get_Search_Results };
361 if (songNew.finalDownloadUrls.size() > 0) {
362 if (songNew.finalDownloadUrls.at(0).urlPath.find(
".com") != jsonifier::string::npos) {
363 workloadData.baseUrl = songNew.finalDownloadUrls.at(0).urlPath.substr(0, songNew.finalDownloadUrls.at(0).urlPath.find(
".com") + 4);
366 weFailedToDownloadOrDecode(songNew, threadHandle, currentReconnectTries);
367 areWeWorkingBool.store(
false, std::memory_order_release);
370 workloadData.workloadClass = https_workload_class::Get;
371 workloadData.headersToInsert[
"User-Agent"] =
"com.google.android.you_tube/17.10.35 (Linux; U; Android 12; US) gzip";
372 workloadData.headersToInsert[
"Connection"] =
"Keep-Alive";
373 workloadData.headersToInsert[
"Host"] = songNew.finalDownloadUrls.at(0).urlPath;
374 workloadData.headersToInsert[
"Origin"] =
"https://music.you_tube.com";
375 workloadData.relativePath = songNew.finalDownloadUrls.at(1).urlPath +
"&range=" + jsonifier::toString(currentStart) +
"-" + jsonifier::toString(currentEnd);
376 workloadVector.emplace_back(std::move(workloadData));
377 currentStart = currentEnd;
378 currentEnd += x == intervalCount - 2 ? remainder : (1024ULL * 1024ULL);
380 jsonifier::string_base<uint8_t> buffer{};
381 matroska_demuxer demuxer{};
383 while (index < intervalCount || !demuxer.areWeDone() && !threadHandle.promise().stopRequested()) {
384 if (index < intervalCount) {
385 https_response_data result{ submitWorkloadAndGetResult(std::move(workloadVector[index])) };
386 if (result.responseCode != 200) {
387 weFailedToDownloadOrDecode(songNew, threadHandle, currentReconnectTries);
388 areWeWorkingBool.store(
false, std::memory_order_release);
391 if (result.responseData.size() > 0) {
393 auto oldSize = buffer.size();
394 buffer.resize(buffer.size() + result.responseData.size());
395 std::memcpy(buffer.data() + oldSize, result.responseData.data(), result.responseData.size());
396 demuxer.writeData({ buffer.data(), buffer.size() });
397 demuxer.proceedDemuxing();
400 bool didWeReceive{
true };
402 audio_frame_data frameData{};
403 didWeReceive = demuxer.collectFrame(frameData);
404 if (threadHandle.promise().stopRequested()) {
405 areWeWorkingBool.store(
false, std::memory_order_release);
408 if (frameData.currentSize != 0) {
409 discord_core_client::getSongAPI(guildId).audioDataBuffer.send(std::move(frameData));
411 }
while (didWeReceive);
412 std::this_thread::sleep_for(1ms);
414 areWeWorkingBool.store(
false, std::memory_order_release);
415 discord_core_client::getVoiceConnection(guildId).skip(
false);
416 audio_frame_data frameData{};
417 discord_core_client::getSongAPI(guildId).audioDataBuffer.send(std::move(frameData));
419 }
catch (
const https_error& error) {
420 message_printer::printError<print_message_type::https>(
"you_tube_api::downloadAndStreamAudio() error: " + jsonifier::string{ error.what() });
421 weFailedToDownloadOrDecode(songNew, threadHandle, currentReconnectTries);
422 areWeWorkingBool.store(
false, std::memory_order_release);
427 bool you_tube_api::areWeWorking() {
428 return areWeWorkingBool.load(std::memory_order_acquire);
431 jsonifier::vector<song> you_tube_api::searchForSong(jsonifier::string_view searchQuery, uint64_t limit) {
432 return collectSearchResults(searchQuery, limit);
The main namespace for the forward-facing interfaces.