diff options
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | include/Body.hpp | 4 | ||||
-rw-r--r-- | include/DownloadUtils.hpp | 2 | ||||
-rw-r--r-- | plugins/Soundcloud.hpp | 2 | ||||
-rw-r--r-- | src/Body.cpp | 41 | ||||
-rw-r--r-- | src/DownloadUtils.cpp | 1 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 13 | ||||
-rw-r--r-- | src/SearchBar.cpp | 7 | ||||
-rw-r--r-- | src/plugins/NyaaSi.cpp | 8 | ||||
-rw-r--r-- | src/plugins/Youtube.cpp | 8 |
10 files changed, 66 insertions, 21 deletions
@@ -31,6 +31,7 @@ Press `Home` to scroll to the top or `End` to scroll to the bottom.\ Press `Enter` (aka `Return`) to select the item.\ Press `ESC` to go back to the previous menu.\ Press `ESC` or `Backspace` to close the video.\ +Press `Ctrl + D` to clear the search input text.\ Press `Ctrl + F` to switch between window mode and fullscreen mode when watching a video.\ Press `Space` to pause/unpause a video.\ Press `Ctrl + R` to show video comments, related videos or video channel when watching a video (if supported).\ diff --git a/include/Body.hpp b/include/Body.hpp index fb22a21..22d9ff9 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -230,12 +230,8 @@ namespace QuickMedia { float get_item_height(BodyItem *item, float width, bool load_texture = true, bool include_embedded_item = true, bool merge_with_previous = false, int item_index = -1); float get_spacing_y() const; - static bool string_find_case_insensitive(const std::string &str, const std::string &substr); - // TODO: Make this actually fuzzy... Right now it's just a case insensitive string find. - // This would require reordering the body. // TODO: Highlight the part of the text that matches the search. - // TODO: Ignore dot, whitespace and special characters void filter_search_fuzzy(const std::string &text); void filter_search_fuzzy_item(const std::string &text, BodyItem *body_item); diff --git a/include/DownloadUtils.hpp b/include/DownloadUtils.hpp index 40ec12a..dd74f50 100644 --- a/include/DownloadUtils.hpp +++ b/include/DownloadUtils.hpp @@ -22,7 +22,9 @@ namespace QuickMedia { using DownloadErrorHandler = std::function<bool(std::string&)>; DownloadResult download_to_string(const std::string &url, std::string &result, const std::vector<CommandArg> &additional_args, bool use_browser_useragent = false, bool fail_on_error = true); + // Note: This function saves the content to the file atomically DownloadResult download_to_string_cache(const std::string &url, std::string &result, const std::vector<CommandArg> &additional_args, bool use_browser_useragent = false, DownloadErrorHandler error_handler = nullptr, Path cache_path = ""); + // Note: This function saves the content to the file atomically DownloadResult download_to_file(const std::string &url, const std::string &destination_filepath, const std::vector<CommandArg> &additional_args, bool use_browser_useragent = false); DownloadResult download_to_json(const std::string &url, rapidjson::Document &result, const std::vector<CommandArg> &additional_args, bool use_browser_useragent = false, bool fail_on_error = true); }
\ No newline at end of file diff --git a/plugins/Soundcloud.hpp b/plugins/Soundcloud.hpp index 0d73d36..e04d409 100644 --- a/plugins/Soundcloud.hpp +++ b/plugins/Soundcloud.hpp @@ -52,7 +52,7 @@ namespace QuickMedia { std::unique_ptr<LazyFetchPage> create_channels_page(Program *, const std::string &) override { return nullptr; } std::string get_url() override { return url; } std::string url_get_playable_url(const std::string &url) override; - bool video_should_be_skipped(const std::string &url) override { return url == "track"; } + bool video_should_be_skipped(const std::string &url) override { return url == "track" || url.find("/stream/users/") != std::string::npos; } private: std::string url; }; diff --git a/src/Body.cpp b/src/Body.cpp index 5c40c96..6833134 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -1254,14 +1254,35 @@ namespace QuickMedia { return spacing_y; } + // Returns std::string::npos if not found + static size_t find_next_non_whitespace_character(const std::string &str, size_t start_index) { + for(size_t i = start_index; i < str.size(); ++i) { + char c = str[i]; + if(c != ' ' && c != '\n' && c != '\t' && c != '\v') + return i; + } + return std::string::npos; + } + // TODO: Support utf-8 case insensitive find - //static - bool Body::string_find_case_insensitive(const std::string &str, const std::string &substr) { - auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(), - [](char c1, char c2) { - return std::toupper(c1) == std::toupper(c2); - }); - return it != str.end(); + static bool string_find_fuzzy_case_insensitive(const std::string &str, const std::string &substr) { + size_t substr_index = find_next_non_whitespace_character(substr, 0); + if(substr_index == std::string::npos) + return true; + + char substr_c = std::toupper(substr[substr_index]); + for(size_t i = 0; i < str.size(); ++i) { + char str_c = std::toupper(str[i]); + if(str_c == substr_c) { + substr_index = find_next_non_whitespace_character(substr, substr_index + 1); + if(substr_index == std::string::npos || substr_index == substr.size()) + return true; + else + substr_c = std::toupper(substr[substr_index]); + } + } + + return false; } void Body::filter_search_fuzzy(const std::string &text) { @@ -1280,11 +1301,11 @@ namespace QuickMedia { } void Body::filter_search_fuzzy_item(const std::string &text, BodyItem *body_item) { - body_item->visible = string_find_case_insensitive(body_item->get_title(), text); + body_item->visible = string_find_fuzzy_case_insensitive(body_item->get_title(), text); if(!body_item->visible && !body_item->get_description().empty()) - body_item->visible = string_find_case_insensitive(body_item->get_description(), text); + body_item->visible = string_find_fuzzy_case_insensitive(body_item->get_description(), text); if(!body_item->visible && !body_item->get_author().empty()) - body_item->visible = string_find_case_insensitive(body_item->get_author(), text); + body_item->visible = string_find_fuzzy_case_insensitive(body_item->get_author(), text); } bool Body::no_items_visible() const { diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp index e83bced..4a35640 100644 --- a/src/DownloadUtils.cpp +++ b/src/DownloadUtils.cpp @@ -87,7 +87,6 @@ namespace QuickMedia { } } - // TODO: Use this everywhere we want to save to file (such as manga download) DownloadResult download_to_file(const std::string &url, const std::string &destination_filepath, const std::vector<CommandArg> &additional_args, bool use_browser_useragent) { Path tmp_filepath = destination_filepath; tmp_filepath.append(".tmp"); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index fad2b94..053c97f 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -685,6 +685,11 @@ namespace QuickMedia { window.setTitle("QuickMedia - " + std::string(plugin_name)); no_video = force_no_video; + if(strcmp(plugin_name, "youtube-audio") == 0) { + plugin_name = "youtube"; + no_video = true; + } + std::string plugin_logo_path; const char *plugin_logo_name = get_plugin_logo_name(plugin_name); if(plugin_logo_name) @@ -710,6 +715,7 @@ namespace QuickMedia { pipe_body->items.push_back(create_launcher_body_item("Soundcloud", "soundcloud", resources_root + "icons/soundcloud_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("Spotify", "spotify", resources_root + "icons/spotify_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("YouTube", "youtube", resources_root + "icons/yt_launcher.png")); + pipe_body->items.push_back(create_launcher_body_item("YouTube (audio only)", "youtube-audio", resources_root + "icons/yt_launcher.png")); tabs.push_back(Tab{std::move(pipe_body), std::make_unique<PipePage>(this, "Select plugin to launch"), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "manganelo") == 0) { auto search_body = create_body(); @@ -787,7 +793,7 @@ namespace QuickMedia { tabs.push_back(Tab{std::move(search_body), std::make_unique<SpankbangSearchPage>(this), create_search_bar("Search...", 500)}); } else if(strcmp(plugin_name, "spotify") == 0) { auto search_body = create_body(); - tabs.push_back(Tab{std::move(search_body), std::make_unique<SpotifyPodcastSearchPage>(this), create_search_bar("Search...", 250)}); + tabs.push_back(Tab{std::move(search_body), std::make_unique<SpotifyPodcastSearchPage>(this), create_search_bar("Search...", 350)}); no_video = true; } else if(strcmp(plugin_name, "soundcloud") == 0) { auto search_body = create_body(); @@ -1482,6 +1488,9 @@ namespace QuickMedia { update_idle_state(); handle_window_close(); + if(!loop_running || !window.isOpen()) + break; + if(redraw) { redraw = false; if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->onWindowResize(window_size); @@ -1985,7 +1994,7 @@ namespace QuickMedia { } } - fprintf(stderr, "event name: %s\n", event_name); + //fprintf(stderr, "event name: %s\n", event_name); }; load_video_error_check(false); diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp index 676618a..7c20568 100644 --- a/src/SearchBar.cpp +++ b/src/SearchBar.cpp @@ -99,6 +99,12 @@ namespace QuickMedia { append_text(std::string(clipboard.begin(), clipboard.end())); } + if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::D && event.key.control) { + clear(); + updated_search = true; + updated_autocomplete = true; + } + if(event.type == sf::Event::TextEntered && event.text.unicode != 8 && event.text.unicode != 127) // 8 = backspace, 127 = del onTextEntered(event.text.unicode); else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Backspace) @@ -124,6 +130,7 @@ namespace QuickMedia { if(backspace_pressed) timeout = 750; if(updated_search && elapsed_time >= timeout) { + fprintf(stderr, "update search!\n"); updated_search = false; auto u8 = text.getString().toUtf8(); std::string *u8_str = (std::string*)&u8; diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp index c005b31..7a84346 100644 --- a/src/plugins/NyaaSi.cpp +++ b/src/plugins/NyaaSi.cpp @@ -288,7 +288,9 @@ namespace QuickMedia { std::string *description = (std::string*)userdata; const char *text = quickmedia_html_node_get_text(node); if(description->empty() && text) { - *description = strip(text); + std::string desc = strip(text); + html_unescape_sequences(desc); + *description = std::move(desc); } }, &description); @@ -360,7 +362,9 @@ namespace QuickMedia { auto *item_data = (BodyItemContext*)userdata; const char *text = quickmedia_html_node_get_text(node); if(text && item_data->index < item_data->body_items->size()) { - (*item_data->body_items)[item_data->index]->set_description(strip(text)); + std::string desc = strip(text); + html_unescape_sequences(desc); + (*item_data->body_items)[item_data->index]->set_description(std::move(desc)); item_data->index++; } }, &body_item_image_context); diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 4add02c..f7d38aa 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -734,7 +734,7 @@ namespace QuickMedia { const Json::Value &author_is_channel_owner_json = comment_renderer_json["authorIsChannelOwner"]; if(author_is_channel_owner_json.isBool() && author_is_channel_owner_json.asBool()) - body_item->set_title_color(sf::Color(150, 255, 150)); + body_item->set_author_color(sf::Color(150, 255, 150)); std::optional<std::string> comment = yt_json_get_text(comment_renderer_json, "contentText"); if(comment) @@ -779,6 +779,9 @@ namespace QuickMedia { } PluginResult YoutubeCommentsPage::lazy_fetch(BodyItems &result_items) { + if(continuation_token.empty()) + return PluginResult::OK; + std::string next_url = "https://www.youtube.com/comment_service_ajax?action_get_comments=1&pbj=1&ctoken="; next_url += url_param_encode(continuation_token); //next_url += "&continuation="; @@ -858,6 +861,9 @@ namespace QuickMedia { } PluginResult YoutubeCommentRepliesPage::lazy_fetch(BodyItems &result_items) { + if(continuation_token.empty()) + return PluginResult::OK; + std::string next_url = "https://www.youtube.com/comment_service_ajax?action_get_comment_replies=1&pbj=1&ctoken="; next_url += url_param_encode(continuation_token); //next_url += "&continuation="; |