From 110296e210e7de15c6a219b25d935feb9069131e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 9 Apr 2022 00:01:05 +0200 Subject: Youtube: fix search, related videos, comments and channel page after youtube REVERTED their changes --- src/plugins/Youtube.cpp | 229 +++++++++++++++++++++++++++++------------------- 1 file changed, 140 insertions(+), 89 deletions(-) diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index c32e22e..3177f4d 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -676,29 +676,26 @@ namespace QuickMedia { return ""; } - static BodyItems parse_channel_videos(const Json::Value &json_root, std::string &continuation_token, std::unordered_set &added_videos) { - BodyItems body_items; - + static void parse_channel_videos(const Json::Value &json_root, std::string &continuation_token, std::unordered_set &added_videos, BodyItems &body_items) { if(!json_root.isObject()) - return body_items; + return; const Json::Value &response_json = json_root["response"]; if(!response_json.isObject()) - return body_items; + return; const Json::Value &contents_json = response_json["contents"]; if(!contents_json.isObject()) - return body_items; + return; const Json::Value &tcbrr_json = contents_json["twoColumnBrowseResultsRenderer"]; if(!tcbrr_json.isObject()) - return body_items; + return; const Json::Value &tabs_json = tcbrr_json["tabs"]; if(!tabs_json.isArray()) - return body_items; + return; - std::string new_continuation_token; for(const Json::Value &tab_json : tabs_json) { if(!tab_json.isObject()) continue; @@ -747,8 +744,8 @@ namespace QuickMedia { if(!item_json.isObject()) continue; - if(new_continuation_token.empty()) - new_continuation_token = item_section_renderer_get_continuation_token(item_json); + if(continuation_token.empty()) + continuation_token = item_section_renderer_get_continuation_token(item_json); const Json::Value &grid_video_renderer = item_json["gridVideoRenderer"]; if(!grid_video_renderer.isObject()) @@ -761,9 +758,6 @@ namespace QuickMedia { } } } - - continuation_token = std::move(new_continuation_token); - return body_items; } static void parse_section_list_renderer(const Json::Value §ion_list_renderer_json, std::string &continuation_token, BodyItems &result_items, std::unordered_set &added_videos) { @@ -875,6 +869,29 @@ namespace QuickMedia { return PluginResult::OK; } + static void search_page_submit_suggestion_handler(const Json::Value &json_item, std::string &continuation_token, BodyItems &result_items, std::unordered_set &added_videos) { + if(!json_item.isObject()) + return; + + const Json::Value &response_json = json_item["response"]; + if(!response_json.isObject()) + return; + + const Json::Value &contents_json = response_json["contents"]; + if(!contents_json.isObject()) + return; + + const Json::Value &tcsrr_json = contents_json["twoColumnSearchResultsRenderer"]; + if(!tcsrr_json.isObject()) + return; + + const Json::Value &primary_contents_json = tcsrr_json["primaryContents"]; + if(!primary_contents_json.isObject()) + return; + + parse_section_list_renderer(primary_contents_json["sectionListRenderer"], continuation_token, result_items, added_videos); + } + PluginResult YoutubeSearchPage::submit_suggestion(const SubmitArgs &args, BodyItems &result_items) { continuation_token.clear(); current_page = 0; @@ -897,26 +914,17 @@ namespace QuickMedia { DownloadResult result = download_json(json_root, search_url + "&pbj=1&gl=US&hl=en", std::move(additional_args), true); if(result != DownloadResult::OK) return download_result_to_plugin_result(result); - if(!json_root.isObject()) - return PluginResult::ERR; - - const Json::Value &response_json = json_root["response"]; - if(!response_json.isObject()) - return PluginResult::OK; - - const Json::Value &contents_json = response_json["contents"]; - if(!contents_json.isObject()) - return PluginResult::OK; - - const Json::Value &tcsrr_json = contents_json["twoColumnSearchResultsRenderer"]; - if(!tcsrr_json.isObject()) + if(json_root.isObject()) { + search_page_submit_suggestion_handler(json_root, continuation_token, result_items, added_videos); return PluginResult::OK; + } - const Json::Value &primary_contents_json = tcsrr_json["primaryContents"]; - if(!primary_contents_json.isObject()) - return PluginResult::OK; + if(!json_root.isArray()) + return PluginResult::ERR; - parse_section_list_renderer(primary_contents_json["sectionListRenderer"], continuation_token, result_items, added_videos); + for(const Json::Value &json_item : json_root) { + search_page_submit_suggestion_handler(json_item, continuation_token, result_items, added_videos); + } return PluginResult::OK; } @@ -926,39 +934,18 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult YoutubeSearchPage::search_get_continuation(const std::string &url, const std::string ¤t_continuation_token, BodyItems &result_items) { - if(current_continuation_token.empty()) - return PluginResult::OK; - - std::string next_url = url + "&pbj=1&gl=US&hl=en&ctoken=" + current_continuation_token; - - std::vector additional_args = { - { "-H", "x-spf-referer: " + url }, - { "-H", "x-youtube-client-name: 1" }, - { "-H", "x-spf-previous: " + url }, - { "-H", youtube_client_version }, - { "-H", "referer: " + url } - }; - - std::vector cookies = get_cookies(); - additional_args.insert(additional_args.end(), cookies.begin(), cookies.end()); - - Json::Value json_root; - DownloadResult result = download_json(json_root, next_url, std::move(additional_args), true); - if(result != DownloadResult::OK) return download_result_to_plugin_result(result); - - if(!json_root.isObject()) - return PluginResult::ERR; + static void search_page_search_get_continuation(const Json::Value &json_item, std::string &new_continuation_token, std::unordered_set &added_videos, BodyItems &result_items) { + if(!json_item.isObject()) + return; - const Json::Value &response_json = json_root["response"]; + const Json::Value &response_json = json_item["response"]; if(!response_json.isObject()) - return PluginResult::OK; + return; const Json::Value &on_response_received_commands_json = response_json["onResponseReceivedCommands"]; if(!on_response_received_commands_json.isArray()) - return PluginResult::OK; + return; - std::string new_continuation_token; for(const Json::Value &response_received_command : on_response_received_commands_json) { if(!response_received_command.isObject()) continue; @@ -987,6 +974,42 @@ namespace QuickMedia { parse_item_section_renderer(item_section_renderer_json, added_videos, result_items); } } + } + + PluginResult YoutubeSearchPage::search_get_continuation(const std::string &url, const std::string ¤t_continuation_token, BodyItems &result_items) { + if(current_continuation_token.empty()) + return PluginResult::OK; + + std::string next_url = url + "&pbj=1&gl=US&hl=en&ctoken=" + current_continuation_token; + std::string new_continuation_token; + + std::vector additional_args = { + { "-H", "x-spf-referer: " + url }, + { "-H", "x-youtube-client-name: 1" }, + { "-H", "x-spf-previous: " + url }, + { "-H", youtube_client_version }, + { "-H", "referer: " + url } + }; + + std::vector cookies = get_cookies(); + additional_args.insert(additional_args.end(), cookies.begin(), cookies.end()); + + Json::Value json_root; + DownloadResult result = download_json(json_root, next_url, std::move(additional_args), true); + if(result != DownloadResult::OK) return download_result_to_plugin_result(result); + + if(json_root.isObject()) { + search_page_search_get_continuation(json_root, new_continuation_token, added_videos, result_items); + continuation_token = std::move(new_continuation_token); + return PluginResult::OK; + } + + if(!json_root.isArray()) + return PluginResult::ERR; + + for(const Json::Value &json_item : json_root) { + search_page_search_get_continuation(json_item, new_continuation_token, added_videos, result_items); + } continuation_token = std::move(new_continuation_token); return PluginResult::OK; @@ -1538,6 +1561,8 @@ namespace QuickMedia { PluginResult YoutubeChannelPage::lazy_fetch(BodyItems &result_items) { added_videos.clear(); + continuation_token.clear(); + std::vector additional_args = { { "-H", "x-spf-referer: " + url }, { "-H", "x-youtube-client-name: 1" }, @@ -1552,7 +1577,19 @@ namespace QuickMedia { Json::Value json_root; DownloadResult result = download_json(json_root, url + "/videos?pbj=1&gl=US&hl=en", std::move(additional_args), true); if(result != DownloadResult::OK) return download_result_to_plugin_result(result); - result_items = parse_channel_videos(json_root, continuation_token, added_videos); + + if(json_root.isObject()) { + parse_channel_videos(json_root, continuation_token, added_videos, result_items); + return PluginResult::OK; + } + + if(!json_root.isArray()) + return PluginResult::ERR; + + for(const Json::Value &json_item : json_root) { + parse_channel_videos(json_item, continuation_token, added_videos, result_items); + } + return PluginResult::OK; } @@ -1874,58 +1911,36 @@ namespace QuickMedia { return std::to_string(it->second.time_pos_sec); } - BodyItems YoutubeVideoPage::get_related_media(const std::string &url) { - comments_continuation_token.clear(); - BodyItems result_items; - - std::string video_id; - if(!youtube_url_extract_id(url, video_id)) { - fprintf(stderr, "Failed to extract youtube id from %s\n", url.c_str()); - return result_items; - } - - std::vector additional_args = { - { "-H", "x-youtube-client-name: 1" }, - { "-H", youtube_client_version }, - }; - - std::vector cookies = get_cookies(); - additional_args.insert(additional_args.end(), cookies.begin(), cookies.end()); - - // TODO: Remove this code completely and replace with existing player? api - Json::Value json_root; - DownloadResult download_result = download_json(json_root, "https://www.youtube.com/watch?v=" + video_id + "&pbj=1&gl=US&hl=en", additional_args, true); - if(download_result != DownloadResult::OK) return result_items; - + static void video_page_related_media_handler(const Json::Value &json_root, std::string &comments_continuation_token, BodyItems &result_items) { if(!json_root.isObject()) - return result_items; + return; const Json::Value &response_json = json_root["response"]; if(!response_json.isObject()) - return result_items; + return; const Json::Value &contents_json = response_json["contents"]; if(!contents_json.isObject()) - return result_items; + return; const Json::Value &tcwnr_json = contents_json["twoColumnWatchNextResults"]; if(!tcwnr_json.isObject()) - return result_items; + return; if(comments_continuation_token.empty()) comments_continuation_token = two_column_watch_next_results_get_comments_continuation_token(tcwnr_json); const Json::Value &secondary_results_json = tcwnr_json["secondaryResults"]; if(!secondary_results_json.isObject()) - return result_items; + return; const Json::Value &secondary_results2_json = secondary_results_json["secondaryResults"]; if(!secondary_results2_json.isObject()) - return result_items; + return; const Json::Value &results_json = secondary_results2_json["results"]; if(!results_json.isArray()) - return result_items; + return; std::unordered_set added_videos; for(const Json::Value &item_json : results_json) { @@ -1953,6 +1968,42 @@ namespace QuickMedia { result_items.push_back(std::move(body_item)); } } + } + + BodyItems YoutubeVideoPage::get_related_media(const std::string &url) { + comments_continuation_token.clear(); + BodyItems result_items; + + std::string video_id; + if(!youtube_url_extract_id(url, video_id)) { + fprintf(stderr, "Failed to extract youtube id from %s\n", url.c_str()); + return result_items; + } + + std::vector additional_args = { + { "-H", "x-youtube-client-name: 1" }, + { "-H", youtube_client_version }, + }; + + std::vector cookies = get_cookies(); + additional_args.insert(additional_args.end(), cookies.begin(), cookies.end()); + + // TODO: Remove this code completely and replace with existing player? api + Json::Value json_root; + DownloadResult download_result = download_json(json_root, "https://www.youtube.com/watch?v=" + video_id + "&pbj=1&gl=US&hl=en", additional_args, true); + if(download_result != DownloadResult::OK) return result_items; + + if(json_root.isObject()) { + video_page_related_media_handler(json_root, comments_continuation_token, result_items); + return result_items; + } + + if(!json_root.isArray()) + return result_items; + + for(const Json::Value &json_item : json_root) { + video_page_related_media_handler(json_item, comments_continuation_token, result_items); + } return result_items; } -- cgit v1.2.3