aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-06-17 03:28:09 +0200
committerdec05eba <dec05eba@protonmail.com>2021-06-17 03:28:09 +0200
commit4028d87367710a4cd6501314adea58678408351f (patch)
treed3ba5c6291a77a622c723758f92a33c6b239ea71 /src
parent8fdf45fcdc0853f0630640234a0b2612fbb1f0a9 (diff)
Fix related video
Diffstat (limited to 'src')
-rw-r--r--src/plugins/Youtube.cpp274
1 files changed, 138 insertions, 136 deletions
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index 7e71830..ed30aec 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -16,6 +16,65 @@ extern "C" {
#include <fcntl.h>
namespace QuickMedia {
+ static const char *youtube_client_version = "x-youtube-client-version: 2.20210615.01.00";
+ static const std::string key_api_request_data =
+R"END(
+{
+ "context": {
+ "client": {
+ "hl": "en",
+ "gl": "US",
+ "deviceMake": "",
+ "deviceModel": "",
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0,gzip(gfe)",
+ "clientName": "WEB",
+ "clientVersion": "2.20210615.01.00",
+ "osName": "Windows",
+ "osVersion": "10.0",
+ "originalUrl": "https://www.youtube.com/watch?v=%VIDEO_ID%",
+ "platform": "DESKTOP",
+ "clientFormFactor": "UNKNOWN_FORM_FACTOR",
+ "timeZone": "UTC",
+ "browserName": "Firefox",
+ "browserVersion": "78.0",
+ "screenWidthPoints": 1054,
+ "screenHeightPoints": 289,
+ "screenPixelDensity": 1,
+ "screenDensityFloat": 1,
+ "utcOffsetMinutes": 0,
+ "userInterfaceTheme": "USER_INTERFACE_THEME_LIGHT",
+ "clientScreen": "WATCH",
+ "mainAppWebInfo": {
+ "graftUrl": "/watch?v=%VIDEO_ID%",
+ "webDisplayMode": "WEB_DISPLAY_MODE_BROWSER",
+ "isWebNativeShareAvailable": false
+ }
+ },
+ "user": {
+ "lockedSafetyMode": false
+ },
+ "request": {
+ "useSsl": true,
+ "internalExperimentFlags": [],
+ "consistencyTokenJars": []
+ }
+ },
+ "videoId": "%VIDEO_ID%",
+ "playbackContext": {
+ "contentPlaybackContext": {
+ "currentUrl": "/watch?v=%VIDEO_ID%",
+ "vis": 0,
+ "splay": false,
+ "autoCaptionsDefaultOn": false,
+ "autonavState": "STATE_NONE",
+ "html5Preference": "HTML5_PREF_WANTS"
+ }
+ },
+ "racyCheckOk": false,
+ "contentCheckOk": false
+}
+)END";
+
bool youtube_url_extract_id(const std::string &youtube_url, std::string &youtube_video_id) {
size_t index = youtube_url.find("youtube.com/watch?v=");
if(index != std::string::npos) {
@@ -626,7 +685,7 @@ namespace QuickMedia {
std::vector<CommandArg> additional_args = {
{ "-H", "x-spf-referer: " + search_url },
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
+ { "-H", youtube_client_version },
{ "-H", "referer: " + search_url }
};
@@ -634,7 +693,7 @@ namespace QuickMedia {
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
- DownloadResult result = download_json(json_root, search_url + "&pbj=1", std::move(additional_args), true);
+ 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(!json_root.isArray())
@@ -694,13 +753,13 @@ namespace QuickMedia {
}
PluginResult YoutubeSearchPage::search_get_continuation(const std::string &url, const std::string &current_continuation_token, BodyItems &result_items) {
- std::string next_url = url + "&pbj=1&ctoken=" + current_continuation_token;
+ std::string next_url = url + "&pbj=1&gl=US&hl=en&ctoken=" + current_continuation_token;
std::vector<CommandArg> additional_args = {
{ "-H", "x-spf-referer: " + url },
{ "-H", "x-youtube-client-name: 1" },
{ "-H", "x-spf-previous: " + url },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
+ { "-H", youtube_client_version },
{ "-H", "referer: " + url }
};
@@ -893,7 +952,7 @@ namespace QuickMedia {
std::vector<CommandArg> additional_args = {
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20210308.08.00" },
+ { "-H", youtube_client_version },
{ "-F", "session_token=" + xsrf_token }
};
@@ -975,7 +1034,7 @@ namespace QuickMedia {
std::vector<CommandArg> additional_args = {
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20210308.08.00" },
+ { "-H", youtube_client_version },
{ "-F", "session_token=" + xsrf_token }
};
@@ -1081,7 +1140,7 @@ namespace QuickMedia {
{ "-H", "x-origin: https://www.youtube.com" },
{ "-H", "content-type: application/json" },
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
+ { "-H", youtube_client_version },
{ "-H", "referer: " + url + "/videos" },
{ "--data-raw", Json::writeString(json_builder, request_json) }
};
@@ -1164,7 +1223,7 @@ namespace QuickMedia {
{ "-H", "x-origin: https://www.youtube.com" },
{ "-H", "content-type: application/json" },
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
+ { "-H", youtube_client_version },
{ "-H", "referer: " + url + "/videos" },
{ "--data-raw", Json::writeString(json_builder, request_json) }
};
@@ -1232,7 +1291,7 @@ namespace QuickMedia {
{ "-H", "x-spf-referer: " + url },
{ "-H", "x-youtube-client-name: 1" },
{ "-H", "x-spf-previous: " + url },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
+ { "-H", youtube_client_version },
{ "-H", "referer: " + url }
};
@@ -1240,7 +1299,7 @@ namespace QuickMedia {
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
- DownloadResult result = download_json(json_root, url + "/videos?pbj=1", std::move(additional_args), true);
+ 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);
return PluginResult::OK;
@@ -1486,13 +1545,13 @@ namespace QuickMedia {
}
PluginResult YoutubeRecommendedPage::search_get_continuation(const std::string &current_continuation_token, BodyItems &result_items) {
- std::string next_url = "https://www.youtube.com/?pbj=1&ctoken=" + current_continuation_token;
+ std::string next_url = "https://www.youtube.com/?pbj=1&gl=US&hl=en&ctoken=" + current_continuation_token;
std::vector<CommandArg> additional_args = {
{ "-H", "x-spf-referer: https://www.youtube.com/" },
{ "-H", "x-youtube-client-name: 1" },
{ "-H", "x-spf-previous: https://www.youtube.com/" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
+ { "-H", youtube_client_version },
{ "-H", "referer: https://www.youtube.com/" }
};
@@ -1575,14 +1634,14 @@ namespace QuickMedia {
std::vector<CommandArg> additional_args = {
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" }
+ { "-H", youtube_client_version }
};
std::vector<CommandArg> cookies = get_cookies();
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
- DownloadResult result = download_json(json_root, "https://www.youtube.com/?pbj=1", std::move(additional_args), true);
+ DownloadResult result = download_json(json_root, "https://www.youtube.com/?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.isArray())
@@ -1695,90 +1754,84 @@ namespace QuickMedia {
BodyItems YoutubeVideoPage::get_related_media(const std::string &url) {
BodyItems result_items;
- std::string modified_url = remove_index_from_playlist_url(url);
+ 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::string request_data = key_api_request_data;
+ string_replace_all(request_data, "%VIDEO_ID%", video_id);
+
std::vector<CommandArg> additional_args = {
- { "-H", "x-spf-referer: " + url },
+ { "-H", "Content-Type: application/json" },
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-spf-previous: " + url },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" },
- { "-H", "referer: " + url }
+ { "-H", youtube_client_version },
+ { "--data-raw", std::move(request_data) }
};
std::vector<CommandArg> cookies = get_cookies();
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
- DownloadResult result = download_json(json_root, modified_url + "&pbj=1", std::move(additional_args), true);
- if(result != DownloadResult::OK) return result_items;
+ DownloadResult download_result = download_json(json_root, "https://www.youtube.com/youtubei/v1/next?key=" + api_key, additional_args, true);
+ if(download_result != DownloadResult::OK) return result_items;
- if(!json_root.isArray())
+ if(!json_root.isObject())
return result_items;
std::unordered_set<std::string> added_videos;
xsrf_token.clear();
comments_continuation_token.clear();
- for(const Json::Value &json_item : json_root) {
- if(!json_item.isObject())
- continue;
+ // TODO: Find xsrf_token somehow. Maybe use /embed/ endpoint to find it? xsrf_token is needed for comments
- if(xsrf_token.empty()) {
- const Json::Value &xsrf_token_json = json_item["xsrf_token"];
- if(xsrf_token_json.isString())
- xsrf_token = xsrf_token_json.asString();
- }
+ const Json::Value &contents_json = json_root["contents"];
+ if(!contents_json.isObject())
+ return result_items;
- const Json::Value &response_json = json_item["response"];
- if(!response_json.isObject())
- continue;
+ const Json::Value &tcwnr_json = contents_json["twoColumnWatchNextResults"];
+ if(!tcwnr_json.isObject())
+ return result_items;
- const Json::Value &contents_json = response_json["contents"];
- if(!contents_json.isObject())
- return result_items;
+ if(comments_continuation_token.empty())
+ comments_continuation_token = two_column_watch_next_results_get_comments_continuation_token(tcwnr_json);
- const Json::Value &tcwnr_json = contents_json["twoColumnWatchNextResults"];
- if(!tcwnr_json.isObject())
- return result_items;
+ const Json::Value &secondary_results_json = tcwnr_json["secondaryResults"];
+ if(!secondary_results_json.isObject())
+ return result_items;
- if(comments_continuation_token.empty())
- comments_continuation_token = two_column_watch_next_results_get_comments_continuation_token(tcwnr_json);
+ const Json::Value &secondary_results2_json = secondary_results_json["secondaryResults"];
+ if(!secondary_results2_json.isObject())
+ return result_items;
+
+ const Json::Value &results_json = secondary_results2_json["results"];
+ if(!results_json.isArray())
+ return result_items;
- const Json::Value &secondary_results_json = tcwnr_json["secondaryResults"];
- if(!secondary_results_json.isObject())
- return result_items;
+ for(const Json::Value &item_json : results_json) {
+ if(!item_json.isObject())
+ continue;
- const Json::Value &secondary_results2_json = secondary_results_json["secondaryResults"];
- if(!secondary_results2_json.isObject())
- return result_items;
+ auto body_item = parse_compact_video_renderer_json(item_json, added_videos);
+ if(body_item)
+ result_items.push_back(std::move(body_item));
- const Json::Value &results_json = secondary_results2_json["results"];
- if(!results_json.isArray())
- return result_items;
-
- for(const Json::Value &item_json : results_json) {
- if(!item_json.isObject())
+ const Json::Value &compact_autoplay_renderer_json = item_json["compactAutoplayRenderer"];
+ if(!compact_autoplay_renderer_json.isObject())
+ continue;
+
+ const Json::Value &item_contents_json = compact_autoplay_renderer_json["contents"];
+ if(!item_contents_json.isArray())
+ continue;
+
+ for(const Json::Value &content_item_json : item_contents_json) {
+ if(!content_item_json.isObject())
continue;
-
- auto body_item = parse_compact_video_renderer_json(item_json, added_videos);
+
+ auto body_item = parse_compact_video_renderer_json(content_item_json, added_videos);
if(body_item)
result_items.push_back(std::move(body_item));
-
- const Json::Value &compact_autoplay_renderer_json = item_json["compactAutoplayRenderer"];
- if(!compact_autoplay_renderer_json.isObject())
- continue;
-
- const Json::Value &item_contents_json = compact_autoplay_renderer_json["contents"];
- if(!item_contents_json.isArray())
- continue;
-
- for(const Json::Value &content_item_json : item_contents_json) {
- if(!content_item_json.isObject())
- continue;
-
- auto body_item = parse_compact_video_renderer_json(content_item_json, added_videos);
- if(body_item)
- result_items.push_back(std::move(body_item));
- }
}
}
@@ -1903,76 +1956,20 @@ namespace QuickMedia {
return PluginResult::ERR;
}
+ std::string request_data = key_api_request_data;
+ string_replace_all(request_data, "%VIDEO_ID%", video_id);
+
std::vector<CommandArg> additional_args = {
+ { "-H", "Content-Type: application/json" },
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" }
+ { "-H", youtube_client_version },
+ { "--data-raw", std::move(request_data) }
};
std::vector<CommandArg> cookies = get_cookies();
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
- std::string request_data =
-R"END(
-{
- "context": {
- "client": {
- "hl": "en",
- "gl": "US",
- "deviceMake": "",
- "deviceModel": "",
- "userAgent": "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0,gzip(gfe)",
- "clientName": "WEB",
- "clientVersion": "2.20210615.01.00",
- "osName": "Windows",
- "osVersion": "10.0",
- "originalUrl": "https://www.youtube.com/watch?v=%VIDEO_ID%",
- "platform": "DESKTOP",
- "clientFormFactor": "UNKNOWN_FORM_FACTOR",
- "timeZone": "UTC",
- "browserName": "Firefox",
- "browserVersion": "78.0",
- "screenWidthPoints": 1054,
- "screenHeightPoints": 289,
- "screenPixelDensity": 1,
- "screenDensityFloat": 1,
- "utcOffsetMinutes": 0,
- "userInterfaceTheme": "USER_INTERFACE_THEME_LIGHT",
- "clientScreen": "WATCH",
- "mainAppWebInfo": {
- "graftUrl": "/watch?v=%VIDEO_ID%",
- "webDisplayMode": "WEB_DISPLAY_MODE_BROWSER",
- "isWebNativeShareAvailable": false
- }
- },
- "user": {
- "lockedSafetyMode": false
- },
- "request": {
- "useSsl": true,
- "internalExperimentFlags": [],
- "consistencyTokenJars": []
- }
- },
- "videoId": "%VIDEO_ID%",
- "playbackContext": {
- "contentPlaybackContext": {
- "currentUrl": "/watch?v=%VIDEO_ID%",
- "vis": 0,
- "splay": false,
- "autoCaptionsDefaultOn": false,
- "autonavState": "STATE_NONE",
- "html5Preference": "HTML5_PREF_WANTS"
- }
- },
- "racyCheckOk": false,
- "contentCheckOk": false
-}
-)END";
- string_replace_all(request_data, "%VIDEO_ID%", video_id);
- additional_args.push_back({ "-H", "Content-Type: application/json" });
- additional_args.push_back({ "--data-raw", std::move(request_data) });
-
DownloadResult download_result = download_json(json_root, "https://www.youtube.com/youtubei/v1/player?key=" + api_key, additional_args, true);
if(download_result != DownloadResult::OK) return download_result_to_plugin_result(download_result);
@@ -2076,7 +2073,7 @@ R"END(
std::vector<CommandArg> additional_args = {
{ "-H", "x-youtube-client-name: 1" },
- { "-H", "x-youtube-client-version: 2.20200626.03.00" }
+ { "-H", youtube_client_version }
};
std::vector<CommandArg> cookies = get_cookies();
@@ -2109,7 +2106,12 @@ R"END(
if(cipher_params.empty() || url.empty())
return false;
+ std::string cpn;
+ cpn.resize(16);
+ generate_random_characters(cpn.data(), cpn.size(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_", 64);
+
std::string url_decoded = url_param_decode(url);
+ url_decoded += "&alr=yes&cver=2.20210615.01.00&altitags=395,394&cpn=" + cpn;
const std::string &s = cipher_params["s"];
const std::string &sp = cipher_params["sp"];
@@ -2210,6 +2212,6 @@ R"END(
parse_format(formats_json, false);
const Json::Value &adaptive_formats_json = streaming_data_json["adaptiveFormats"];
- parse_format(adaptive_formats_json, false);
+ parse_format(adaptive_formats_json, true);
}
} \ No newline at end of file