aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/MediaGeneric.cpp8
-rw-r--r--src/plugins/Youtube.cpp139
2 files changed, 102 insertions, 45 deletions
diff --git a/src/plugins/MediaGeneric.cpp b/src/plugins/MediaGeneric.cpp
index c829a33..d536a09 100644
--- a/src/plugins/MediaGeneric.cpp
+++ b/src/plugins/MediaGeneric.cpp
@@ -191,9 +191,11 @@ namespace QuickMedia {
return result_items;
}
- std::unique_ptr<Page> MediaGenericVideoPage::create_search_page(Program*, int &search_delay) {
- search_delay = 500; // TODO: Make configurable?
- return std::make_unique<MediaGenericSearchPage>(*search_page);
+ bool MediaGenericVideoPage::create_search_page(Program*, Tab &tab) {
+ tab.body = create_body(false, true);
+ tab.page = std::make_unique<MediaGenericSearchPage>(*search_page);
+ tab.search_bar = create_search_bar("Search...", 500); // TODO: Make search delay configurable?
+ return true;
}
std::unique_ptr<RelatedVideosPage> MediaGenericVideoPage::create_related_videos_page(Program *program) {
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index ea72f03..5b0591c 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -764,12 +764,98 @@ namespace QuickMedia {
}
SearchResult YoutubeSearchPage::search(const std::string &str, BodyItems &result_items) {
+ if(str.empty())
+ return SearchResult::OK;
+
+ // TODO: Find this search url from youtube.com/... searchbox.js, and the url to that script from youtube.com/ html
+ std::string url = "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&hl=en&gl=us&sugexp=rdcfrc%2Ccfro%3D1%2Cfp.cfr%3D1&gs_rn=64&gs_ri=youtube&ds=yt&cp=4&gs_id=f&xhr=t&xssi=t&q=";
+ url += url_param_encode(str);
+ if(!video_id.empty())
+ url += "&video_id=" + video_id;
+
+ std::vector<CommandArg> additional_args = {
+ { "-H", "origin: https://www.youtube.com" },
+ { "-H", "referer: https://www.youtube.com/" }
+ };
+
+ std::vector<CommandArg> cookies = get_cookies();
+ additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
+
+ std::string website_data;
+ DownloadResult result = download_to_string(url, website_data, std::move(additional_args), true);
+ if(result != DownloadResult::OK) return download_result_to_search_result(result);
+
+ const size_t json_start_index = website_data.find('[');
+ if(json_start_index == std::string::npos)
+ return SearchResult::ERR;
+
+ Json::Value json_root;
+ Json::CharReaderBuilder json_builder;
+ std::unique_ptr<Json::CharReader> json_reader(json_builder.newCharReader());
+ std::string json_errors;
+ if(!json_reader->parse(&website_data[json_start_index], &website_data[website_data.size()], &json_root, &json_errors)) {
+ fprintf(stderr, "youtube search error: %s\n", json_errors.c_str());
+ return SearchResult::ERR;
+ }
+
+ if(!json_root.isArray() || json_root.size() < 2)
+ return SearchResult::ERR;
+
+ const Json::Value &search_result_list_json = json_root[1];
+ if(!search_result_list_json.isArray())
+ return SearchResult::ERR;
+
+ for(const Json::Value &json_item : search_result_list_json) {
+ if(!json_item.isArray() || json_item.size() == 0)
+ continue;
+
+ const Json::Value &search_result_json = json_item[0];
+ if(!search_result_json.isString())
+ continue;
+
+ auto body_item = BodyItem::create(search_result_json.asString());
+ body_item->url = body_item->get_title();
+ result_items.push_back(std::move(body_item));
+ }
+
+ if(result_items.empty() || !strcase_equals(str.c_str(), result_items.front()->get_title().c_str())) {
+ auto body_item = BodyItem::create(str);
+ body_item->url = str;
+ result_items.insert(result_items.begin(), std::move(body_item));
+ }
+
+ return SearchResult::OK;
+ }
+
+ PluginResult YoutubeSearchPage::get_page(const std::string&, int page, BodyItems &result_items) {
+ while(current_page < page) {
+ PluginResult plugin_result = search_get_continuation(search_url, continuation_token, result_items);
+ if(plugin_result != PluginResult::OK) return plugin_result;
+ ++current_page;
+ }
+ return PluginResult::OK;
+ }
+
+ PluginResult YoutubeSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
+ if(url.empty())
+ return PluginResult::OK;
+
+ if(strncmp(url.c_str(), "https://www.youtube.com/channel/", 32) == 0) {
+ // TODO: Make all pages (for all services) lazy fetch in a similar manner!
+ result_tabs.push_back(Tab{create_body(false, true), std::make_unique<YoutubeChannelPage>(program, url, "", title), create_search_bar("Search...", 350)});
+ } else {
+ result_tabs.push_back(Tab{nullptr, std::make_unique<YoutubeVideoPage>(program, url), nullptr});
+ }
+ return PluginResult::OK;
+ }
+
+ PluginResult YoutubeSearchPage::submit_suggestion(const std::string&, const std::string &url, BodyItems &result_items) {
continuation_token.clear();
current_page = 0;
added_videos.clear();
search_url = "https://www.youtube.com/results?search_query=";
- search_url += url_param_encode(str);
+ search_url += url_param_encode(url);
std::vector<CommandArg> additional_args = {
{ "-H", "x-spf-referer: " + search_url },
@@ -783,10 +869,10 @@ namespace QuickMedia {
Json::Value json_root;
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_search_result(result);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
if(!json_root.isArray())
- return SearchResult::ERR;
+ return PluginResult::ERR;
for(const Json::Value &json_item : json_root) {
if(!json_item.isObject())
@@ -811,28 +897,6 @@ namespace QuickMedia {
parse_section_list_renderer(primary_contents_json["sectionListRenderer"], continuation_token, result_items, added_videos);
}
- return SearchResult::OK;
- }
-
- PluginResult YoutubeSearchPage::get_page(const std::string&, int page, BodyItems &result_items) {
- while(current_page < page) {
- PluginResult plugin_result = search_get_continuation(search_url, continuation_token, result_items);
- if(plugin_result != PluginResult::OK) return plugin_result;
- ++current_page;
- }
- return PluginResult::OK;
- }
-
- PluginResult YoutubeSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- if(url.empty())
- return PluginResult::OK;
-
- if(strncmp(url.c_str(), "https://www.youtube.com/channel/", 32) == 0) {
- // TODO: Make all pages (for all services) lazy fetch in a similar manner!
- result_tabs.push_back(Tab{create_body(false, true), std::make_unique<YoutubeChannelPage>(program, url, "", title), create_search_bar("Search...", 350)});
- } else {
- result_tabs.push_back(Tab{nullptr, std::make_unique<YoutubeVideoPage>(program, url), nullptr});
- }
return PluginResult::OK;
}
@@ -2061,9 +2125,14 @@ namespace QuickMedia {
return result_items;
}
- std::unique_ptr<Page> YoutubeVideoPage::create_search_page(Program *program, int &search_delay) {
- search_delay = 350;
- return std::make_unique<YoutubeSearchPage>(program);
+ bool YoutubeVideoPage::create_search_page(Program *program, Tab &tab) {
+ std::string video_id;
+ youtube_url_extract_id(url, video_id);
+
+ tab.body = create_body(false, false);
+ tab.page = std::make_unique<YoutubeSearchPage>(program, std::move(video_id));
+ tab.search_bar = create_search_bar("Search...", 100);
+ return true;
}
std::unique_ptr<Page> YoutubeVideoPage::create_comments_page(Program *program) {
@@ -2095,20 +2164,6 @@ namespace QuickMedia {
return result;
}
- static std::string url_extract_param(const std::string &url, const std::string &param) {
- std::string param_s = param + "=";
- size_t index = url.find(param_s);
- if(index == std::string::npos)
- return "";
-
- index += param_s.size();
- size_t end = url.find('&', index);
- if(end == std::string::npos)
- end = url.size();
-
- return url.substr(index, end - index);
- }
-
static const YoutubeVideoFormat* get_highest_resolution_mp4_non_av1(const std::vector<YoutubeVideoFormat> &video_formats, int max_height) {
for(const YoutubeVideoFormat &video_format : video_formats) {
if(video_format.height <= max_height && video_format.base.mime_type.find("mp4") != std::string::npos && video_format.base.mime_type.find("av01") == std::string::npos)