aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/MediaGeneric.cpp2
-rw-r--r--src/plugins/Youtube.cpp135
2 files changed, 89 insertions, 48 deletions
diff --git a/src/plugins/MediaGeneric.cpp b/src/plugins/MediaGeneric.cpp
index c3a8d8e..11b0957 100644
--- a/src/plugins/MediaGeneric.cpp
+++ b/src/plugins/MediaGeneric.cpp
@@ -180,7 +180,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- BodyItems MediaGenericVideoPage::get_related_media(const std::string &url, std::string&) {
+ BodyItems MediaGenericVideoPage::get_related_media(const std::string &url) {
BodyItems result_items;
search_page->get_related_media(url, result_items);
return result_items;
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index 9cec69c..04c29e0 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -13,6 +13,7 @@ extern "C" {
#include <json/writer.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
namespace QuickMedia {
bool youtube_url_extract_id(const std::string &youtube_url, std::string &youtube_video_id) {
@@ -1691,7 +1692,7 @@ namespace QuickMedia {
return "";
}
- BodyItems YoutubeVideoPage::get_related_media(const std::string &url, std::string &channel_url) {
+ BodyItems YoutubeVideoPage::get_related_media(const std::string &url) {
BodyItems result_items;
std::string modified_url = remove_index_from_playlist_url(url);
@@ -1721,56 +1722,12 @@ namespace QuickMedia {
if(!json_item.isObject())
continue;
- const Json::Value &player_response_json = json_item["playerResponse"];
- if(channel_url.empty()) {
- if(player_response_json.isObject()) {
- const Json::Value &video_details_json = player_response_json["videoDetails"];
- if(video_details_json.isObject()) {
- const Json::Value &channel_id_json = video_details_json["channelId"];
- if(channel_id_json.isString())
- channel_url = "https://www.youtube.com/channel/" + channel_id_json.asString();
- }
- }
- }
-
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();
}
- if(player_response_json.isObject()) {
- const Json::Value &playback_tracing_json = player_response_json["playbackTracking"];
- if(playback_tracing_json.isObject()) {
- if(playback_url.empty()) {
- const Json::Value &video_stats_playback_url_json = playback_tracing_json["videostatsPlaybackUrl"];
- if(video_stats_playback_url_json.isObject()) {
- const Json::Value &base_url_json = video_stats_playback_url_json["baseUrl"];
- if(base_url_json.isString())
- playback_url = base_url_json.asString();
- }
- }
-
- if(watchtime_url.empty()) {
- const Json::Value &video_stats_watchtime_url_json = playback_tracing_json["videostatsWatchtimeUrl"];
- if(video_stats_watchtime_url_json.isObject()) {
- const Json::Value &base_url_json = video_stats_watchtime_url_json["baseUrl"];
- if(base_url_json.isString())
- watchtime_url = base_url_json.asString();
- }
- }
-
- if(tracking_url.empty()) {
- const Json::Value &p_tracking_url_json = playback_tracing_json["ptrackingUrl"];
- if(p_tracking_url_json.isObject()) {
- const Json::Value &base_url_json = p_tracking_url_json["baseUrl"];
- if(base_url_json.isString())
- tracking_url = base_url_json.asString();
- }
- }
- }
- }
-
const Json::Value &response_json = json_item["response"];
if(!response_json.isObject())
continue;
@@ -1900,7 +1857,7 @@ namespace QuickMedia {
return audio_formats.front().base.url;
}
- PluginResult YoutubeVideoPage::load() {
+ PluginResult YoutubeVideoPage::load(std::string &channel_url) {
video_formats.clear();
audio_formats.clear();
@@ -1919,7 +1876,7 @@ namespace QuickMedia {
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
std::string response;
- DownloadResult download_result = download_to_string("https://www.youtube.com/get_video_info?html5=1&video_id=" + video_id + "&eurl=https://www.youtube.googleapis.com/v/" + video_id, response, std::move(additional_args), true); // TODO: true?
+ DownloadResult download_result = download_to_string("https://www.youtube.com/get_video_info?html5=1&video_id=" + video_id + "&eurl=https://www.youtube.googleapis.com/v/" + video_id, response, std::move(additional_args), true);
if(download_result != DownloadResult::OK) return download_result_to_plugin_result(download_result);
std::string player_response_param = url_extract_param(response, "player_response");
@@ -1946,6 +1903,43 @@ namespace QuickMedia {
if(video_formats.empty() && audio_formats.empty())
return PluginResult::ERR;
+ const Json::Value &video_details_json = json_root["videoDetails"];
+ if(video_details_json.isObject()) {
+ const Json::Value &channel_id_json = video_details_json["channelId"];
+ if(channel_id_json.isString())
+ channel_url = "https://www.youtube.com/channel/" + channel_id_json.asString();
+ }
+
+ const Json::Value &playback_tracing_json = json_root["playbackTracking"];
+ if(playback_tracing_json.isObject()) {
+ if(playback_url.empty()) {
+ const Json::Value &video_stats_playback_url_json = playback_tracing_json["videostatsPlaybackUrl"];
+ if(video_stats_playback_url_json.isObject()) {
+ const Json::Value &base_url_json = video_stats_playback_url_json["baseUrl"];
+ if(base_url_json.isString())
+ playback_url = base_url_json.asString();
+ }
+ }
+
+ if(watchtime_url.empty()) {
+ const Json::Value &video_stats_watchtime_url_json = playback_tracing_json["videostatsWatchtimeUrl"];
+ if(video_stats_watchtime_url_json.isObject()) {
+ const Json::Value &base_url_json = video_stats_watchtime_url_json["baseUrl"];
+ if(base_url_json.isString())
+ watchtime_url = base_url_json.asString();
+ }
+ }
+
+ if(tracking_url.empty()) {
+ const Json::Value &p_tracking_url_json = playback_tracing_json["ptrackingUrl"];
+ if(p_tracking_url_json.isObject()) {
+ const Json::Value &base_url_json = p_tracking_url_json["baseUrl"];
+ if(base_url_json.isString())
+ tracking_url = base_url_json.asString();
+ }
+ }
+ }
+
std::sort(video_formats.begin(), video_formats.end(), [](const YoutubeVideoFormat &format1, const YoutubeVideoFormat &format2) {
return format1.base.bitrate > format2.base.bitrate;
});
@@ -1957,6 +1951,53 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ static bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) {
+ int fd = open("/dev/urandom", O_RDONLY);
+ if(fd == -1) {
+ perror("/dev/urandom");
+ return false;
+ }
+
+ if(read(fd, buffer, buffer_size) < buffer_size) {
+ fprintf(stderr, "Failed to read %d bytes from /dev/urandom\n", buffer_size);
+ close(fd);
+ return false;
+ }
+
+ for(int i = 0; i < buffer_size; ++i) {
+ unsigned char c = *(unsigned char*)&buffer[i];
+ buffer[i] = alphabet[c % alphabet_size];
+ }
+ close(fd);
+ return true;
+ }
+
+ void YoutubeVideoPage::mark_watched() {
+ if(playback_url.empty()) {
+ fprintf(stderr, "Failed to mark video as watched because playback_url is empty\n");
+ return;
+ }
+
+ std::vector<CommandArg> additional_args = {
+ { "-H", "x-youtube-client-name: 1" },
+ { "-H", "x-youtube-client-version: 2.20200626.03.00" }
+ };
+
+ std::vector<CommandArg> cookies = get_cookies();
+ additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
+
+ std::string cpn;
+ cpn.resize(16);
+ generate_random_characters(cpn.data(), cpn.size(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_", 64);
+
+ std::string response;
+ DownloadResult download_result = download_to_string(playback_url + "&ver=2&cpn=" + cpn, response, std::move(additional_args), true);
+ if(download_result != DownloadResult::OK) {
+ fprintf(stderr, "Failed to mark video as watched because http request failed\n");
+ return;
+ }
+ }
+
static bool parse_cipher_format(const Json::Value &format, YoutubeFormat &youtube_format) {
std::map<std::string, std::string> cipher_params;
const Json::Value &cipher_json = format["cipher"];