aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-08-09 18:22:43 +0200
committerdec05eba <dec05eba@protonmail.com>2021-08-09 18:22:43 +0200
commit44bc399ccbd7e37107ae754db7da3d918229422d (patch)
treecf989a2699c7638328f292bcde49ed66ac013640
parentc2efd1e6587223cf9fff302fbc0ef80fcb4340e2 (diff)
Youtube: show search suggestions instead of immediate search
Fix save file dialog not showing all files after navigating to another directory if the search is not empty. Fix matrix system message deletion reverting back to use message (for avatar) and text color.
-rw-r--r--include/Body.hpp1
-rw-r--r--include/QuickMedia.hpp1
-rw-r--r--include/SearchBar.hpp10
-rw-r--r--plugins/MediaGeneric.hpp2
-rw-r--r--plugins/Page.hpp11
-rw-r--r--plugins/Youtube.hpp7
-rw-r--r--src/Body.cpp18
-rw-r--r--src/QuickMedia.cpp64
-rw-r--r--src/SearchBar.cpp86
-rw-r--r--src/plugins/MediaGeneric.cpp8
-rw-r--r--src/plugins/Youtube.cpp139
11 files changed, 171 insertions, 176 deletions
diff --git a/include/Body.hpp b/include/Body.hpp
index b44a386..23439ec 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -69,7 +69,6 @@ namespace QuickMedia {
void append_item(std::shared_ptr<BodyItem> body_item);
void append_items(BodyItems new_items);
void insert_item(std::shared_ptr<BodyItem> body_item, int index);
- void move_items_to(Body *other_body);
void move_item(size_t src_index, size_t dst_index);
// Returns the inserted position
size_t insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item);
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index 4399c3d..71e4797 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -68,6 +68,7 @@ namespace QuickMedia {
bool typing = false;
bool fetching_next_page_running = false;
bool fetching_next_page_failed = false;
+ bool search_suggestion_submitted = false;
int fetched_page = 0;
sf::Text search_result_text;
AsyncTask<FetchResult> fetch_future;
diff --git a/include/SearchBar.hpp b/include/SearchBar.hpp
index 027f534..a888ffb 100644
--- a/include/SearchBar.hpp
+++ b/include/SearchBar.hpp
@@ -18,7 +18,6 @@ namespace QuickMedia {
using TextUpdateCallback = std::function<void(const std::string &text)>;
using TextSubmitCallback = std::function<void(const std::string &text)>;
using TextBeginTypingCallback = std::function<void()>;
- using AutocompleteRequestCallback = std::function<void(const std::string &text)>;
class SearchBar {
public:
@@ -29,9 +28,6 @@ namespace QuickMedia {
void onWindowResize(const sf::Vector2f &window_size);
void clear();
void append_text(const std::string &text_to_add);
- bool is_cursor_at_start_of_line() const;
- void set_to_autocomplete();
- void set_autocomplete_text(const std::string &text);
void set_position(sf::Vector2f pos);
void set_editable(bool editable);
@@ -45,9 +41,7 @@ namespace QuickMedia {
TextUpdateCallback onTextUpdateCallback;
TextSubmitCallback onTextSubmitCallback;
TextBeginTypingCallback onTextBeginTypingCallback;
- AutocompleteRequestCallback onAutocompleteRequestCallback;
int text_autosearch_delay;
- int autocomplete_search_delay;
bool caret_visible;
float padding_top = 0.0f;
@@ -55,11 +49,8 @@ namespace QuickMedia {
float padding_x = 10.0f;
private:
void onTextEntered(sf::Uint32 codepoint);
- void clear_autocomplete_if_text_not_substring();
- void clear_autocomplete_if_last_char_not_substr();
private:
sf::Text text;
- sf::Text autocomplete_text;
RoundedRectangle background;
sf::RectangleShape shade;
sf::RectangleShape caret;
@@ -67,7 +58,6 @@ namespace QuickMedia {
std::string placeholder_str;
bool show_placeholder;
bool updated_search;
- bool updated_autocomplete;
bool draw_logo;
bool needs_update;
bool input_masked;
diff --git a/plugins/MediaGeneric.hpp b/plugins/MediaGeneric.hpp
index b1f9030..8885db2 100644
--- a/plugins/MediaGeneric.hpp
+++ b/plugins/MediaGeneric.hpp
@@ -84,7 +84,7 @@ namespace QuickMedia {
MediaGenericVideoPage(Program *program, MediaGenericSearchPage *search_page, const std::string &url) : VideoPage(program, url), search_page(search_page) {}
const char* get_title() const override { return ""; }
BodyItems get_related_media(const std::string &url) override;
- std::unique_ptr<Page> create_search_page(Program *program, int &search_delay) override;
+ bool create_search_page(Program *program, Tab &tab) override;
std::unique_ptr<RelatedVideosPage> create_related_videos_page(Program *program) override;
std::unique_ptr<Page> create_channels_page(Program*, const std::string&) override {
return nullptr;
diff --git a/plugins/Page.hpp b/plugins/Page.hpp
index c61fd6d..9d5bade 100644
--- a/plugins/Page.hpp
+++ b/plugins/Page.hpp
@@ -29,6 +29,9 @@ namespace QuickMedia {
virtual bool search_is_filter() { return true; }
// This show be overriden if search_is_filter is overriden to return false
virtual SearchResult search(const std::string &str, BodyItems &result_items) { (void)str; (void)result_items; return SearchResult::ERR; }
+ // If this returns true then |submit_suggestion| is called when submitting the selected item instead of |submit|
+ // and |submit| is called when submitting the response of |submit_suggestion|
+ virtual bool search_is_suggestion() { return false; }
// Return empty |result_tabs| and PluginResult::OK to do nothing; which is useful for implementing custom actions on item submit
virtual PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
@@ -40,6 +43,12 @@ namespace QuickMedia {
// Override and return false to make submit run in the main (ui) thread
virtual bool submit_is_async() { return true; }
virtual bool clear_search_after_submit() { return false; }
+ virtual PluginResult submit_suggestion(const std::string &title, const std::string &url, BodyItems &result_items) {
+ (void)title;
+ (void)url;
+ (void)result_items;
+ return PluginResult::ERR;
+ }
// Note: If pagination is done by fetching the next page until we get to |page|, then the "current page" should be reset everytime |search| is called.
// Note: the first page is 0
virtual PluginResult get_page(const std::string &str, int page, BodyItems &result_items) { (void)str; (void)page; (void)result_items; return PluginResult::OK; }
@@ -110,7 +119,7 @@ namespace QuickMedia {
virtual PageTypez get_type() const override { return PageTypez::VIDEO; }
virtual bool autoplay_next_item() { return false; }
virtual BodyItems get_related_media(const std::string &url) { (void)url; return {}; }
- virtual std::unique_ptr<Page> create_search_page(Program *program, int &search_delay) { (void)program; (void)search_delay; return nullptr; }
+ virtual bool create_search_page(Program *program, Tab &tab) { (void)program; (void)tab; return false; }
virtual std::unique_ptr<Page> create_comments_page(Program *program) { (void)program; return nullptr; }
// Return nullptr if the service doesn't support related videos page
virtual std::unique_ptr<RelatedVideosPage> create_related_videos_page(Program *program) = 0;
diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp
index 50412de..c7aff93 100644
--- a/plugins/Youtube.hpp
+++ b/plugins/Youtube.hpp
@@ -30,17 +30,20 @@ namespace QuickMedia {
class YoutubeSearchPage : public LazyFetchPage {
public:
- YoutubeSearchPage(Program *program) : LazyFetchPage(program) {}
+ YoutubeSearchPage(Program *program, std::string video_id = "") : LazyFetchPage(program), video_id(std::move(video_id)) {}
const char* get_title() const override { return "Search"; }
+ bool search_is_suggestion() override { return true; }
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<Tab> &result_tabs) override;
+ PluginResult submit_suggestion(const std::string &title, const std::string &url, BodyItems &result_items) override;
PluginResult lazy_fetch(BodyItems &result_items) override;
bool lazy_fetch_is_loader() override { return true; }
private:
PluginResult search_get_continuation(const std::string &url, const std::string &continuation_token, BodyItems &result_items);
private:
+ std::string video_id;
std::string search_url;
std::string continuation_token;
int current_page = 0;
@@ -138,7 +141,7 @@ namespace QuickMedia {
YoutubeVideoPage(Program *program, std::string url);
const char* get_title() const override { return ""; }
BodyItems get_related_media(const std::string &url) override;
- std::unique_ptr<Page> create_search_page(Program *program, int &search_delay) override;
+ bool create_search_page(Program *program, Tab &tab) override;
std::unique_ptr<Page> create_comments_page(Program *program) override;
std::unique_ptr<RelatedVideosPage> create_related_videos_page(Program *program) override;
std::unique_ptr<Page> create_channels_page(Program *program, const std::string &channel_url) override;
diff --git a/src/Body.cpp b/src/Body.cpp
index 766542a..c3cbcda 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -261,6 +261,11 @@ namespace QuickMedia {
filter_search_fuzzy_item(current_filter, item.get());
}
this->items = std::move(items);
+ if(attach_side == AttachSide::TOP) {
+ selected_item = 0;
+ prev_selected_item = selected_item;
+ page_scroll = 0.0f;
+ }
}
void Body::clear_items() {
@@ -299,10 +304,6 @@ namespace QuickMedia {
items.insert(items.begin() + index, std::move(body_item));
}
- void Body::move_items_to(Body *other_body) {
- other_body->set_items(std::move(items));
- }
-
void Body::move_item(size_t src_index, size_t dst_index) {
assert(src_index < items.size());
assert(dst_index < items.size());
@@ -1661,11 +1662,20 @@ namespace QuickMedia {
}
void Body::filter_search_fuzzy_item(const std::string &text, BodyItem *body_item) {
+ const bool prev_visible = body_item->visible;
+
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_fuzzy_case_insensitive(body_item->get_description(), text);
if(!body_item->visible && !body_item->get_author().empty())
body_item->visible = string_find_fuzzy_case_insensitive(body_item->get_author(), text);
+
+ if(prev_visible && !body_item->visible) {
+ clear_body_item_cache(body_item);
+ // TODO: Make sure the embedded item is not referencing another item in the |items| list
+ if(body_item->embedded_item)
+ clear_body_item_cache(body_item->embedded_item.get());
+ }
}
bool Body::no_items_visible() const {
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 52452d4..dc23892 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1222,7 +1222,7 @@ namespace QuickMedia {
if(youtube_url.empty()) {
start_tab_index = 1;
tabs.push_back(Tab{create_body(false, true), std::make_unique<YoutubeSubscriptionsPage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
- tabs.push_back(Tab{create_body(false, true), std::make_unique<YoutubeSearchPage>(this), create_search_bar("Search...", 350)});
+ tabs.push_back(Tab{create_body(false, false), std::make_unique<YoutubeSearchPage>(this), create_search_bar("Search...", 100)});
auto recommended_page = std::make_unique<YoutubeRecommendedPage>(this);
tabs.push_back(Tab{create_body(false, true), std::move(recommended_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
@@ -1708,9 +1708,6 @@ namespace QuickMedia {
tab_associated_data.push_back(std::move(data));
}
- //std::string autocomplete_text;
- //bool autocomplete_running = false;
-
double gradient_inc = 0.0;
const float gradient_height = 5.0f;
@@ -1733,12 +1730,19 @@ namespace QuickMedia {
hide_virtual_keyboard();
std::vector<Tab> new_tabs;
+ BodyItems new_body_items;
+ const bool search_suggestion_submitted = tab_associated_data[selected_tab].search_suggestion_submitted;
auto prev_selected_item = tabs[selected_tab].page->submit_body_item;
tabs[selected_tab].page->submit_body_item = selected_item;
- auto plugin_submit_handler = [&tabs, selected_tab, &selected_item, &search_text, &new_tabs]() {
- PluginResult plugin_result = tabs[selected_tab].page->submit(selected_item ? selected_item->get_title() : search_text, selected_item ? selected_item->url : search_text, new_tabs);
- return plugin_result == PluginResult::OK;
+ auto plugin_submit_handler = [&tabs, selected_tab, &selected_item, &search_text, &new_tabs, &new_body_items, search_suggestion_submitted]() {
+ if(tabs[selected_tab].page->search_is_suggestion() && !search_suggestion_submitted) {
+ PluginResult plugin_result = tabs[selected_tab].page->submit_suggestion(selected_item ? selected_item->get_title() : search_text, selected_item ? selected_item->url : search_text, new_body_items);
+ return plugin_result == PluginResult::OK;
+ } else {
+ PluginResult plugin_result = tabs[selected_tab].page->submit(selected_item ? selected_item->get_title() : search_text, selected_item ? selected_item->url : search_text, new_tabs);
+ return plugin_result == PluginResult::OK;
+ }
};
TaskResult submit_result;
@@ -1769,11 +1773,11 @@ namespace QuickMedia {
}
}
- if(tabs[selected_tab].page->is_single_page()) {
+ if(tabs[selected_tab].page->is_single_page() && !tabs[selected_tab].page->search_is_suggestion()) {
if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->clear();
if(new_tabs.size() == 1 && !new_tabs[0].page) {
tabs[selected_tab].body = std::move(new_tabs[0].body);
- tabs[selected_tab].page->submit_body_item = prev_selected_item;
+ tabs[selected_tab].page->submit_body_item = nullptr;
return;
} else if(new_tabs.empty()) {
loop_running = false;
@@ -1782,6 +1786,15 @@ namespace QuickMedia {
}
}
+ if(tabs[selected_tab].page->search_is_suggestion() && !search_suggestion_submitted) {
+ tabs[selected_tab].body->set_items(std::move(new_body_items));
+ tabs[selected_tab].page->submit_body_item = nullptr;
+ tab_associated_data[selected_tab].search_suggestion_submitted = true;
+ if(tabs[selected_tab].search_bar)
+ tabs[selected_tab].search_bar->clear();
+ return;
+ }
+
if(new_tabs.empty()) {
tabs[selected_tab].page->submit_body_item = prev_selected_item;
return;
@@ -1909,6 +1922,7 @@ namespace QuickMedia {
&& !tab_associated_data[selected_tab].fetching_next_page_failed
&& (!tabs[selected_tab].search_bar || !tabs[selected_tab].page->search_is_filter() || tabs[selected_tab].search_bar->is_empty())
&& tabs[selected_tab].body->get_num_visible_items() > 0
+ && (!tabs[selected_tab].page->search_is_suggestion() || tab_associated_data[selected_tab].search_suggestion_submitted)
&& tabs[selected_tab].page->is_ready()
&& (!tabs[selected_tab].page->is_lazy_fetch_page() || tab_associated_data[selected_tab].lazy_fetch_finished))
{
@@ -1940,12 +1954,6 @@ namespace QuickMedia {
TabAssociatedData &associated_data = tab_associated_data[i];
if(tab.search_bar) {
- // tab.search_bar->autocomplete_search_delay = current_plugin->get_autocomplete_delay();
- // tab.search_bar->onAutocompleteRequestCallback = [this, &tabs, &selected_tab, &autocomplete_text](const std::string &text) {
- // if(tabs[selected_tab].body == body && !current_plugin->search_is_filter())
- // autocomplete_text = text;
- // };
-
tab.search_bar->onTextUpdateCallback = [&associated_data, &tabs, i](const std::string &text) {
if(!tabs[i].page->search_is_filter()) {
associated_data.update_search_text = text;
@@ -2000,8 +2008,6 @@ namespace QuickMedia {
else if(event.type == sf::Event::KeyPressed) {
if(event.key.code == sf::Keyboard::Escape) {
return false;
- } else if(event.key.code == sf::Keyboard::Tab) {
- if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->set_to_autocomplete();
} else if(event.key.code == sf::Keyboard::Enter) {
if(!tabs[selected_tab].search_bar) {
BodyItem *selected_item = tabs[selected_tab].body->get_selected();
@@ -2162,11 +2168,13 @@ namespace QuickMedia {
if(associated_data.search_text_updated && associated_data.fetch_status == FetchStatus::NONE && !associated_data.fetching_next_page_running) {
std::string update_search_text = associated_data.update_search_text;
+ if(!tabs[i].page->search_is_suggestion() || associated_data.search_suggestion_submitted)
+ tabs[i].body->clear_items();
associated_data.search_text_updated = false;
- tabs[i].body->clear_items();
associated_data.fetch_status = FetchStatus::LOADING;
associated_data.fetch_type = FetchType::SEARCH;
associated_data.search_result_text.setString("Searching...");
+ associated_data.search_suggestion_submitted = false;
Page *page = tabs[i].page.get();
associated_data.fetch_future = AsyncTask<FetchResult>([update_search_text, page]() {
FetchResult fetch_result;
@@ -2937,18 +2945,18 @@ namespace QuickMedia {
window.setMouseCursorVisible(true);
cursor_visible = true;
- int search_delay = 0;
- auto search_page = video_page->create_search_page(this, search_delay);
+ Tab search_page_tab;
+ const bool search_page_created = video_page->create_search_page(this, search_page_tab);
auto comments_page = video_page->create_comments_page(this);
auto related_videos_page = video_page->create_related_videos_page(this);
auto channels_page = video_page->create_channels_page(this, channel_url);
- if(search_page || related_videos_page || channels_page) {
+ if(search_page_created || related_videos_page || channels_page) {
XUnmapWindow(disp, video_player_window);
XSync(disp, False);
std::vector<Tab> tabs;
- if(search_page) {
- tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", search_delay)});
+ if(search_page_created) {
+ tabs.push_back(std::move(search_page_tab));
}
if(comments_page) {
tabs.push_back(Tab{create_body(), std::move(comments_page), nullptr});
@@ -4755,12 +4763,15 @@ namespace QuickMedia {
//message->related_event_id.clear();
//message->related_event_type = RelatedEventType::NONE;
Message *original_message = static_cast<Message*>(body_item->userdata);
- if(original_message) {
+ if(original_message && !is_system_message_type(original_message->type)) {
body_item->thumbnail_url = current_room->get_user_avatar_url(original_message->user);
body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE;
}
body_item->set_description("Message deleted");
- body_item->set_description_color(get_current_theme().text_color);
+ if(original_message && is_system_message_type(original_message->type))
+ body_item->set_description_color(get_current_theme().faded_text_color);
+ else
+ body_item->set_description_color(get_current_theme().text_color);
body_item->thumbnail_size = AVATAR_THUMBNAIL_SIZE;
body_item->url.clear();
};
@@ -6995,8 +7006,7 @@ namespace QuickMedia {
if(task_result == TaskResult::TRUE) {
if(!new_tabs.empty()) {
- new_tabs[0].body->move_items_to(file_manager_body.get());
- file_manager_body->select_first_item();
+ file_manager_body = std::move(new_tabs[0].body);
search_bar->clear();
}
} else if(task_result == TaskResult::FALSE) {
diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp
index 5909635..c1f9ceb 100644
--- a/src/SearchBar.cpp
+++ b/src/SearchBar.cpp
@@ -21,17 +21,13 @@ namespace QuickMedia {
onTextUpdateCallback(nullptr),
onTextSubmitCallback(nullptr),
onTextBeginTypingCallback(nullptr),
- onAutocompleteRequestCallback(nullptr),
text_autosearch_delay(50),
- autocomplete_search_delay(250),
caret_visible(true),
text(placeholder, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(16 * get_ui_scale())),
- autocomplete_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(16 * get_ui_scale())),
background(sf::Vector2f(1.0f, 1.0f), 10.0f, get_current_theme().selected_color, rounded_rectangle_shader),
placeholder_str(placeholder),
show_placeholder(true),
updated_search(false),
- updated_autocomplete(false),
draw_logo(false),
needs_update(true),
input_masked(input_masked),
@@ -44,7 +40,6 @@ namespace QuickMedia {
padding_top = padding_top_default;
padding_bottom = padding_bottom_default;
text.setFillColor(get_current_theme().placeholder_text_color);
- autocomplete_text.setFillColor(get_current_theme().placeholder_text_color);
shade.setFillColor(get_current_theme().shade_color);
if(plugin_logo && plugin_logo->getNativeHandle() != 0)
plugin_logo_sprite.setTexture(*plugin_logo, true);
@@ -65,8 +60,7 @@ namespace QuickMedia {
window.draw(shade);
background.draw(window);
- // TODO: Render starting from the character after text length
- window.draw(autocomplete_text);
+
if(input_masked && !show_placeholder) {
std::string masked_str(text.getString().getSize(), '*');
sf::Text masked_text(std::move(masked_str), *text.getFont(), text.getCharacterSize());
@@ -117,7 +111,6 @@ namespace QuickMedia {
if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::D && event.key.control) {
clear();
updated_search = true;
- updated_autocomplete = true;
time_since_search_update.restart();
}
@@ -144,13 +137,6 @@ namespace QuickMedia {
if(onTextUpdateCallback)
onTextUpdateCallback(*u8_str);
typing = false;
- } else if(updated_autocomplete && elapsed_time >= autocomplete_search_delay) {
- updated_autocomplete = false;
- if(!show_placeholder && onAutocompleteRequestCallback) {
- auto u8 = text.getString().toUtf8();
- std::string *u8_str = (std::string*)&u8;
- onAutocompleteRequestCallback(*u8_str);
- }
}
}
@@ -184,7 +170,6 @@ namespace QuickMedia {
background.set_position(sf::Vector2f(pos.x + offset_x, pos.y + padding_top));
shade.setPosition(pos);
sf::Vector2f font_position(std::floor(pos.x + offset_x + background_margin_horizontal), std::floor(pos.y + padding_top + background_margin_vertical));
- autocomplete_text.setPosition(font_position);
text.setPosition(font_position);
}
@@ -201,9 +186,6 @@ namespace QuickMedia {
show_placeholder = true;
text.setString(placeholder_str);
text.setFillColor(get_current_theme().placeholder_text_color);
- autocomplete_text.setString("");
- } else {
- clear_autocomplete_if_text_not_substring();
}
if(!updated_search) {
typing = true;
@@ -211,7 +193,6 @@ namespace QuickMedia {
onTextBeginTypingCallback();
}
updated_search = true;
- updated_autocomplete = true;
time_since_search_update.restart();
}
} else if(codepoint == 13) { // Return
@@ -235,10 +216,8 @@ namespace QuickMedia {
show_placeholder = true;
text.setString(placeholder_str);
text.setFillColor(get_current_theme().placeholder_text_color);
- autocomplete_text.setString("");
needs_update = true;
updated_search = false;
- updated_autocomplete = false;
backspace_pressed = false;
}
@@ -256,51 +235,18 @@ namespace QuickMedia {
str += text_to_add;
text.setString(str);
- clear_autocomplete_if_text_not_substring();
if(!updated_search) {
typing = true;
if(onTextBeginTypingCallback)
onTextBeginTypingCallback();
}
updated_search = true;
- updated_autocomplete = true;
time_since_search_update.restart();
backspace_pressed = false;
if(text_to_add.find('\n') != std::string::npos)
needs_update = true;
}
- bool SearchBar::is_cursor_at_start_of_line() const {
- // TODO: When it's possible to move the cursor, then check at the cursor position instead of end of the string
- const sf::String &str = text.getString();
- return show_placeholder || str.getSize() == 0 || str[str.getSize() - 1] == '\n';
- }
-
- void SearchBar::set_to_autocomplete() {
- const sf::String &autocomplete_str = autocomplete_text.getString();
- if(!autocomplete_str.isEmpty()) {
- if(show_placeholder) {
- show_placeholder = false;
- text.setString("");
- text.setFillColor(sf::Color::White);
- }
- text.setString(autocomplete_str);
- if(!updated_search) {
- typing = true;
- if(onTextBeginTypingCallback)
- onTextBeginTypingCallback();
- }
- updated_search = true;
- updated_autocomplete = true;
- time_since_search_update.restart();
- needs_update = true;
- }
- }
-
- void SearchBar::set_autocomplete_text(const std::string &text) {
- autocomplete_text.setString(text);
- }
-
void SearchBar::set_position(sf::Vector2f pos) {
if(std::abs(this->pos.x - pos.x) > 1.0f || std::abs(this->pos.y - pos.y) > 1.0f) {
this->pos = pos;
@@ -316,36 +262,6 @@ namespace QuickMedia {
return editable;
}
- void SearchBar::clear_autocomplete_if_text_not_substring() {
- const sf::String &text_str = text.getString();
- const sf::String &autocomplete_str = autocomplete_text.getString();
- if(text_str.getSize() > autocomplete_str.getSize()) {
- autocomplete_text.setString("");
- return;
- }
-
- for(size_t i = 0; i < autocomplete_str.getSize(); ++i) {
- if(text_str[i] != autocomplete_str[i]) {
- autocomplete_text.setString("");
- return;
- }
- }
- }
-
- void SearchBar::clear_autocomplete_if_last_char_not_substr() {
- const sf::String &text_str = text.getString();
- const sf::String &autocomplete_str = autocomplete_text.getString();
- if(text_str.isEmpty() || text_str.getSize() > autocomplete_str.getSize()) {
- autocomplete_text.setString("");
- return;
- }
-
- if(autocomplete_str[text_str.getSize() - 1] != text_str[text_str.getSize() - 1]) {
- autocomplete_text.setString("");
- return;
- }
- }
-
float SearchBar::getBottom() const {
return getBottomWithoutShadow() + 5.0f;//background_shadow.getSize().y;
}
diff --git a/src/plugins/MediaGeneric.cpp b/src/plugins/MediaGeneric.cpp
index c829a33..d536a09 100644
--- a/src/plugins/MediaGeneric.cpp
+++ b/src/plugins/MediaGeneric.cpp
@@ -191,9 +191,11 @@ namespace QuickMedia {
return result_items;
}
- std::unique_ptr<Page> MediaGenericVideoPage::create_search_page(Program*, int &search_delay) {
- search_delay = 500; // TODO: Make configurable?
- return std::make_unique<MediaGenericSearchPage>(*search_page);
+ bool MediaGenericVideoPage::create_search_page(Program*, Tab &tab) {
+ tab.body = create_body(false, true);
+ tab.page = std::make_unique<MediaGenericSearchPage>(*search_page);
+ tab.search_bar = create_search_bar("Search...", 500); // TODO: Make search delay configurable?
+ return true;
}
std::unique_ptr<RelatedVideosPage> MediaGenericVideoPage::create_related_videos_page(Program *program) {
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index ea72f03..5b0591c 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -764,12 +764,98 @@ namespace QuickMedia {
}
SearchResult YoutubeSearchPage::search(const std::string &str, BodyItems &result_items) {
+ if(str.empty())
+ return SearchResult::OK;
+
+ // TODO: Find this search url from youtube.com/... searchbox.js, and the url to that script from youtube.com/ html
+ std::string url = "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&hl=en&gl=us&sugexp=rdcfrc%2Ccfro%3D1%2Cfp.cfr%3D1&gs_rn=64&gs_ri=youtube&ds=yt&cp=4&gs_id=f&xhr=t&xssi=t&q=";
+ url += url_param_encode(str);
+ if(!video_id.empty())
+ url += "&video_id=" + video_id;
+
+ std::vector<CommandArg> additional_args = {
+ { "-H", "origin: https://www.youtube.com" },
+ { "-H", "referer: https://www.youtube.com/" }
+ };
+
+ std::vector<CommandArg> cookies = get_cookies();
+ additional_args.insert(additional_args.end(), cookies.begin(), cookies.end());
+
+ std::string website_data;
+ DownloadResult result = download_to_string(url, website_data, std::move(additional_args), true);
+ if(result != DownloadResult::OK) return download_result_to_search_result(result);
+
+ const size_t json_start_index = website_data.find('[');
+ if(json_start_index == std::string::npos)
+ return SearchResult::ERR;
+
+ Json::Value json_root;
+ Json::CharReaderBuilder json_builder;
+ std::unique_ptr<Json::CharReader> json_reader(json_builder.newCharReader());
+ std::string json_errors;
+ if(!json_reader->parse(&website_data[json_start_index], &website_data[website_data.size()], &json_root, &json_errors)) {
+ fprintf(stderr, "youtube search error: %s\n", json_errors.c_str());
+ return SearchResult::ERR;
+ }
+
+ if(!json_root.isArray() || json_root.size() < 2)
+ return SearchResult::ERR;
+
+ const Json::Value &search_result_list_json = json_root[1];
+ if(!search_result_list_json.isArray())
+ return SearchResult::ERR;
+
+ for(const Json::Value &json_item : search_result_list_json) {
+ if(!json_item.isArray() || json_item.size() == 0)
+ continue;
+
+ const Json::Value &search_result_json = json_item[0];
+ if(!search_result_json.isString())
+ continue;
+
+ auto body_item = BodyItem::create(search_result_json.asString());
+ body_item->url = body_item->get_title();
+ result_items.push_back(std::move(body_item));
+ }
+
+ if(result_items.empty() || !strcase_equals(str.c_str(), result_items.front()->get_title().c_str())) {
+ auto body_item = BodyItem::create(str);
+ body_item->url = str;
+ result_items.insert(result_items.begin(), std::move(body_item));
+ }
+
+ return SearchResult::OK;
+ }
+
+ PluginResult YoutubeSearchPage::get_page(const std::string&, int page, BodyItems &result_items) {
+ while(current_page < page) {
+ PluginResult plugin_result = search_get_continuation(search_url, continuation_token, result_items);
+ if(plugin_result != PluginResult::OK) return plugin_result;
+ ++current_page;
+ }
+ return PluginResult::OK;
+ }
+
+ PluginResult YoutubeSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
+ if(url.empty())
+ return PluginResult::OK;
+
+ if(strncmp(url.c_str(), "https://www.youtube.com/channel/", 32) == 0) {
+ // TODO: Make all pages (for all services) lazy fetch in a similar manner!
+ result_tabs.push_back(Tab{create_body(false, true), std::make_unique<YoutubeChannelPage>(program, url, "", title), create_search_bar("Search...", 350)});
+ } else {
+ result_tabs.push_back(Tab{nullptr, std::make_unique<YoutubeVideoPage>(program, url), nullptr});
+ }
+ return PluginResult::OK;
+ }
+
+ PluginResult YoutubeSearchPage::submit_suggestion(const std::string&, const std::string &url, BodyItems &result_items) {
continuation_token.clear();
current_page = 0;
added_videos.clear();
search_url = "https://www.youtube.com/results?search_query=";
- search_url += url_param_encode(str);
+ search_url += url_param_encode(url);
std::vector<CommandArg> additional_args = {
{ "-H", "x-spf-referer: " + search_url },
@@ -783,10 +869,10 @@ namespace QuickMedia {
Json::Value json_root;
DownloadResult result = download_json(json_root, search_url + "&pbj=1&gl=US&hl=en", std::move(additional_args), true);
- if(result != DownloadResult::OK) return download_result_to_search_result(result);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
if(!json_root.isArray())
- return SearchResult::ERR;
+ return PluginResult::ERR;
for(const Json::Value &json_item : json_root) {
if(!json_item.isObject())
@@ -811,28 +897,6 @@ namespace QuickMedia {
parse_section_list_renderer(primary_contents_json["sectionListRenderer"], continuation_token, result_items, added_videos);
}
- return SearchResult::OK;
- }
-
- PluginResult YoutubeSearchPage::get_page(const std::string&, int page, BodyItems &result_items) {
- while(current_page < page) {
- PluginResult plugin_result = search_get_continuation(search_url, continuation_token, result_items);
- if(plugin_result != PluginResult::OK) return plugin_result;
- ++current_page;
- }
- return PluginResult::OK;
- }
-
- PluginResult YoutubeSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- if(url.empty())
- return PluginResult::OK;
-
- if(strncmp(url.c_str(), "https://www.youtube.com/channel/", 32) == 0) {
- // TODO: Make all pages (for all services) lazy fetch in a similar manner!
- result_tabs.push_back(Tab{create_body(false, true), std::make_unique<YoutubeChannelPage>(program, url, "", title), create_search_bar("Search...", 350)});
- } else {
- result_tabs.push_back(Tab{nullptr, std::make_unique<YoutubeVideoPage>(program, url), nullptr});
- }
return PluginResult::OK;
}
@@ -2061,9 +2125,14 @@ namespace QuickMedia {
return result_items;
}
- std::unique_ptr<Page> YoutubeVideoPage::create_search_page(Program *program, int &search_delay) {
- search_delay = 350;
- return std::make_unique<YoutubeSearchPage>(program);
+ bool YoutubeVideoPage::create_search_page(Program *program, Tab &tab) {
+ std::string video_id;
+ youtube_url_extract_id(url, video_id);
+
+ tab.body = create_body(false, false);
+ tab.page = std::make_unique<YoutubeSearchPage>(program, std::move(video_id));
+ tab.search_bar = create_search_bar("Search...", 100);
+ return true;
}
std::unique_ptr<Page> YoutubeVideoPage::create_comments_page(Program *program) {
@@ -2095,20 +2164,6 @@ namespace QuickMedia {
return result;
}
- static std::string url_extract_param(const std::string &url, const std::string &param) {
- std::string param_s = param + "=";
- size_t index = url.find(param_s);
- if(index == std::string::npos)
- return "";
-
- index += param_s.size();
- size_t end = url.find('&', index);
- if(end == std::string::npos)
- end = url.size();
-
- return url.substr(index, end - index);
- }
-
static const YoutubeVideoFormat* get_highest_resolution_mp4_non_av1(const std::vector<YoutubeVideoFormat> &video_formats, int max_height) {
for(const YoutubeVideoFormat &video_format : video_formats) {
if(video_format.height <= max_height && video_format.base.mime_type.find("mp4") != std::string::npos && video_format.base.mime_type.find("av01") == std::string::npos)