From 0bde24974d6cc84e6bcc314de731144f4a479c2a Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 15 Oct 2020 15:33:23 +0200 Subject: Youtube: remove old recommendations --- src/QuickMedia.cpp | 61 ++++++++++++++++++++-------- src/plugins/Youtube.cpp | 106 ++++++++++++++++-------------------------------- 2 files changed, 77 insertions(+), 90 deletions(-) diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 8c3f5b6..3b059d9 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -187,16 +187,54 @@ namespace QuickMedia { Page *search_page; }; + static Path get_recommended_filepath(const char *plugin_name) { + Path video_history_dir = get_storage_dir().join("recommended"); + if(create_directory_recursive(video_history_dir) != 0) { + std::string err_msg = "Failed to create recommended directory "; + err_msg += video_history_dir.data; + show_notification("QuickMedia", err_msg.c_str(), Urgency::CRITICAL); + exit(1); + } + + Path video_history_filepath = video_history_dir; + return video_history_filepath.join(plugin_name).append(".json"); + } + // TODO: Make asynchronous - static void fill_recommended_items_from_json(const Json::Value &recommended_json, BodyItems &body_items) { + static void fill_recommended_items_from_json(const char *plugin_name, const Json::Value &recommended_json, BodyItems &body_items) { assert(recommended_json.isObject()); + const int64_t recommendations_autodelete_period = 60*60*24*20; // 20 days + time_t time_now = time(NULL); + int num_items_deleted = 0; + std::vector> recommended_items(recommended_json.size()); /* TODO: Optimize member access */ for(auto &member_name : recommended_json.getMemberNames()) { Json::Value recommended_item = recommended_json[member_name]; - if(recommended_item.isObject()) - recommended_items.push_back(std::make_pair(member_name, std::move(recommended_item))); + if(recommended_item.isObject()) { + Json::Value recommended_timestamp_json = recommended_item.get("recommended_timestamp", Json::Value::nullSingleton()); + Json::Value watched_timestamp_json = recommended_item.get("watched_timestamp", Json::Value::nullSingleton()); + if(watched_timestamp_json.isNumeric() && time_now - watched_timestamp_json.asInt64() >= recommendations_autodelete_period) { + ++num_items_deleted; + } else if(recommended_timestamp_json.isNumeric() && time_now - recommended_timestamp_json.asInt64() >= recommendations_autodelete_period) { + ++num_items_deleted; + } else if(recommended_timestamp_json.isNull() && watched_timestamp_json.isNull()) { + ++num_items_deleted; + } else { + recommended_items.push_back(std::make_pair(member_name, std::move(recommended_item))); + } + } + } + + if(num_items_deleted > 0) { + // TODO: Is there a better way? + Json::Value new_recommendations(Json::objectValue); + for(auto &recommended : recommended_items) { + new_recommendations[recommended.first] = recommended.second; + } + fprintf(stderr, "Number of old recommendations to delete: %d\n", num_items_deleted); + save_json_to_file_atomic(get_recommended_filepath(plugin_name), new_recommendations); } /* TODO: Better algorithm for recommendations */ @@ -567,7 +605,7 @@ namespace QuickMedia { auto recommended_body = create_body(); recommended_body->draw_thumbnails = true; - fill_recommended_items_from_json(load_recommended_json(), recommended_body->items); + fill_recommended_items_from_json(plugin_name, load_recommended_json(), recommended_body->items); tabs.push_back(Tab{std::move(recommended_body), std::make_unique(this, tabs.front().page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "pornhub") == 0) { auto search_body = create_body(); @@ -727,19 +765,6 @@ namespace QuickMedia { return video_history_filepath.join(plugin_name).append(".json"); } - static Path get_recommended_filepath(const char *plugin_name) { - Path video_history_dir = get_storage_dir().join("recommended"); - if(create_directory_recursive(video_history_dir) != 0) { - std::string err_msg = "Failed to create recommended directory "; - err_msg += video_history_dir.data; - show_notification("QuickMedia", err_msg.c_str(), Urgency::CRITICAL); - exit(1); - } - - Path video_history_filepath = video_history_dir; - return video_history_filepath.join(plugin_name).append(".json"); - } - // This is not cached because we could have multiple instances of QuickMedia running the same plugin! // TODO: Find a way to optimize this Json::Value Program::load_video_history_json() { @@ -1705,7 +1730,7 @@ namespace QuickMedia { current_page = previous_page; break; } else if(update_err != VideoPlayer::Error::OK) { - show_notification("Video player", "The video player failed to play the video", Urgency::CRITICAL); + show_notification("Video player", "The video player failed to play the video (error code " + std::to_string((int)update_err) + ")", Urgency::CRITICAL); current_page = previous_page; break; } diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 9e84903..a9a2811 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -4,15 +4,8 @@ #include namespace QuickMedia { - static std::shared_ptr parse_content_video_renderer(const Json::Value &content_item_json, std::unordered_set &added_videos) { - if(!content_item_json.isObject()) - return nullptr; - - const Json::Value &video_renderer_json = content_item_json["videoRenderer"]; - if(!video_renderer_json.isObject()) - return nullptr; - - const Json::Value &video_id_json = video_renderer_json["videoId"]; + static std::shared_ptr parse_common_video_item(const Json::Value &video_item_json, std::unordered_set &added_videos) { + const Json::Value &video_id_json = video_item_json["videoId"]; if(!video_id_json.isString()) return nullptr; @@ -23,7 +16,7 @@ namespace QuickMedia { std::string thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg"; const char *date = nullptr; - const Json::Value &published_time_text_json = video_renderer_json["publishedTimeText"]; + const Json::Value &published_time_text_json = video_item_json["publishedTimeText"]; if(published_time_text_json.isObject()) { const Json::Value &text_json = published_time_text_json["simpleText"]; if(text_json.isString()) @@ -31,7 +24,7 @@ namespace QuickMedia { } const char *length = nullptr; - const Json::Value &length_text_json = video_renderer_json["lengthText"]; + const Json::Value &length_text_json = video_item_json["lengthText"]; if(length_text_json.isObject()) { const Json::Value &text_json = length_text_json["simpleText"]; if(text_json.isString()) @@ -39,15 +32,25 @@ namespace QuickMedia { } const char *title = nullptr; - const Json::Value &title_json = video_renderer_json["title"]; + const Json::Value &title_json = video_item_json["title"]; if(title_json.isObject()) { - const Json::Value &runs_json = title_json["runs"]; - if(runs_json.isArray() && !runs_json.empty()) { - const Json::Value &first_runs_json = runs_json[0]; - if(first_runs_json.isObject()) { - const Json::Value &text_json = first_runs_json["text"]; - if(text_json.isString()) - title = text_json.asCString(); + const Json::Value &simple_text_json = title_json["simpleText"]; + if(simple_text_json.isString()) { + title = simple_text_json.asCString(); + } + } + + if(!title) { + const Json::Value &title_json = video_item_json["title"]; + if(title_json.isObject()) { + const Json::Value &runs_json = title_json["runs"]; + if(runs_json.isArray() && !runs_json.empty()) { + const Json::Value &first_runs_json = runs_json[0]; + if(first_runs_json.isObject()) { + const Json::Value &text_json = first_runs_json["text"]; + if(text_json.isString()) + title = text_json.asCString(); + } } } } @@ -72,6 +75,17 @@ namespace QuickMedia { return body_item; } + static std::shared_ptr parse_content_video_renderer(const Json::Value &content_item_json, std::unordered_set &added_videos) { + if(!content_item_json.isObject()) + return nullptr; + + const Json::Value &video_renderer_json = content_item_json["videoRenderer"]; + if(!video_renderer_json.isObject()) + return nullptr; + + return parse_common_video_item(video_renderer_json, added_videos); + } + // Returns empty string if continuation token can't be found static std::string item_section_renderer_get_continuation_token(const Json::Value &item_section_renderer_json) { const Json::Value &continuations_json = item_section_renderer_json["continuations"]; @@ -150,60 +164,8 @@ namespace QuickMedia { const Json::Value &compact_video_renderer_json = item_json["compactVideoRenderer"]; if(!compact_video_renderer_json.isObject()) return nullptr; - - const Json::Value &video_id_json = compact_video_renderer_json["videoId"]; - if(!video_id_json.isString()) - return nullptr; - - std::string video_id_str = video_id_json.asString(); - if(added_videos.find(video_id_str) != added_videos.end()) - return nullptr; - - std::string thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg"; - - const char *date = nullptr; - const Json::Value &published_time_text_json = compact_video_renderer_json["publishedTimeText"]; - if(published_time_text_json.isObject()) { - const Json::Value &text_json = published_time_text_json["simpleText"]; - if(text_json.isString()) - date = text_json.asCString(); - } - - const char *length = nullptr; - const Json::Value &length_text_json = compact_video_renderer_json["lengthText"]; - if(length_text_json.isObject()) { - const Json::Value &text_json = length_text_json["simpleText"]; - if(text_json.isString()) - length = text_json.asCString(); - } - - const char *title = nullptr; - const Json::Value &title_json = compact_video_renderer_json["title"]; - if(title_json.isObject()) { - const Json::Value &simple_text_json = title_json["simpleText"]; - if(simple_text_json.isString()) { - title = simple_text_json.asCString(); - } - } - if(!title) - return nullptr; - - auto body_item = BodyItem::create(title); - /* TODO: Make date a different color */ - std::string date_str; - if(date) - date_str += date; - if(length) { - if(!date_str.empty()) - date_str += '\n'; - date_str += length; - } - body_item->set_description(std::move(date_str)); - body_item->url = "https://www.youtube.com/watch?v=" + video_id_str; - body_item->thumbnail_url = std::move(thumbnail_url); - added_videos.insert(video_id_str); - return body_item; + return parse_common_video_item(compact_video_renderer_json, added_videos); } SearchResult YoutubeSearchPage::search(const std::string &str, BodyItems &result_items) { -- cgit v1.2.3