From 77ed51898157d99112be7550471ec06e32344c9e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 11 Oct 2020 21:35:37 +0200 Subject: Refactor plugin into seperate pages TODO: Readd 4chan login page, manganelo creators page, autocomplete --- src/plugins/Youtube.cpp | 317 +++++++++++++----------------------------------- 1 file changed, 83 insertions(+), 234 deletions(-) (limited to 'src/plugins/Youtube.cpp') diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 7e1fc63..40b296d 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -1,22 +1,9 @@ #include "../../plugins/Youtube.hpp" #include "../../include/Storage.hpp" -#include -#include #include #include namespace QuickMedia { - static void iterate_suggestion_result(const Json::Value &value, std::vector &result_items, int &iterate_count) { - ++iterate_count; - if(value.isArray()) { - for(const Json::Value &child : value) { - iterate_suggestion_result(child, result_items, iterate_count); - } - } else if(value.isString() && iterate_count > 2) { - result_items.push_back(value.asString()); - } - } - static std::shared_ptr parse_content_video_renderer(const Json::Value &content_item_json, std::unordered_set &added_videos) { if(!content_item_json.isObject()) return nullptr; @@ -85,135 +72,6 @@ namespace QuickMedia { return body_item; } - Youtube::Youtube() : Plugin("youtube") { - - } - - PluginResult Youtube::get_front_page(BodyItems &result_items) { - bool disabled = true; - if(disabled) - return PluginResult::OK; - - std::string url = "https://youtube.com/"; - - std::vector additional_args = { - { "-H", "x-spf-referer: " + url }, - { "-H", "x-youtube-client-name: 1" }, - { "-H", "x-youtube-client-version: 2.20200626.03.00" }, - { "-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, url + "?pbj=1", std::move(additional_args), true); - if(result != DownloadResult::OK) return download_result_to_plugin_result(result); - - if(!json_root.isArray()) - return PluginResult::ERR; - - std::unordered_set added_videos; - - for(const Json::Value &json_item : json_root) { - if(!json_item.isObject()) - continue; - - const Json::Value &response_json = json_item["response"]; - if(!response_json.isObject()) - continue; - - const Json::Value &contents_json = response_json["contents"]; - if(!contents_json.isObject()) - continue; - - const Json::Value &tcbrr_json = contents_json["twoColumnBrowseResultsRenderer"]; - if(!tcbrr_json.isObject()) - continue; - - const Json::Value &tabs_json = tcbrr_json["tabs"]; - if(!tabs_json.isArray()) - continue; - - for(const Json::Value &tab_item_json : tabs_json) { - if(!tab_item_json.isObject()) - continue; - - const Json::Value &tab_renderer_json = tab_item_json["tabRenderer"]; - if(!tab_renderer_json.isObject()) - continue; - - const Json::Value &content_json = tab_renderer_json["content"]; - if(!content_json.isObject()) - continue; - - const Json::Value &rich_grid_renderer = content_json["richGridRenderer"]; - if(!rich_grid_renderer.isObject()) - continue; - - const Json::Value &contents2_json = rich_grid_renderer["contents"]; - if(!contents2_json.isArray()) - continue; - - for(const Json::Value &contents_item : contents2_json) { - const Json::Value &rich_item_renderer_json = contents_item["richItemRenderer"]; - if(!rich_item_renderer_json.isObject()) - continue; - - const Json::Value &rich_item_contents = rich_item_renderer_json["content"]; - std::shared_ptr body_item = parse_content_video_renderer(rich_item_contents, added_videos); - if(body_item) - result_items.push_back(std::move(body_item)); - } - } - } - - return PluginResult::OK; - } - - std::string Youtube::autocomplete_search(const std::string &query) { - // Return the last result if the query is a substring of the autocomplete result - if(last_autocomplete_result.size() >= query.size() && memcmp(query.data(), last_autocomplete_result.data(), query.size()) == 0) - return last_autocomplete_result; - - std::string url = "https://clients1.google.com/complete/search?client=youtube&hl=en&gs_rn=64&gs_ri=youtube&ds=yt&cp=7&gs_id=x&q="; - url += url_param_encode(query); - - std::string server_response; - if(download_to_string(url, server_response, {}, use_tor, true) != DownloadResult::OK) - return query; - - size_t json_start = server_response.find_first_of('('); - if(json_start == std::string::npos) - return query; - ++json_start; - - size_t json_end = server_response.find_last_of(')'); - if(json_end == std::string::npos) - return query; - - if(json_end == 0 || json_start >= json_end) - return query; - - Json::Value json_root; - Json::CharReaderBuilder json_builder; - std::unique_ptr json_reader(json_builder.newCharReader()); - std::string json_errors; - if(!json_reader->parse(&server_response[json_start], &server_response[json_end], &json_root, &json_errors)) { - fprintf(stderr, "Youtube autocomplete search json error: %s\n", json_errors.c_str()); - return query; - } - - int iterate_count = 0; - std::vector result_items; - iterate_suggestion_result(json_root, result_items, iterate_count); - if(result_items.empty()) - return query; - - last_autocomplete_result = result_items[0]; - return result_items[0]; - } - // Returns empty string if continuation token can't be found static std::string item_section_renderer_get_continuation_token(const Json::Value &item_section_renderer_json) { const Json::Value &continuations_json = item_section_renderer_json["continuations"]; @@ -277,9 +135,77 @@ namespace QuickMedia { } } - SuggestionResult Youtube::update_search_suggestions(const std::string &text, BodyItems &result_items) { + static std::string remove_index_from_playlist_url(const std::string &url) { + std::string result = url; + size_t index = result.rfind("&index="); + if(index == std::string::npos) + return result; + return result.substr(0, index); + } + + static std::shared_ptr parse_compact_video_renderer_json(const Json::Value &item_json, std::unordered_set &added_videos) { + const Json::Value &compact_video_renderer_json = item_json["compactVideoRenderer"]; + if(!compact_video_renderer_json.isObject()) + return nullptr; + + const Json::Value &video_id_json = compact_video_renderer_json["videoId"]; + if(!video_id_json.isString()) + return nullptr; + + std::string video_id_str = video_id_json.asString(); + if(added_videos.find(video_id_str) != added_videos.end()) + return nullptr; + + std::string thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg"; + + const char *date = nullptr; + const Json::Value &published_time_text_json = compact_video_renderer_json["publishedTimeText"]; + if(published_time_text_json.isObject()) { + const Json::Value &text_json = published_time_text_json["simpleText"]; + if(text_json.isString()) + date = text_json.asCString(); + } + + const char *length = nullptr; + const Json::Value &length_text_json = compact_video_renderer_json["lengthText"]; + if(length_text_json.isObject()) { + const Json::Value &text_json = length_text_json["simpleText"]; + if(text_json.isString()) + length = text_json.asCString(); + } + + const char *title = nullptr; + const Json::Value &title_json = compact_video_renderer_json["title"]; + if(title_json.isObject()) { + const Json::Value &simple_text_json = title_json["simpleText"]; + if(simple_text_json.isString()) { + title = simple_text_json.asCString(); + } + } + + if(!title) + return nullptr; + + auto body_item = BodyItem::create(title); + /* TODO: Make date a different color */ + std::string date_str; + if(date) + date_str += date; + if(length) { + if(!date_str.empty()) + date_str += '\n'; + date_str += length; + } + body_item->set_description(std::move(date_str)); + body_item->url = "https://www.youtube.com/watch?v=" + video_id_str; + body_item->thumbnail_url = std::move(thumbnail_url); + added_videos.insert(video_id_str); + return body_item; + } + + SearchResult YoutubeSearchPage::search(const std::string &str, BodyItems &result_items) { std::string url = "https://youtube.com/results?search_query="; - url += url_param_encode(text); + url += url_param_encode(str); std::vector additional_args = { { "-H", "x-spf-referer: " + url }, @@ -292,11 +218,11 @@ namespace QuickMedia { //additional_args.insert(additional_args.end(), cookies.begin(), cookies.end()); Json::Value json_root; - DownloadResult result = download_json(json_root, url + "?pbj=1", std::move(additional_args), true); - if(result != DownloadResult::OK) return download_result_to_suggestion_result(result); + DownloadResult result = download_json(json_root, url + "&pbj=1", std::move(additional_args), true); + if(result != DownloadResult::OK) return download_result_to_search_result(result); if(!json_root.isArray()) - return SuggestionResult::ERR; + return SearchResult::ERR; std::string continuation_token; std::unordered_set added_videos; /* The input contains duplicates, filter them out! */ @@ -345,10 +271,17 @@ namespace QuickMedia { if(!continuation_token.empty()) search_suggestions_get_continuation(url, continuation_token, result_items); - return SuggestionResult::OK; + return SearchResult::OK; + } + + PluginResult YoutubeSearchPage::submit(const std::string &title, const std::string &url, std::vector &result_tabs) { + (void)title; + (void)url; + result_tabs.push_back(Tab{create_body(), std::make_unique(program), nullptr}); + return PluginResult::OK; } - void Youtube::search_suggestions_get_continuation(const std::string &url, const std::string &continuation_token, BodyItems &result_items) { + void YoutubeSearchPage::search_suggestions_get_continuation(const std::string &url, const std::string &continuation_token, BodyItems &result_items) { std::string next_url = url + "&pbj=1&ctoken=" + continuation_token; std::vector additional_args = { @@ -393,93 +326,9 @@ namespace QuickMedia { } } - std::vector Youtube::get_cookies() const { - if(use_tor) - return {}; - - Path cookies_filepath; - if(get_cookies_filepath(cookies_filepath, name) != 0) { - fprintf(stderr, "Warning: Failed to create youtube cookies file\n"); - return {}; - } - - return { - CommandArg{ "-b", cookies_filepath.data }, - CommandArg{ "-c", cookies_filepath.data } - }; - } - - static std::string remove_index_from_playlist_url(const std::string &url) { - std::string result = url; - size_t index = result.rfind("&index="); - if(index == std::string::npos) - return result; - return result.substr(0, index); - } - - static std::shared_ptr parse_compact_video_renderer_json(const Json::Value &item_json, std::unordered_set &added_videos) { - const Json::Value &compact_video_renderer_json = item_json["compactVideoRenderer"]; - if(!compact_video_renderer_json.isObject()) - return nullptr; - - const Json::Value &video_id_json = compact_video_renderer_json["videoId"]; - if(!video_id_json.isString()) - return nullptr; - - std::string video_id_str = video_id_json.asString(); - if(added_videos.find(video_id_str) != added_videos.end()) - return nullptr; - - std::string thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg"; - - const char *date = nullptr; - const Json::Value &published_time_text_json = compact_video_renderer_json["publishedTimeText"]; - if(published_time_text_json.isObject()) { - const Json::Value &text_json = published_time_text_json["simpleText"]; - if(text_json.isString()) - date = text_json.asCString(); - } - - const char *length = nullptr; - const Json::Value &length_text_json = compact_video_renderer_json["lengthText"]; - if(length_text_json.isObject()) { - const Json::Value &text_json = length_text_json["simpleText"]; - if(text_json.isString()) - length = text_json.asCString(); - } - - const char *title = nullptr; - const Json::Value &title_json = compact_video_renderer_json["title"]; - if(title_json.isObject()) { - const Json::Value &simple_text_json = title_json["simpleText"]; - if(simple_text_json.isString()) { - title = simple_text_json.asCString(); - } - } - - if(!title) - return nullptr; - - auto body_item = BodyItem::create(title); - /* TODO: Make date a different color */ - std::string date_str; - if(date) - date_str += date; - if(length) { - if(!date_str.empty()) - date_str += '\n'; - date_str += length; - } - body_item->set_description(std::move(date_str)); - body_item->url = "https://www.youtube.com/watch?v=" + video_id_str; - body_item->thumbnail_url = std::move(thumbnail_url); - added_videos.insert(video_id_str); - return body_item; - } - // TODO: Make this faster by using string search instead of parsing html. // TODO: If the result is a play - BodyItems Youtube::get_related_media(const std::string &url) { + BodyItems YoutubeVideoPage::get_related_media(const std::string &url) { BodyItems result_items; std::string modified_url = remove_index_from_playlist_url(url); -- cgit v1.2.3