aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--src/QuickMedia.cpp15
-rw-r--r--src/plugins/Youtube.cpp79
-rw-r--r--src/plugins/youtube/Signature.cpp33
4 files changed, 85 insertions, 45 deletions
diff --git a/TODO b/TODO
index 7f3440a..6af2d71 100644
--- a/TODO
+++ b/TODO
@@ -168,4 +168,5 @@ Load the next page in chapter list when reaching the bottom (when going to previ
Loading image background should be rounded.
//Workaround mpv issue where video is frozen after seeking (with and without cache enabled, but more often with cache enabled). This happens because of audio. Reloading audio fixes this but audio will then be gone.
Fix youtube comments not working because of youtube update. Missing xsrf_token.
-Fix youtube copyrighted videos not working. Youtube triggers recaptcha for request to watch?v, but signature code is also broken? \ No newline at end of file
+Fix youtube copyrighted videos not working. Youtube triggers recaptcha for request to watch?v, but signature code is also broken?
+Better deal with reading from file errors. This could happen when reading a file while its being modified. See read_file_as_json. \ No newline at end of file
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 82741bb..77098a3 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1360,8 +1360,15 @@ namespace QuickMedia {
Json::Value Program::load_video_history_json() {
Path video_history_filepath = get_video_history_filepath(plugin_name);
Json::Value json_result;
- if(!read_file_as_json(video_history_filepath, json_result) || !json_result.isArray())
+ FileType file_type = get_file_type(video_history_filepath);
+ if(file_type == FileType::REGULAR) {
+ if(!read_file_as_json(video_history_filepath, json_result) || !json_result.isArray()) {
+ show_notification("QuickMedia", "Failed to read " + video_history_filepath.data, Urgency::CRITICAL);
+ abort();
+ }
+ } else {
json_result = Json::Value(Json::arrayValue);
+ }
return json_result;
}
@@ -1473,6 +1480,10 @@ namespace QuickMedia {
FileType file_type = get_file_type(content_storage_file);
if(file_type == FileType::REGULAR) {
result = read_file_as_json(content_storage_file, content_storage_json) && content_storage_json.isObject();
+ if(!result) {
+ show_notification("QuickMedia", "Failed to read " + content_storage_file.data, Urgency::CRITICAL);
+ abort();
+ }
} else {
result = true;
}
@@ -2916,9 +2927,9 @@ namespace QuickMedia {
load_video_error_check();
} else if(update_err != VideoPlayer::Error::OK) {
+ ++load_try;
if(load_try < num_load_tries_max) {
fprintf(stderr, "Failed to play the media, retrying (try %d out of %d)\n", 1 + load_try, num_load_tries_max);
- ++load_try;
load_video_error_check(prev_start_time);
} else {
show_notification("QuickMedia", "Failed to play the video (error code " + std::to_string((int)update_err) + ")", Urgency::CRITICAL);
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index ed30aec..d9d9239 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -472,11 +472,37 @@ R"END(
return "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
}
+ static std::string cpn;
+
+ 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;
+ }
+
static std::vector<CommandArg> get_cookies() {
std::lock_guard<std::mutex> lock(cookies_mutex);
if(cookies_filepath.empty()) {
YoutubeSignatureDecryptor::get_instance();
+ cpn.resize(16);
+ generate_random_characters(cpn.data(), cpn.size(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_", 64);
+
Path cookies_filepath_p;
if(get_cookies_filepath(cookies_filepath_p, "youtube") != 0) {
show_notification("QuickMedia", "Failed to create youtube cookies file", Urgency::CRITICAL);
@@ -1331,14 +1357,20 @@ R"END(
std::unordered_set<std::string> channel_ids;
std::string subscriptions_str;
- if(file_get_content(subscriptions_path, subscriptions_str) == 0) {
- string_split(subscriptions_str, '\n', [&channel_ids](const char *str, size_t size) {
- std::string line(str, size);
- line = strip(line);
- if(!line.empty())
- channel_ids.insert(std::move(line));
- return true;
- });
+ FileType file_type = get_file_type(subscriptions_path);
+ if(file_type == FileType::REGULAR) {
+ if(file_get_content(subscriptions_path, subscriptions_str) == 0) {
+ string_split(subscriptions_str, '\n', [&channel_ids](const char *str, size_t size) {
+ std::string line(str, size);
+ line = strip(line);
+ if(!line.empty())
+ channel_ids.insert(std::move(line));
+ return true;
+ });
+ } else {
+ show_notification("QuickMedia", "Failed to read " + subscriptions_path.data, Urgency::CRITICAL);
+ abort();
+ }
}
auto it = channel_ids.find(channel_id);
@@ -2044,27 +2076,6 @@ R"END(
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");
@@ -2079,10 +2090,6 @@ R"END(
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) {
@@ -2106,12 +2113,8 @@ 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;
+ url_decoded += "&alr=yes&cver=2.20210615.01.00&cpn=" + cpn;
const std::string &s = cipher_params["s"];
const std::string &sp = cipher_params["sp"];
diff --git a/src/plugins/youtube/Signature.cpp b/src/plugins/youtube/Signature.cpp
index 8c87817..7631182 100644
--- a/src/plugins/youtube/Signature.cpp
+++ b/src/plugins/youtube/Signature.cpp
@@ -4,6 +4,7 @@
#include "../../../include/DownloadUtils.hpp"
#include "../../../include/StringUtils.hpp"
#include "../../../include/Program.hpp"
+#include "../../../include/NetUtils.hpp"
#include <regex>
#include <mutex>
#include <unistd.h>
@@ -18,6 +19,26 @@ namespace QuickMedia {
static std::mutex update_signature_mutex;
static const int timeout_default_sec = 60 * 5; // 5 minutes
+ static bool is_whitespace(char c) {
+ switch(c) {
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ return true;
+ }
+ return false;
+ }
+
+ static std::string remove_whitespaces(const std::string &str) {
+ std::string result;
+ for(char c : str) {
+ if(!is_whitespace(c))
+ result += c;
+ }
+ return result;
+ }
+
bool YoutubeSignatureDecryptor::js_code_to_operations(const std::string &function_body_str, const std::string &var_body_str, std::vector<DecryptFuncCall> &new_func_calls, std::map<std::string, DecryptFunction> &new_func_decls) {
std::vector<std::string> function_body;
string_split(function_body_str, ';', [&function_body](const char *str, size_t size) {
@@ -77,7 +98,7 @@ namespace QuickMedia {
std::string func_name = func_decl_str.substr(0, func_name_split_index);
func_name = strip(func_name);
std::string function_body = func_decl_str.substr(func_start_index + 1);
- function_body = strip(function_body);
+ function_body = strip(remove_whitespaces(function_body));
if(!function_body.empty() && function_body.back() == '}')
function_body.pop_back();
@@ -86,8 +107,12 @@ namespace QuickMedia {
decrypt_function = DecryptFunction::REVERSE;
else if(function_body == "a.splice(0,b)")
decrypt_function = DecryptFunction::SPLICE;
- else
+ else if(function_body.find("a[0]=a[b%a.length]") != std::string::npos)
decrypt_function = DecryptFunction::SWAP;
+ else {
+ fprintf(stderr, "Unexpected decryption function body: |%s|\n", function_body.c_str());
+ return false;
+ }
//fprintf(stderr, "declared function: %s, body: |%s|\n", func_name.c_str(), function_body.c_str());
new_func_decls[std::move(func_name)] = decrypt_function;
}
@@ -216,7 +241,7 @@ namespace QuickMedia {
break;
}
case DecryptFunction::SWAP: {
- if(sig.empty()) {
+ if(sig.empty() || func_call.arg < 0) {
fprintf(stderr, "YoutubeSignatureDecryptor: sig unexpectedly empty in swap\n");
} else {
char c = sig[0];
@@ -229,7 +254,7 @@ namespace QuickMedia {
}
sig_key = sp;
- sig_value = sig;
+ sig_value = url_param_encode(sig);
return true;
}