aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--src/Text.cpp8
-rw-r--r--src/plugins/Youtube.cpp115
3 files changed, 95 insertions, 31 deletions
diff --git a/TODO b/TODO
index 670ff89..d2ac4bd 100644
--- a/TODO
+++ b/TODO
@@ -136,4 +136,5 @@ Update room name and topic text in ui when they change.
Support webp directly without using ffmpeg to convert it to a png.
Add client side 4chan file size limit (note, webm has different limit than images).
Add client side 4chan max comment chars limit.
-Use a directory icon and a file icon for non-media files in the file manager. \ No newline at end of file
+Use a directory icon and a file icon for non-media files in the file manager.
+Dynamically fetch 4chan api key, if it ever changes in the future. Same for youtube. \ No newline at end of file
diff --git a/src/Text.cpp b/src/Text.cpp
index cac485d..0431c4e 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -370,6 +370,7 @@ namespace QuickMedia
TextElement &textElement = textElements[textElementIndex];
const sf::Font *ff = latin_font;
int vertices_index = 0;
+ prevCodePoint = 0;
if(textElement.text_type == TextElement::TextType::CJK) {
ff = FontLoader::get_font(FontLoader::FontType::CJK);
vertices_index = 1;
@@ -513,13 +514,6 @@ namespace QuickMedia
glyphPos.x += glyph.advance + characterSpacing;
vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
}
-
- //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 4] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 3] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 2] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
-
- prevCodePoint = 0;
}
const float line_height = floor(vspace + lineSpacing);
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index cbe6b81..09568f6 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -333,6 +333,11 @@ namespace QuickMedia {
static std::mutex cookies_mutex;
static std::string cookies_filepath;
+ static std::string api_key;
+
+ static bool is_whitespace(char c) {
+ return (c >= 8 && c <= 13) || c == ' ';
+ }
static void remove_cookies_file_at_exit() {
std::lock_guard<std::mutex> lock(cookies_mutex);
@@ -340,6 +345,45 @@ namespace QuickMedia {
remove(cookies_filepath.c_str());
}
+ // TODO: Cache this and redownload it when a network request fails with this api key?
+ static std::string youtube_page_find_api_key() {
+ size_t api_key_index;
+ size_t api_key_index_end;
+ size_t api_key_length;
+ std::string website_result;
+ std::string::iterator api_key_start;
+
+ if(download_to_string("https://www.youtube.com", website_result, {}, true) != DownloadResult::OK)
+ goto fallback;
+
+ api_key_index = website_result.find("INNERTUBE_API_KEY");
+ if(api_key_index == std::string::npos)
+ goto fallback;
+
+ api_key_index += 17;
+ api_key_start = std::find_if(website_result.begin() + api_key_index, website_result.end(), [](char c) {
+ return c != '"' && c != ':' && !is_whitespace(c);
+ });
+
+ if(api_key_start == website_result.end())
+ goto fallback;
+
+ api_key_index = api_key_start - website_result.begin();
+ api_key_index_end = website_result.find('"', api_key_index);
+ if(api_key_index_end == std::string::npos)
+ goto fallback;
+
+ api_key_length = api_key_index_end - api_key_index;
+ if(api_key_length > 512) // sanity check
+ goto fallback;
+
+ return website_result.substr(api_key_index, api_key_length);
+
+ fallback:
+ fprintf(stderr, "Failed to fetch youtube api key, fallback to %s\n", api_key.c_str());
+ return "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
+ }
+
static std::vector<CommandArg> get_cookies() {
std::lock_guard<std::mutex> lock(cookies_mutex);
if(cookies_filepath.empty()) {
@@ -352,6 +396,13 @@ namespace QuickMedia {
cookies_filepath = filename;
atexit(remove_cookies_file_at_exit);
+ // TODO: Re-enable this if the api key ever changes in the future
+ #if 0
+ //api_key = youtube_page_find_api_key();
+ #else
+ api_key = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
+ #endif
+
// TODO: Is there any way to bypass this? this is needed to set VISITOR_INFO1_LIVE which is required to read comments
const char *args[] = { "curl", "-I", "-s", "-b", cookies_filepath.c_str(), "-c", cookies_filepath.c_str(), "https://www.youtube.com/subscription_manager?disable_polymer=1", nullptr };
if(exec_program(args, nullptr, nullptr) != 0)
@@ -954,7 +1005,8 @@ namespace QuickMedia {
if(str.empty())
return plugin_result_to_search_result(lazy_fetch(result_items));
- std::string next_url = "https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
+ std::vector<CommandArg> cookies = get_cookies();
+ std::string next_url = "https://www.youtube.com/youtubei/v1/browse?key=" + url_param_encode(api_key);
Json::Value request_json(Json::objectValue);
Json::Value context_json(Json::objectValue);
@@ -990,7 +1042,6 @@ namespace QuickMedia {
{ "--data-raw", Json::writeString(json_builder, request_json) }
};
- std::vector<CommandArg> cookies = get_cookies();
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
@@ -1040,7 +1091,8 @@ namespace QuickMedia {
}
PluginResult YoutubeChannelPage::search_get_continuation(const std::string &url, const std::string &current_continuation_token, BodyItems &result_items) {
- std::string next_url = "https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
+ std::vector<CommandArg> cookies = get_cookies();
+ std::string next_url = "https://www.youtube.com/youtubei/v1/browse?key=" + url_param_encode(api_key);
Json::Value request_json(Json::objectValue);
Json::Value context_json(Json::objectValue);
@@ -1073,7 +1125,6 @@ namespace QuickMedia {
{ "--data-raw", Json::writeString(json_builder, request_json) }
};
- std::vector<CommandArg> cookies = get_cookies();
additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
Json::Value json_root;
@@ -1223,7 +1274,9 @@ namespace QuickMedia {
};
struct SubscriptionData {
- SubscriptionEntry subscription_entry;
+ std::vector<SubscriptionEntry> subscription_entry;
+ std::string author;
+ bool inside_title = false;
bool inside_entry = false;
};
@@ -1285,27 +1338,38 @@ namespace QuickMedia {
return {YoutubeSubscriptionTaskResult{body_item, 0}};
}
- std::vector<SubscriptionData> subscription_data_list;
+ SubscriptionData subscription_data;
html_parser_parse(website_data.data(), website_data.size(), [](HtmlParser *html_parser, HtmlParseType parse_type, void *userdata) {
- std::vector<SubscriptionData> &subscription_data_list = *(std::vector<SubscriptionData>*)userdata;
+ SubscriptionData &subscription_data = *(SubscriptionData*)userdata;
+
+ if(!subscription_data.inside_entry && subscription_data.author.empty()) {
+ if(parse_type == HTML_PARSE_TAG_START && string_view_equals(&html_parser->tag_name, "title")) {
+ subscription_data.inside_title = true;
+ return;
+ } else if(parse_type == HTML_PARSE_TAG_END && string_view_equals(&html_parser->tag_name, "title")) {
+ subscription_data.inside_title = false;
+ subscription_data.author.assign(html_parser->text_stripped.data, html_parser->text_stripped.size);
+ return;
+ }
+ }
if(parse_type == HTML_PARSE_TAG_START && string_view_equals(&html_parser->tag_name, "entry")) {
- subscription_data_list.push_back({});
- subscription_data_list.back().inside_entry = true;
+ subscription_data.subscription_entry.push_back({});
+ subscription_data.inside_entry = true;
return;
} else if(parse_type == HTML_PARSE_TAG_END && string_view_equals(&html_parser->tag_name, "entry")) {
- subscription_data_list.back().inside_entry = false;
+ subscription_data.inside_entry = false;
return;
}
- if(subscription_data_list.empty() || !subscription_data_list.back().inside_entry)
+ if(!subscription_data.inside_entry)
return;
if(string_view_equals(&html_parser->tag_name, "title") && parse_type == HTML_PARSE_TAG_END) {
- subscription_data_list.back().subscription_entry.title.assign(html_parser->text_stripped.data, html_parser->text_stripped.size);
+ subscription_data.subscription_entry.back().title.assign(html_parser->text_stripped.data, html_parser->text_stripped.size);
} else if(string_view_equals(&html_parser->tag_name, "yt:videoId") && parse_type == HTML_PARSE_TAG_END) {
- subscription_data_list.back().subscription_entry.video_id.assign(html_parser->text_stripped.data, html_parser->text_stripped.size);
+ subscription_data.subscription_entry.back().video_id.assign(html_parser->text_stripped.data, html_parser->text_stripped.size);
} else if(string_view_equals(&html_parser->tag_name, "published") && parse_type == HTML_PARSE_TAG_END) {
std::string published_str(html_parser->text_stripped.data, html_parser->text_stripped.size);
@@ -1327,23 +1391,28 @@ namespace QuickMedia {
time.tm_min = minute;
time.tm_sec = second;
- subscription_data_list.back().subscription_entry.published = timegm(&time);
+ subscription_data.subscription_entry.back().published = timegm(&time);
}
- }, &subscription_data_list);
+ }, &subscription_data);
std::vector<YoutubeSubscriptionTaskResult> results;
- for(SubscriptionData &subscription_data : subscription_data_list) {
- if(subscription_data.subscription_entry.title.empty() || subscription_data.subscription_entry.video_id.empty() || subscription_data.subscription_entry.published == 0)
+ for(SubscriptionEntry &subscription_entry : subscription_data.subscription_entry) {
+ if(subscription_entry.title.empty() || subscription_entry.video_id.empty() || subscription_entry.published == 0)
continue;
- html_unescape_sequences(subscription_data.subscription_entry.title);
- auto body_item = BodyItem::create(std::move(subscription_data.subscription_entry.title));
- body_item->set_description("Uploaded " + seconds_to_relative_time_str(time_now - subscription_data.subscription_entry.published));
+ html_unescape_sequences(subscription_entry.title);
+ auto body_item = BodyItem::create(std::move(subscription_entry.title));
+ std::string description = "Uploaded " + seconds_to_relative_time_str(time_now - subscription_entry.published);
+ if(!subscription_data.author.empty()) {
+ description += '\n';
+ description += subscription_data.author;
+ }
+ body_item->set_description(std::move(description));
body_item->set_description_color(sf::Color(179, 179, 179));
- body_item->url = "https://www.youtube.com/watch?v=" + subscription_data.subscription_entry.video_id;
- body_item->thumbnail_url = "https://img.youtube.com/vi/" + subscription_data.subscription_entry.video_id + "/mqdefault.jpg";
+ body_item->url = "https://www.youtube.com/watch?v=" + subscription_entry.video_id;
+ body_item->thumbnail_url = "https://img.youtube.com/vi/" + subscription_entry.video_id + "/mqdefault.jpg";
body_item->thumbnail_size = sf::Vector2i(192, 108);
- results.push_back({std::move(body_item), subscription_data.subscription_entry.published});
+ results.push_back({std::move(body_item), subscription_entry.published});
}
return results;
});