aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/youtube/Signature.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/youtube/Signature.cpp')
-rw-r--r--src/plugins/youtube/Signature.cpp316
1 files changed, 0 insertions, 316 deletions
diff --git a/src/plugins/youtube/Signature.cpp b/src/plugins/youtube/Signature.cpp
deleted file mode 100644
index 91796d0..0000000
--- a/src/plugins/youtube/Signature.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-#ifdef YOUTUBE_SIGNATURE_DECRYPTOR
-#include "../../../plugins/youtube/Signature.hpp"
-#include "../../../include/Storage.hpp"
-#include "../../../include/Notification.hpp"
-#include "../../../include/DownloadUtils.hpp"
-#include "../../../include/StringUtils.hpp"
-#include "../../../include/Program.hpp"
-#include <regex>
-#include <mutex>
-#include <unistd.h>
-
-namespace QuickMedia {
- enum UpdateDecryptFunctionError {
- U_DEC_FUN_NET_ERR = 1,
- U_DEC_FUN_FAILED_MATCH_ERR = 2
- };
-
- static YoutubeSignatureDecryptor *instance = nullptr;
- static std::mutex update_signature_mutex;
- static const int timeout_default_sec = 60 * 10; // 10 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) {
- function_body.push_back(std::string(str, size));
- return true;
- });
-
- if(function_body.empty())
- return false;
-
- std::vector<std::string> var_body;
- string_split(var_body_str, "},", [&var_body](const char *str, size_t size) {
- var_body.push_back(std::string(str, size));
- return true;
- });
-
- if(var_body.empty())
- return false;
-
- //fprintf(stderr, "function body: %s\n", function_body_str.c_str());
- for(const std::string &func_call_str : function_body) {
- const size_t var_name_split_index = func_call_str.find('.');
- if(var_name_split_index == std::string::npos) return false;
-
- const size_t func_name_end_index = func_call_str.find('(', var_name_split_index + 1);
- if(func_name_end_index == std::string::npos) return false;
-
- const size_t values_index = func_call_str.find(',', func_name_end_index + 1);
- if(values_index == std::string::npos) return false;
-
- const size_t values_end_index = func_call_str.find(')', values_index + 1);
- if(values_end_index == std::string::npos) return false;
-
- std::string func_name = func_call_str.substr(var_name_split_index + 1, func_name_end_index - (var_name_split_index + 1));
- func_name = strip(func_name);
- std::string value_args = func_call_str.substr(values_index + 1, values_end_index - (values_index + 1));
-
- errno = 0;
- char *endptr;
- const int64_t value_int = strtoll(value_args.c_str(), &endptr, 10);
- if(endptr != value_args.c_str() && errno == 0)
- new_func_calls.push_back({ std::move(func_name), value_int });
- else
- return false;
-
- //fprintf(stderr, "func_call: %s, value: %ld\n", new_func_calls.back().func_name.c_str(), value_int);
- }
-
- //fprintf(stderr, "declaration body: %s\n", var_body_str.c_str());
- for(const std::string &func_decl_str : var_body) {
- const size_t func_name_split_index = func_decl_str.find(':');
- if(func_name_split_index == std::string::npos) return false;
-
- const size_t func_start_index = func_decl_str.find('{', func_name_split_index + 1);
- if(func_start_index == std::string::npos) return false;
-
- 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(remove_whitespaces(function_body));
- if(!function_body.empty() && function_body.back() == '}')
- function_body.pop_back();
-
- DecryptFunction decrypt_function;
- if(function_body == "a.reverse()")
- decrypt_function = DecryptFunction::REVERSE;
- else if(function_body == "a.splice(0,b)")
- decrypt_function = DecryptFunction::SPLICE;
- 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;
- }
-
- for(const auto &func_call : new_func_calls) {
- if(new_func_decls.find(func_call.func_name) == new_func_decls.end()) {
- fprintf(stderr, "YoutubeSignatureDecryptor: declaration for decryption function %s not found\n", func_call.func_name.c_str());
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 10 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return false;
- }
- }
-
- return true;
- }
-
- YoutubeSignatureDecryptor::YoutubeSignatureDecryptor() {
- {
- Path youtube_cache_dir = get_cache_dir().join("youtube");
- if(create_directory_recursive(youtube_cache_dir) != 0) {
- show_notification("QuickMedia", "Failed to create youtube cache directory", Urgency::CRITICAL);
- return;
- }
-
- youtube_cache_dir.join("decryption_function");
- if(file_get_content(youtube_cache_dir, decryption_function) == 0) {
- file_get_last_modified_time_seconds(youtube_cache_dir.data.c_str(), &decrypt_function_last_updated);
-
- size_t newline_index = decryption_function.find('\n');
- if(newline_index != std::string::npos) {
- std::string function_body_str = decryption_function.substr(0, newline_index);
- std::string var_body_str = decryption_function.substr(newline_index + 1);
-
- std::vector<DecryptFuncCall> new_func_calls;
- std::map<std::string, DecryptFunction> new_func_decls;
- if(js_code_to_operations(function_body_str, var_body_str, new_func_calls, new_func_decls)) {
- func_calls = std::move(new_func_calls);
- func_decls = std::move(new_func_decls);
-
- time_t time_now = time(nullptr);
- if(time_now - decrypt_function_last_updated <= timeout_default_sec)
- up_to_date = true;
- }
- }
- }
- }
-
- if(up_to_date)
- return;
-
- running = true;
- poll_task = AsyncTask<void>([this]() mutable {
- int update_res = update_decrypt_function();
- if(update_res != 0 && !program_is_dead_in_current_thread()) {
- if(update_res == U_DEC_FUN_NET_ERR) {
- show_notification("QuickMedia", "Failed to decrypt youtube signature. Is your internet down?", Urgency::CRITICAL);
- } else if(update_res == U_DEC_FUN_FAILED_MATCH_ERR) {
- show_notification("QuickMedia", "Failed to decrypt youtube signature. Make sure you are running the latest version of QuickMedia", Urgency::CRITICAL);
- }
- }
- running = false;
- });
- }
-
- // static
- YoutubeSignatureDecryptor& YoutubeSignatureDecryptor::get_instance() {
- std::lock_guard<std::mutex> lock(update_signature_mutex);
- if(!instance)
- instance = new YoutubeSignatureDecryptor();
- return *instance;
- }
-
- bool YoutubeSignatureDecryptor::decrypt(const std::string &s, const std::string &sp, std::string &sig_key, std::string &sig_value) {
- if(s.empty() || sp.empty())
- return false;
-
- if(!up_to_date) {
- int num_tries = 0;
- const int max_tries = 30;
- while(running && num_tries < max_tries && !program_is_dead_in_current_thread()) { // 6 seconds in total
- if(up_to_date)
- break;
- ++num_tries;
- usleep(200 * 1000); // 200 milliseconds
- }
-
- if(num_tries == max_tries) {
- show_notification("QuickMedia", "Failed to get decryption function for youtube. Make sure your internet is working and that you are running the latest version of QuickMedia", Urgency::CRITICAL);
- return false;
- }
- }
-
- std::lock_guard<std::mutex> lock(update_signature_mutex);
- std::string sig = s;
- for(const auto &func_call : func_calls) {
- auto func_decl_it = func_decls.find(func_call.func_name);
- assert(func_decl_it != func_decls.end());
-
- switch(func_decl_it->second) {
- case DecryptFunction::REVERSE: {
- std::reverse(sig.begin(), sig.end());
- break;
- }
- case DecryptFunction::SPLICE: {
- long int erase_index = func_call.arg;
- if(erase_index > 0 && (size_t)erase_index < sig.size())
- sig.erase(0, erase_index);
- break;
- }
- case DecryptFunction::SWAP: {
- if(sig.empty() || func_call.arg < 0) {
- fprintf(stderr, "YoutubeSignatureDecryptor: sig unexpectedly empty in swap\n");
- } else {
- char c = sig[0];
- sig[0] = sig[func_call.arg % sig.size()];
- sig[func_call.arg % sig.size()] = c;
- }
- break;
- }
- }
- }
-
- sig_key = sp;
- sig_value = sig;//url_param_encode(sig);
- return true;
- }
-
- int YoutubeSignatureDecryptor::update_decrypt_function() {
- std::string response;
- DownloadResult download_result = download_to_string("https://www.youtube.com/watch?v=jNQXAC9IVRw&gl=US&hl=en", response, {}, true);
- if(download_result != DownloadResult::OK) {
- fprintf(stderr, "YoutubeSignatureDecryptor::update_decrypt_function failed. Failed to get youtube page\n");
- return U_DEC_FUN_NET_ERR;
- }
-
- std::smatch base_js_match;
- if(!std::regex_search(response, base_js_match, std::regex(R"END((\/s\/player\/[^\/]+\/player_ias[^\/]+\/en_US\/base\.js))END", std::regex::ECMAScript)) || base_js_match.size() != 2) {
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 1 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return U_DEC_FUN_FAILED_MATCH_ERR;
- }
-
- const std::string &url = base_js_match[1].str();
- download_result = download_to_string("https://www.youtube.com" + url, response, {}, true);
- if(download_result != DownloadResult::OK) {
- fprintf(stderr, "YoutubeSignatureDecryptor::update_decrypt_function failed. Failed to get https://www.youtube.com%s\n", url.c_str());
- return U_DEC_FUN_NET_ERR;
- }
-
- std::smatch function_match;
- if(!std::regex_search(response, function_match, std::regex(R"END((^|\n)\w+=function\(\w\)\{\w=\w\.split\(""\);([^\}]*)\})END", std::regex::ECMAScript)) || function_match.size() != 3) {
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 2 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return U_DEC_FUN_FAILED_MATCH_ERR;
- }
-
- std::string function_body_str = function_match[2].str();
- size_t last_semicolon_index = function_body_str.rfind(';');
- if(last_semicolon_index == std::string::npos) {
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 3 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return U_DEC_FUN_FAILED_MATCH_ERR;
- }
-
- function_body_str.erase(last_semicolon_index, function_body_str.size());
- string_replace_all(function_body_str, '\n', ' ');
-
- size_t var_dot_index = function_body_str.find('.');
- if(var_dot_index == std::string::npos) {
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 5 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return U_DEC_FUN_FAILED_MATCH_ERR;
- }
-
- std::string var_name = function_body_str.substr(0, var_dot_index);
- string_replace_all(response, '\n', ' ');
- std::smatch var_body_match;
- if(!std::regex_search(response, var_body_match, std::regex("var " + var_name + "=\\{(.*?)\\};", std::regex::ECMAScript)) || var_body_match.size() != 2) {
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 6 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return U_DEC_FUN_FAILED_MATCH_ERR;
- }
-
- std::string var_body_str = var_body_match[1].str();
- string_replace_all(var_body_str, '\n', ' ');
-
- std::vector<DecryptFuncCall> new_func_calls;
- std::map<std::string, DecryptFunction> new_func_decls;
- if(!js_code_to_operations(function_body_str, var_body_str, new_func_calls, new_func_decls)) {
- fprintf(stderr, "YoutubeSignatureDecryptor: Regex match 7 invalid. Youtube likely updated and QuickMedia needs to be fixed?\n");
- return U_DEC_FUN_FAILED_MATCH_ERR;
- }
-
- {
- std::lock_guard<std::mutex> lock(update_signature_mutex);
- decryption_function = function_body_str + "\n" + var_body_str;
- decrypt_function_last_updated = time(nullptr);
- up_to_date = true;
- func_calls = std::move(new_func_calls);
- func_decls = std::move(new_func_decls);
- }
-
- file_overwrite_atomic(get_cache_dir().join("youtube").join("decryption_function"), decryption_function);
- return 0;
- }
-}
-#endif \ No newline at end of file