From 9c1d43e772efb8f5af4b7ef5562fb433c8985697 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 29 Oct 2022 21:33:18 +0200 Subject: Youtube: allow opening youtube channels directly from url. Same with ctrl+i info menu --- src/plugins/Youtube.cpp | 118 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 36 deletions(-) (limited to 'src/plugins/Youtube.cpp') diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 4dbb6c4..f2fb36e 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -91,6 +91,54 @@ namespace QuickMedia { return false; } + bool youtube_url_extract_channel_id(const std::string &youtube_url, std::string &channel_id, std::string &channel_url) { + size_t index = youtube_url.find("youtube.com/c/"); + if(index != std::string::npos) { + index += 14; + size_t end_index = youtube_url.find("/", index); + if(end_index == std::string::npos) + end_index = youtube_url.size(); + channel_id = youtube_url.substr(index, end_index - index); + channel_url = "https://www.youtube.com/c/" + channel_id; + return true; + } + + index = youtube_url.find("youtu.be/c/"); + if(index != std::string::npos) { + index += 11; + size_t end_index = youtube_url.find("/", index); + if(end_index == std::string::npos) + end_index = youtube_url.size(); + channel_id = youtube_url.substr(index, end_index - index); + channel_url = "https://www.youtube.com/c/" + channel_id; + return true; + } + + index = youtube_url.find("youtube.com/channel/"); + if(index != std::string::npos) { + index += 20; + size_t end_index = youtube_url.find("/", index); + if(end_index == std::string::npos) + end_index = youtube_url.size(); + channel_id = youtube_url.substr(index, end_index - index); + channel_url = "https://www.youtube.com/channel/" + channel_id; + return true; + } + + index = youtube_url.find("youtu.be/channel/"); + if(index != std::string::npos) { + index += 17; + size_t end_index = youtube_url.find("/", index); + if(end_index == std::string::npos) + end_index = youtube_url.size(); + channel_id = youtube_url.substr(index, end_index - index); + channel_url = "https://www.youtube.com/channel/" + channel_id; + return true; + } + + return false; + } + static std::mutex cookies_mutex; static std::string cookies_filepath; static std::string api_key; @@ -687,10 +735,20 @@ namespace QuickMedia { return ""; } - static void 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, std::string &browse_id, BodyItems &body_items) { if(!json_root.isObject()) return; + const Json::Value &endpoint_json = json_root["endpoint"]; + if(endpoint_json.isObject()) { + const Json::Value &browse_endpoint_json = endpoint_json["browseEndpoint"]; + if(browse_endpoint_json.isObject()) { + const Json::Value &browse_id_json = browse_endpoint_json["browseId"]; + if(browse_id_json.isString()) + browse_id = browse_id_json.asString(); + } + } + const Json::Value *response_json = &json_root["response"]; if(!response_json->isObject()) response_json = &json_root; @@ -1415,19 +1473,6 @@ namespace QuickMedia { return fetch_comments(this, video_url, continuation_token, result_items); } - static std::string channel_url_extract_id(const std::string &channel_url) { - size_t index = channel_url.find("channel/"); - if(index == std::string::npos) - return ""; - - index += 8; - size_t end_index = channel_url.find('/', index); - if(end_index == std::string::npos) - return channel_url.substr(index); - - return channel_url.substr(index, end_index - index); - } - SearchResult YoutubeChannelPage::search(const std::string &str, BodyItems &result_items) { added_videos.clear(); continuation_token.clear(); @@ -1435,6 +1480,13 @@ namespace QuickMedia { if(str.empty()) return plugin_result_to_search_result(lazy_fetch(result_items)); + std::string channel_id; + std::string channel_url; + if(!youtube_url_extract_channel_id(url, channel_id, channel_url)) { + fprintf(stderr, "Error: failed to extract youtube channel id from url: %s\n", url.c_str()); + return SearchResult::ERR; + } + std::vector cookies = get_cookies(); std::string next_url = "https://www.youtube.com/youtubei/v1/browse?key=" + url_param_encode(api_key) + "&gl=US&hl=en&prettyPrint=false"; @@ -1453,7 +1505,7 @@ namespace QuickMedia { client_json["originalUrl"] = url + "/videos"; context_json["client"] = std::move(client_json); request_json["context"] = std::move(context_json); - request_json["browseId"] = channel_url_extract_id(url); + request_json["browseId"] = channel_id; request_json["query"] = str; request_json["params"] = "EgZzZWFyY2g%3D"; //request_json["continuation"] = current_continuation_token; @@ -1668,35 +1720,29 @@ namespace QuickMedia { 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); + std::string browse_id; if(json_root.isObject()) { - search_page_submit_suggestion_handler(json_root, continuation_token, result_items, added_videos); - parse_channel_videos(json_root, continuation_token, added_videos, result_items); - return PluginResult::OK; - } - - if(!json_root.isArray()) + //search_page_submit_suggestion_handler(json_root, continuation_token, result_items, added_videos); + parse_channel_videos(json_root, continuation_token, added_videos, browse_id, result_items); + } else if(json_root.isArray()) { + for(const Json::Value &json_item : json_root) { + //search_page_submit_suggestion_handler(json_root, continuation_token, result_items, added_videos); + parse_channel_videos(json_item, continuation_token, added_videos, browse_id, result_items); + } + } else { return PluginResult::ERR; - - for(const Json::Value &json_item : json_root) { - search_page_submit_suggestion_handler(json_root, continuation_token, result_items, added_videos); - parse_channel_videos(json_item, continuation_token, added_videos, result_items); } + + if(!browse_id.empty()) + url = "https://www.youtube.com/channel/" + std::move(browse_id); return PluginResult::OK; } TrackResult YoutubeChannelPage::track(const std::string&) { - size_t channel_id_start = url.find("/channel/"); - if(channel_id_start == std::string::npos) { - show_notification("QuickMedia", "Unable to get channel id from " + url, Urgency::CRITICAL); - return TrackResult::ERR; - } - - channel_id_start += 9; - size_t channel_id_end = url.find('/', channel_id_start); - if(channel_id_end == std::string::npos) channel_id_end = url.size(); - std::string channel_id = url.substr(channel_id_start, channel_id_end - channel_id_start); - if(channel_id.empty()) { + std::string channel_id; + std::string channel_url; + if(!youtube_url_extract_channel_id(url, channel_id, channel_url)) { show_notification("QuickMedia", "Unable to get channel id from " + url, Urgency::CRITICAL); return TrackResult::ERR; } -- cgit v1.2.3