From ac01d918e5cfda3dd202b641cd38a7f554e5ae52 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 27 Sep 2023 23:46:06 +0200 Subject: Add support for invidious popular feed (new config option) --- example-config.json | 5 +++- include/Config.hpp | 1 + src/Config.cpp | 6 ++++- src/plugins/Youtube.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/example-config.json b/example-config.json index 5c220ec..a802dfa 100644 --- a/example-config.json +++ b/example-config.json @@ -42,7 +42,10 @@ }, "youtube": { // If true, resume playback where you left off in youtube videos - "load_progress": true + "load_progress": true, + // The invidious instance to use. This is currently only used to show a video feed. + // If not set, then local recommendations are used instead. + "invidious_instance": "" }, "matrix": { // List of homeservers to display in the "Room directory" tab diff --git a/include/Config.hpp b/include/Config.hpp index 44cde39..7b77577 100644 --- a/include/Config.hpp +++ b/include/Config.hpp @@ -46,6 +46,7 @@ namespace QuickMedia { struct YoutubeConfig { bool load_progress = true; + std::string invidious_instance; }; struct MatrixConfig { diff --git a/src/Config.cpp b/src/Config.cpp index 474e6a9..54aa21e 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -57,6 +57,8 @@ namespace QuickMedia { std::string result; if(!path.empty() && path[0] == '~') result = get_home_dir().data + path.substr(1); + else if(path.size() >= 5 && strncmp(path.c_str(), "$HOME", 5) == 0) + result = get_home_dir().data + path.substr(5); else result = path; return result; @@ -210,8 +212,10 @@ namespace QuickMedia { } const Json::Value &youtube_json = json_root["youtube"]; - if(youtube_json.isObject()) + if(youtube_json.isObject()) { get_json_value(youtube_json, "load_progress", config->youtube.load_progress); + get_json_value(youtube_json, "invidious_instance", config->youtube.invidious_instance); + } bool has_known_matrix_homeservers_config = false; const Json::Value &matrix_json = json_root["matrix"]; diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index f7cfb09..474d3a2 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -945,14 +945,74 @@ namespace QuickMedia { } } + static SearchResult invidious_get_popular_feed(Page *page, const std::string &invidious_instance, BodyItems &result_items) { + Json::Value json_root; + std::string err_msg; + DownloadResult download_result = page->download_json(json_root, invidious_instance + "/api/v1/popular", {}); + if(download_result != DownloadResult::OK) return download_result_to_search_result(download_result); + + if(!json_root.isArray()) + return SearchResult::ERR; + + for(const Json::Value &item_json : json_root) { + if(!item_json.isObject()) + continue; + + const Json::Value &title_json = item_json["title"]; + const Json::Value &video_id_json = item_json["videoId"]; + const Json::Value &length_seconds_json = item_json["lengthSeconds"]; + const Json::Value &author_json = item_json["author"]; + const Json::Value &published_text_json = item_json["publishedText"]; + const Json::Value &view_count_json = item_json["viewCount"]; + + if(!title_json.isString() || !video_id_json.isString()) + continue; + + std::string video_id_str = video_id_json.asString(); + auto body_item = BodyItem::create(title_json.asString()); + std::string desc; + if(view_count_json.isInt64()) + desc += std::to_string(view_count_json.asInt64()) + " view" + (view_count_json.asInt64() == 1 ? "" : "s"); + if(published_text_json.isString()) { + if(!desc.empty()) + desc += " • "; + desc += published_text_json.asString(); + } + if(length_seconds_json.isInt64()) { + if(!desc.empty()) + desc += '\n'; + desc += seconds_to_duration(length_seconds_json.asInt64()); + } + if(author_json.isString()) { + if(!desc.empty()) + desc += '\n'; + desc += author_json.asString(); + } + body_item->set_description(std::move(desc)); + body_item->set_description_color(get_theme().faded_text_color); + body_item->url = "https://www.youtube.com/watch?v=" + video_id_str; + + body_item->thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/mqdefault.jpg"; + body_item->thumbnail_size = mgl::vec2i(192, 108); + result_items.push_back(std::move(body_item)); + } + + return SearchResult::OK; + } + SearchResult YoutubeSearchPage::search(const std::string &str, BodyItems &result_items) { continuation_token.clear(); current_page = 0; added_videos.clear(); if(str.empty()) { - program->fill_recommended_items_from_json("youtube", program->load_recommended_json("youtube"), result_items); - return SearchResult::OK; + const std::string &invidious_instance = get_config().youtube.invidious_instance; + if(invidious_instance.empty()) { + program->fill_recommended_items_from_json("youtube", program->load_recommended_json("youtube"), result_items); + return SearchResult::OK; + } else { + return invidious_get_popular_feed(this, invidious_instance, result_items); + } } // TODO: Find this search url from youtube.com/... searchbox.js, and the url to that script from youtube.com/ html @@ -1101,7 +1161,7 @@ namespace QuickMedia { continuation_token.clear(); current_page = 0; added_videos.clear(); - program->fill_recommended_items_from_json("youtube", program->load_recommended_json("youtube"), result_items); + search("", result_items); get_cookies(); return PluginResult::OK; } -- cgit v1.2.3