aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO1
-rw-r--r--plugins/Youtube.hpp1
-rw-r--r--src/QuickMedia.cpp1
-rw-r--r--src/plugins/Youtube.cpp124
4 files changed, 124 insertions, 3 deletions
diff --git a/TODO b/TODO
index 7256868..4f654af 100644
--- a/TODO
+++ b/TODO
@@ -213,3 +213,4 @@ Very large resolutions, such as 7680x2160 (id 272) for video https://www.youtube
Use std::move(string) for all places where text.set_string is called.
Use reference for text.get_string() in all places.
Cleanup font sizes that are not visible (or not used for a while). Also do the same for character ranges font texture.
+Youtube rating doesn't work if a youtube video has more dislikes than likes. The ratio simply becomes inverted then. Find a way to fix this using youtubes api. \ No newline at end of file
diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp
index 2cfe36e..a0c2e16 100644
--- a/plugins/Youtube.hpp
+++ b/plugins/Youtube.hpp
@@ -174,6 +174,7 @@ namespace QuickMedia {
private:
std::string timestamp;
std::string comments_continuation_token;
+ int64_t likes = -1;
std::string livestream_url;
std::vector<YoutubeVideoFormat> video_formats;
std::vector<YoutubeAudioFormat> audio_formats;
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 464417a..3ce881e 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -6725,6 +6725,7 @@ namespace QuickMedia {
if(selected_tab == MESSAGES_TAB_INDEX && current_room && current_room->body_item && (!current_room->last_message_read || has_unread_messages) && matrix->is_initial_sync_finished()) {
if(after_token.empty() && !tabs[selected_tab].body->is_bottom_cut_off() && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.get_elapsed_time_seconds() >= read_marker_timeout_sec) {
+ // TODO: Only set read marker once every second if the message is not the last message in the room
auto body_items = tabs[selected_tab].body->get_items();
int last_timeline_message = (int)body_items.size() - 1;
for(int i = last_timeline_message - 1; i >= 0; --i) {
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index 8e08062..42659b7 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -2036,6 +2036,109 @@ namespace QuickMedia {
return "";
}
+ // Returns -1 if not found
+ static int64_t toggle_button_renderer_get_likes(const Json::Value &toggle_button_renderer_json) {
+ if(!toggle_button_renderer_json.isObject())
+ return -1;
+
+ const Json::Value &default_text_json = toggle_button_renderer_json["defaultText"];
+ if(!default_text_json.isObject())
+ return -1;
+
+ const Json::Value &simple_text_json = default_text_json["simpleText"];
+ if(!simple_text_json.isString())
+ return -1;
+
+ // The accessibility text for videos with 0 likes is "No Likes". We instead check for 0 in the simple text.
+ if(strcmp(simple_text_json.asCString(), "0") == 0)
+ return 0;
+
+ const Json::Value &accessibility_json = default_text_json["accessibility"];
+ if(!accessibility_json.isObject())
+ return -1;
+
+ const Json::Value &accessibility_data_json = accessibility_json["accessibilityData"];
+ if(!accessibility_data_json.isObject())
+ return -1;
+
+ const Json::Value &label_json = accessibility_data_json["label"];
+ if(!label_json.isString())
+ return -1;
+
+ std::string label = label_json.asString();
+ size_t space_index = label.find(' ');
+ if(space_index != std::string::npos)
+ label.erase(space_index);
+
+ string_replace_all(label, ",", "");
+
+ errno = 0;
+ char *endptr;
+ const int64_t likes = strtoll(label.c_str(), &endptr, 10);
+ if(endptr != label.c_str() && errno == 0)
+ return likes;
+ return -1;
+ }
+
+ // Returns -1 if not found
+ static int64_t two_column_watch_next_results_get_video_likes(const Json::Value &tcwnr_json) {
+ if(!tcwnr_json.isObject())
+ return -1;
+
+ const Json::Value &results_json = tcwnr_json["results"];
+ if(!results_json.isObject())
+ return -1;
+
+ const Json::Value &results2_json = results_json["results"];
+ if(!results2_json.isObject())
+ return -1;
+
+ const Json::Value &contents_json = results2_json["contents"];
+ if(!contents_json.isArray())
+ return -1;
+
+ for(const Json::Value &content_item_json : contents_json) {
+ if(!content_item_json.isObject())
+ continue;
+
+ const Json::Value &video_primary_info_renderer_json = content_item_json["videoPrimaryInfoRenderer"];
+ if(!video_primary_info_renderer_json.isObject())
+ continue;
+
+ const Json::Value &video_actions_json = video_primary_info_renderer_json["videoActions"];
+ if(!video_actions_json.isObject())
+ continue;
+
+ const Json::Value &menu_renderer_json = video_actions_json["menuRenderer"];
+ if(!menu_renderer_json.isObject())
+ continue;
+
+ const Json::Value &top_level_buttons_json = menu_renderer_json["topLevelButtons"];
+ if(!top_level_buttons_json.isArray())
+ continue;
+
+ for(const Json::Value &top_level_button_json : top_level_buttons_json) {
+ if(!top_level_button_json.isObject())
+ continue;
+
+ const Json::Value &toggle_button_renderer_json = top_level_button_json["toggleButtonRenderer"];
+ if(!toggle_button_renderer_json.isObject())
+ continue;
+
+ const Json::Value &target_id_json = toggle_button_renderer_json["targetId"];
+ if(!target_id_json.isString())
+ continue;
+
+ if(strcmp(target_id_json.asCString(), "watch-like") != 0)
+ continue;
+
+ return toggle_button_renderer_get_likes(toggle_button_renderer_json);
+ }
+ }
+
+ return -1;
+ }
+
static int youtube_url_timestamp_to_seconds(const std::string &timestamp) {
int hours = 0;
int minutes = 0;
@@ -2080,6 +2183,7 @@ namespace QuickMedia {
BodyItems YoutubeVideoPage::get_related_media(const std::string &url) {
comments_continuation_token.clear();
+ likes = -1;
BodyItems result_items;
std::string video_id;
@@ -2125,6 +2229,9 @@ namespace QuickMedia {
if(comments_continuation_token.empty())
comments_continuation_token = two_column_watch_next_results_get_comments_continuation_token(tcwnr_json);
+ if(likes == -1)
+ likes = two_column_watch_next_results_get_video_likes(tcwnr_json);
+
const Json::Value &secondary_results_json = tcwnr_json["secondaryResults"];
if(!secondary_results_json.isObject())
return result_items;
@@ -2187,7 +2294,11 @@ namespace QuickMedia {
return result;
}
- static std::shared_ptr<BodyItem> video_details_to_body_item(const YoutubeVideoDetails &video_details) {
+ static int64_t round_double(double value) {
+ return value + 0.5;
+ }
+
+ static std::shared_ptr<BodyItem> video_details_to_body_item(const YoutubeVideoDetails &video_details, int64_t likes) {
auto body_item = BodyItem::create(video_details.title);
std::string description;
@@ -2198,7 +2309,14 @@ namespace QuickMedia {
if(!video_details.rating.empty()) {
if(!description.empty())
description += " • ";
- description += "rated " + video_details.rating.substr(0, 4) + "/5";
+
+ if(likes == -1) {
+ description += "rated " + video_details.rating.substr(0, 4) + "/5";
+ } else {
+ fprintf(stderr, "video rating: %s\n", video_details.rating.c_str());
+ int64_t num_dislikes = round_double((double)likes * ((5.0 - atof(video_details.rating.c_str())) / 5.0));
+ description += "👍 " + std::to_string(likes) + " 👎 " + std::to_string(num_dislikes);
+ }
}
if(!video_details.author.empty()) {
@@ -2221,7 +2339,7 @@ namespace QuickMedia {
PluginResult YoutubeVideoPage::get_related_pages(const BodyItems &related_videos, const std::string &channel_url, std::vector<Tab> &result_tabs) {
auto description_page_body = create_body();
- description_page_body->append_item(video_details_to_body_item(video_details));
+ description_page_body->append_item(video_details_to_body_item(video_details, likes));
auto related_page_body = create_body(false, true);
related_page_body->set_items(related_videos);