aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)