From b09d1e70661226697e2441c18ea6ff59c387fb93 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 16 Jul 2021 07:31:02 +0200 Subject: Add sorting options to nyaa.si --- TODO | 4 +-- plugins/NyaaSi.hpp | 34 +++++++++++++++++++-- plugins/Page.hpp | 1 + src/QuickMedia.cpp | 13 +++++++- src/plugins/Matrix.cpp | 18 ++++++----- src/plugins/NyaaSi.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 135 insertions(+), 17 deletions(-) diff --git a/TODO b/TODO index 3d74d0a..8bb8825 100644 --- a/TODO +++ b/TODO @@ -34,7 +34,6 @@ Cleanup keybindings. Some require ctrl, some dont. Set the icon of the window to be the icon of the plugin. Nice for KDE, GNOME, etc with titlebars. If --no-audio is used then music should be played with a lightweight music player instead. MPV is heavy even for music (60mb RAM). Maybe use sfml audio functions? Update 4chan thread in real time, just like 4chan-x. -Add option to sort by other than timestamp for nyaa.si. Add url preview for matrix (using matrix api, fallback to client url preview (using our own url preview project) if disabled by the homeserver). IMPORTANT: Cleanup old messages in matrix (from matrix plugin), and instead either save them to disk or refetch them from server when going up to read old messages. (High memory usage, high disk space) Do not try to reload/redownload thumbnail that fails to download after its cleared when its no longer visible on screen and then becomes visible. @@ -172,4 +171,5 @@ Youtube download gets stuck sometimes because of audio. Find a workaround for th Dynamically change youtube video quality by modifying the itags (and other params?) if download is buffering or if the video is lagging. Use the new media proxy for downloading youtube videos as well. PgUp/PgDown shouldn't move body by the number of visible items. It should instead move by the height of the body. -Add option to view dead link in 4chan with 4chan archive and navigate to crossboard links. \ No newline at end of file +Add option to view dead link in 4chan with 4chan archive and navigate to crossboard links. +Show latest message before sync is done for a room when the latest message is an edit. Right now it has to fetch previous messages until the first non-edit message. diff --git a/plugins/NyaaSi.hpp b/plugins/NyaaSi.hpp index 897c5ab..6f161ac 100644 --- a/plugins/NyaaSi.hpp +++ b/plugins/NyaaSi.hpp @@ -16,18 +16,48 @@ namespace QuickMedia { const bool is_sukebei; }; + enum class NyaaSiSortType { + SIZE_DESC, + UPLOAD_DATE_DESC, + SEEDERS_DESC, + LEECHERS_DESC, + DOWNLOADS_DESC, + + SIZE_ASC, + UPLOAD_DATE_ASC, + SEEDERS_ASC, + LEECHERS_ASC, + DOWNLOADS_ASC + }; + class NyaaSiSearchPage : public Page { public: - NyaaSiSearchPage(Program *program, std::string category_name, std::string category_id, std::string domain) : Page(program), category_name(std::move(category_name)), category_id(std::move(category_id)), domain(std::move(domain)) {} - const char* get_title() const override { return category_name.c_str(); } + NyaaSiSearchPage(Program *program, std::string category_name, std::string category_id, std::string domain); + const char* get_title() const override { return title.c_str(); } bool search_is_filter() override { return false; } SearchResult search(const std::string &str, BodyItems &result_items) override; PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; + void set_sort_type(NyaaSiSortType sort_type); + const std::string category_name; const std::string category_id; const std::string domain; + private: + NyaaSiSortType sort_type = NyaaSiSortType::UPLOAD_DATE_DESC; + std::string title; + }; + + class NyaaSiSortOrderPage : public Page { + public: + NyaaSiSortOrderPage(Program *program, Body *body, NyaaSiSearchPage *search_page) : Page(program), body(body), search_page(search_page) {} + const char* get_title() const override { return "Sort order"; } + PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; + bool submit_is_async() override { return false; } + private: + Body *body; + NyaaSiSearchPage *search_page; }; class NyaaSiTorrentPage : public Page { diff --git a/plugins/Page.hpp b/plugins/Page.hpp index 2d10d50..3a382ec 100644 --- a/plugins/Page.hpp +++ b/plugins/Page.hpp @@ -67,6 +67,7 @@ namespace QuickMedia { Program *program; std::shared_ptr submit_body_item; // TODO: Remove this + bool needs_refresh = false; // Set to true to refresh the page }; enum class TrackResult { diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 252bb19..64833bd 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -2070,6 +2070,17 @@ namespace QuickMedia { if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->update(); + if(tabs[selected_tab].page->needs_refresh && tab_associated_data[selected_tab].fetch_status == FetchStatus::NONE && !tab_associated_data[selected_tab].fetching_next_page_running) { + tabs[selected_tab].page->needs_refresh = false; + if(tabs[selected_tab].page->is_lazy_fetch_page()) { + tab_associated_data[selected_tab].lazy_fetch_finished = false; + tab_associated_data[selected_tab].fetched_page = 0; + } else if(!tabs[selected_tab].page->search_is_filter()) { + tab_associated_data[selected_tab].search_text_updated = true; + } + tabs[selected_tab].body->clear_items(); + } + if(tabs[selected_tab].page->is_ready() && tabs[selected_tab].page->is_lazy_fetch_page() && tab_associated_data[selected_tab].fetch_status == FetchStatus::NONE && !tab_associated_data[selected_tab].lazy_fetch_finished) { tab_associated_data[selected_tab].fetch_status = FetchStatus::LOADING; tab_associated_data[selected_tab].fetch_type = FetchType::LAZY; @@ -2165,7 +2176,7 @@ namespace QuickMedia { associated_data.lazy_fetch_finished = true; FetchResult fetch_result = associated_data.fetch_future.get(); tabs[i].body->items = std::move(fetch_result.body_items); - if(tabs[i].search_bar) tabs[i].body->filter_search_fuzzy(tabs[i].search_bar->get_text()); + if(tabs[i].search_bar && tabs[i].page->search_is_filter()) tabs[i].body->filter_search_fuzzy(tabs[i].search_bar->get_text()); if(tabs[i].body->attach_side == AttachSide::TOP) { tabs[i].body->select_first_item(); } diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 17bc17b..7439bf6 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -81,13 +81,14 @@ namespace QuickMedia { } } - static void remove_body_item_by_url(BodyItems &body_items, const std::string &url) { - for(auto it = body_items.begin(); it != body_items.end();) { - if((*it)->url == url) - it = body_items.erase(it); - else - ++it; + static bool remove_body_item_by_url(BodyItems &body_items, const std::string &url) { + for(auto it = body_items.begin(); it != body_items.end(); ++it) { + if((*it)->url == url) { + body_items.erase(it); + return true; + } } + return false; } static int color_hash_code(const std::string &str) { @@ -873,7 +874,10 @@ namespace QuickMedia { } void MatrixInvitesPage::remove_body_item_by_room_id(const std::string &room_id) { - remove_body_item_by_url(body->items, room_id); + if(remove_body_item_by_url(body->items, room_id)) { + prev_invite_count = body->items.size(); + title = "Invites (" + std::to_string(body->items.size()) + ")"; + } } void MatrixInvitesPage::clear_data() { diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp index d4667af..0202322 100644 --- a/src/plugins/NyaaSi.cpp +++ b/src/plugins/NyaaSi.cpp @@ -107,11 +107,55 @@ namespace QuickMedia { return time_str; } + static const std::array sort_type_names = { + "Size desc 🡇", + "Uploaded date desc 🡇", + "Seeders desc 🡇", + "Leechers desc 🡇", + "Downloads desc 🡇", + + "Size asc 🡅", + "Uploaded date asc 🡅", + "Seeders asc 🡅", + "Leechers asc 🡅", + "Downloads asc 🡅" + }; + + static const std::array sort_params = { + "s=size&o=desc", + "s=id&o=desc", + "s=seeders&o=desc", + "s=leechers&o=desc", + "s=downloads&o=desc", + + "s=size&o=asc", + "s=id&o=asc", + "s=seeders&o=asc", + "s=leechers&o=asc", + "s=downloads&o=asc", + }; + + static std::shared_ptr create_sort_body_item(const std::string &title, NyaaSiSortType sort_type) { + auto body_item = BodyItem::create(title); + body_item->userdata = (void*)sort_type; + return body_item; + } + + static void sort_page_create_body_items(BodyItems &body_items, NyaaSiSortType sort_type) { + for(size_t i = 0; i < sort_type_names.size(); ++i) { + std::string prefix = " "; + if((NyaaSiSortType)i == sort_type) + prefix = "* "; + body_items.push_back(create_sort_body_item(prefix + sort_type_names[i], (NyaaSiSortType)i)); + } + } + // TODO: Also show the number of comments for each torrent. TODO: Optimize? // TODO: Show each field as seperate columns instead of seperating by | - static SearchResult search_page(const std::string &domain, const std::string &list_url, const std::string &text, int page, BodyItems &result_items) { + static SearchResult search_page(const std::string &domain, const std::string &list_url, const std::string &text, int page, NyaaSiSortType sort_type, BodyItems &result_items) { std::string full_url = "https://" + domain + "/?c=" + list_url + "&f=0&p=" + std::to_string(page) + "&q="; full_url += url_param_encode(text); + full_url += std::string("&") + sort_params[(size_t)sort_type]; std::string website_data; if(download_to_string(full_url, website_data, {}, true) != DownloadResult::OK) @@ -251,21 +295,34 @@ namespace QuickMedia { std::string domain = is_sukebei ? "sukebei.nyaa.si" : "nyaa.si"; BodyItems result_items; - SearchResult search_result = search_page(domain, url, "", 1, result_items); + SearchResult search_result = search_page(domain, url, "", 1, NyaaSiSortType::UPLOAD_DATE_DESC, result_items); if(search_result != SearchResult::OK) return search_result_to_plugin_result(search_result); + auto search_page = std::make_unique(program, strip(title), url, std::move(domain)); + NyaaSiSearchPage *search_page_p = search_page.get(); auto body = create_body(); body->items = std::move(result_items); - result_tabs.push_back(Tab{std::move(body), std::make_unique(program, strip(title), url, std::move(domain)), create_search_bar("Search...", 500)}); + result_tabs.push_back(Tab{std::move(body), std::move(search_page), create_search_bar("Search...", 500)}); + + auto sort_order_page_body = create_body(); + Body *sort_order_page_body_p = sort_order_page_body.get(); + sort_page_create_body_items(sort_order_page_body->items, NyaaSiSortType::UPLOAD_DATE_DESC); + result_tabs.push_back(Tab{std::move(sort_order_page_body), std::make_unique(program, sort_order_page_body_p, search_page_p), nullptr}); return PluginResult::OK; } + NyaaSiSearchPage::NyaaSiSearchPage(Program *program, std::string category_name, std::string category_id, std::string domain) : + Page(program), category_name(std::move(category_name)), category_id(std::move(category_id)), domain(std::move(domain)) + { + set_sort_type(NyaaSiSortType::UPLOAD_DATE_DESC); + } + SearchResult NyaaSiSearchPage::search(const std::string &str, BodyItems &result_items) { - return search_page(domain, category_id, str, 1, result_items); + return search_page(domain, category_id, str, 1, sort_type, result_items); } PluginResult NyaaSiSearchPage::get_page(const std::string &str, int page, BodyItems &result_items) { - return search_result_to_plugin_result(search_page(domain, category_id, str, 1 + page, result_items)); + return search_result_to_plugin_result(search_page(domain, category_id, str, 1 + page, sort_type, result_items)); } struct ResultItemExtra { @@ -441,6 +498,21 @@ namespace QuickMedia { return PluginResult::OK; } + void NyaaSiSearchPage::set_sort_type(NyaaSiSortType sort_type) { + this->sort_type = sort_type; + title = category_name + " | " + sort_type_names[(size_t)sort_type]; + title.erase(title.end() - 5, title.end()); // Erase emoji character and space at the end. TODO: Remove this when tabs support emojis. + } + + PluginResult NyaaSiSortOrderPage::submit(const std::string&, const std::string&, std::vector&) { + const NyaaSiSortType sort_type = (NyaaSiSortType)(size_t)submit_body_item->userdata; + body->items.clear(); + sort_page_create_body_items(body->items, sort_type); + search_page->set_sort_type(sort_type); + search_page->needs_refresh = true; + return PluginResult::OK; + } + PluginResult NyaaSiTorrentPage::submit(const std::string &title, const std::string &url, std::vector &result_tabs) { (void)title; (void)result_tabs; -- cgit v1.2.3