aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--TODO1
-rw-r--r--include/QuickMedia.hpp2
-rw-r--r--plugins/FileManager.hpp5
-rw-r--r--plugins/Saucenao.hpp5
-rw-r--r--src/QuickMedia.cpp101
-rw-r--r--src/plugins/FileManager.cpp2
-rw-r--r--src/plugins/Mangadex.cpp6
-rw-r--r--src/plugins/Matrix.cpp8
-rw-r--r--src/plugins/Saucenao.cpp12
10 files changed, 92 insertions, 59 deletions
diff --git a/README.md b/README.md
index 8dafdbf..9d1f325 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ Press `Ctrl + C` to copy the text of the selected item to the clipboard.\
Press `U` in matrix or in a 4chan thread to bring up the file manager to choose a file to upload.\
Press `Ctrl + V` to upload media to room in matrix if the clipboard contains a valid absolute filepath.\
Press `Ctrl + D` to remove the file that was previously selected with `U` in a 4chan thread.\
+Press `Ctrl + I` to reverse image search the selected image on 4chan.\
Press `Ctrl+Alt+Arrow up` / `Ctrl+Alt+Arrow down` or `Ctrl+Alt+K` / `Ctrl+Alt+J` to view the room above/below the selected room in matrix.
In matrix you can select a message with enter to open the url in the message (or if there are multiple urls then a menu will appear for selecting which to open).
@@ -66,14 +67,6 @@ In matrix you can select a message with enter to open the url in the message (or
`/leave` to leave the current room.\
`/me [text]` to send a message of type "m.emote".\
`/react [text]` to react to the selected message or if you are replying to message then it reacts to that message.
-## Mangadex
-To search for manga with mangadex, you need to be logged into mangadex in your browser and copy the `mangadex_rememberme_token` cookie from developer tools
-and store it in `$HOME/.config/quickmedia/credentials/mangadex.json` under the key `rememberme_token`. Here is an example what the file should look like:
-```
-{
- "rememberme_token": "21s9d3f7ad224a131239Dsfaf033029d2e390dAsfd3ldadb3a39dk43jfldj35das"
-}
-```
## Environment variables
Set `QM_PHONE_FACTOR=1` to disable the room list side panel in matrix.
## UI scaling
diff --git a/TODO b/TODO
index 847a185..289316d 100644
--- a/TODO
+++ b/TODO
@@ -20,7 +20,6 @@ Extract thumbnail from images that are being downloaded, while its downloading a
Add setting to disable sending typing events to the server (matrix).
Take code from dchat to support gifs (inline in text) and support animated webp (either animated or as a static thumbnail).
Scrolling in images still messes up the |current| page sometimes, need a way to fix this.
-Add ctrl+i keybind when viewing an image on 4chan to reverse image search it (using google, yandex and saucenao).
Show filename at the bottom when viewing an image/video on 4chan.
Add ctrl+c keybinding to copy the url of the previewing image.
Add ctrl+c keybiding to copy the url to the currently selected post on 4chan.
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index cea9b88..5d5f613 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -100,7 +100,7 @@ namespace QuickMedia {
Json::Value load_video_history_json();
private:
void init(Window parent_window);
- void load_plugin_by_name(std::vector<Tab> &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type);
+ void load_plugin_by_name(std::vector<Tab> &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler);
// Returns true if the window was closed
bool handle_window_close();
void base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_key_press = true, bool handle_searchbar = true);
diff --git a/plugins/FileManager.hpp b/plugins/FileManager.hpp
index 2e93a8e..72ac75d 100644
--- a/plugins/FileManager.hpp
+++ b/plugins/FileManager.hpp
@@ -11,10 +11,12 @@ namespace QuickMedia {
};
static const FileManagerMimeType FILE_MANAGER_MIME_TYPE_ALL = (FileManagerMimeType)(FILE_MANAGER_MIME_TYPE_IMAGE|FILE_MANAGER_MIME_TYPE_VIDEO|FILE_MANAGER_MIME_TYPE_OTHER);
+ // Return the tags to go to after selecting a file, or return an empty array to exit the program
+ using FileSelectionHandler = std::function<std::vector<Tab>()>;
class FileManagerPage : public Page {
public:
- FileManagerPage(Program *program, FileManagerMimeType mime_type = FILE_MANAGER_MIME_TYPE_ALL) : Page(program), current_dir("/"), mime_type(mime_type) {}
+ FileManagerPage(Program *program, FileManagerMimeType mime_type = FILE_MANAGER_MIME_TYPE_ALL, FileSelectionHandler selection_handler = nullptr) : Page(program), current_dir("/"), mime_type(mime_type), selection_handler(selection_handler) {}
const char* get_title() const override { return current_dir.c_str(); }
PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) override;
bool is_single_page() const override { return true; }
@@ -24,5 +26,6 @@ namespace QuickMedia {
private:
std::filesystem::path current_dir;
FileManagerMimeType mime_type;
+ FileSelectionHandler selection_handler;
};
} \ No newline at end of file
diff --git a/plugins/Saucenao.hpp b/plugins/Saucenao.hpp
index a5aadf9..297dde3 100644
--- a/plugins/Saucenao.hpp
+++ b/plugins/Saucenao.hpp
@@ -5,7 +5,7 @@
namespace QuickMedia {
class SaucenaoPage : public LazyFetchPage {
public:
- SaucenaoPage(Program *program, const std::string &upload_filepath) : LazyFetchPage(program), upload_filepath(upload_filepath) {}
+ SaucenaoPage(Program *program, const std::string &path, bool is_local) : LazyFetchPage(program), path(path), is_local(is_local) {}
const char* get_title() const override { return "SauceNAO"; }
PluginResult submit(const std::string&, const std::string&, std::vector<Tab>&) override {
return PluginResult::OK;
@@ -13,6 +13,7 @@ namespace QuickMedia {
PluginResult lazy_fetch(BodyItems &result_items) override;
bool is_single_page() const override { return true; }
private:
- std::string upload_filepath;
+ std::string path;
+ bool is_local;
};
} \ No newline at end of file
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 9593e80..4647787 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -335,9 +335,9 @@ namespace QuickMedia {
if(!thumbnail_url.empty()) {
body_item->thumbnail_url = thumbnail_url;
body_item->thumbnail_is_local = true;
- body_item->thumbnail_size.x = 32;
- body_item->thumbnail_size.y = 32;
}
+ body_item->thumbnail_size.x = 32;
+ body_item->thumbnail_size.y = 32;
return body_item;
}
@@ -414,7 +414,7 @@ namespace QuickMedia {
if(upscale_image_action != UpscaleImageAction::NO) {
if(!is_manga_plugin(plugin_name)) {
- fprintf(stderr, "Option --upscale-images/-upscale-images-force is only valid for manganelo, manganelos, mangatown and mangadex\n");
+ fprintf(stderr, "Option --upscale-images/-upscale-images-force is only valid for manga plugins\n");
return -2;
}
@@ -466,14 +466,21 @@ namespace QuickMedia {
int start_tab_index = 0;
FileManagerMimeType fm_mine_type = FILE_MANAGER_MIME_TYPE_ALL;
+ FileSelectionHandler file_selection_handler = nullptr;
+
+ FileSelectionHandler saucenao_file_selection_handler = [this]() {
+ std::vector<Tab> tabs;
+ tabs.push_back(Tab{create_body(), std::make_unique<SaucenaoPage>(this, selected_files[0], true), nullptr});
+ return tabs;
+ };
init(parent_window);
- bool is_saucenao = (strcmp(plugin_name, "saucenao") == 0);
- if(is_saucenao) {
+ if(strcmp(plugin_name, "saucenao") == 0) {
plugin_name = "file-manager";
fm_mine_type = FILE_MANAGER_MIME_TYPE_IMAGE;
+ file_selection_handler = std::move(saucenao_file_selection_handler);
}
- load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type);
+ load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type, std::move(file_selection_handler));
while(!tabs.empty() || matrix) {
if(matrix) {
@@ -493,10 +500,12 @@ namespace QuickMedia {
if(strcmp(plugin_name, "launcher") == 0) {
plugin_name = pipe_selected_text.c_str();
- load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type);
- } else if(strcmp(plugin_name, "file-manager") == 0 && is_saucenao && !selected_files.empty()) {
- plugin_name = "saucenao";
- load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type);
+ if(strcmp(plugin_name, "saucenao") == 0) {
+ plugin_name = "file-manager";
+ fm_mine_type = FILE_MANAGER_MIME_TYPE_IMAGE;
+ file_selection_handler = std::move(saucenao_file_selection_handler);
+ }
+ load_plugin_by_name(tabs, start_dir, start_tab_index, fm_mine_type, std::move(file_selection_handler));
}
}
@@ -884,7 +893,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- void Program::load_plugin_by_name(std::vector<Tab> &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type) {
+ void Program::load_plugin_by_name(std::vector<Tab> &tabs, const char *start_dir, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler) {
if(!plugin_name || plugin_name[0] == '\0')
return;
@@ -921,6 +930,7 @@ namespace QuickMedia {
pipe_body->items.push_back(create_launcher_body_item("Readm", "readm", resources_root + "icons/readm_launcher.png"));
pipe_body->items.push_back(create_launcher_body_item("Matrix", "matrix", resources_root + "icons/matrix_launcher.png"));
pipe_body->items.push_back(create_launcher_body_item("Nyaa.si", "nyaa.si", resources_root + "icons/nyaa_si_launcher.png"));
+ pipe_body->items.push_back(create_launcher_body_item("SauceNAO", "saucenao", ""));
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"));
@@ -996,7 +1006,7 @@ namespace QuickMedia {
boards_page->get_boards(boards_body->items);
tabs.push_back(Tab{std::move(boards_body), std::move(boards_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
} else if(strcmp(plugin_name, "file-manager") == 0) {
- auto file_manager_page = std::make_unique<FileManagerPage>(this, fm_mime_type);
+ auto file_manager_page = std::make_unique<FileManagerPage>(this, fm_mime_type, file_selection_handler);
if(start_dir && !file_manager_page->set_current_directory(start_dir)) {
fprintf(stderr, "Invalid directory provided with --dir: %s\n", start_dir);
exit_code = -3;
@@ -1009,8 +1019,6 @@ namespace QuickMedia {
auto pipe_body = create_body();
PipePage::load_body_items_from_stdin(pipe_body->items);
tabs.push_back(Tab{std::move(pipe_body), std::make_unique<PipePage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
- } else if(strcmp(plugin_name, "saucenao") == 0) {
- tabs.push_back(Tab{create_body(), std::make_unique<SaucenaoPage>(this, selected_files[0]), nullptr});
} else if(strcmp(plugin_name, "youtube") == 0) {
start_tab_index = 1;
tabs.push_back(Tab{create_body(), std::make_unique<YoutubeSubscriptionsPage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
@@ -1212,11 +1220,6 @@ namespace QuickMedia {
show_notification("QuickMedia", "Failed to create directory: " + content_storage_dir.data, Urgency::CRITICAL);
exit(1);
}
- Path credentials_storage_dir = get_storage_dir().join("credentials");
- if(create_directory_recursive(credentials_storage_dir) != 0) {
- show_notification("QuickMedia", "Failed to create directory: " + credentials_storage_dir.data, Urgency::CRITICAL);
- exit(1);
- }
// TODO: Make asynchronous
for_files_in_dir_sort_last_modified(content_storage_dir, [&history_items, plugin_name](const std::filesystem::path &filepath) {
// This can happen when QuickMedia crashes/is killed while writing to storage.
@@ -1320,6 +1323,7 @@ namespace QuickMedia {
void Program::select_file(const std::string &filepath) {
puts(filepath.c_str());
+ selected_files.clear();
selected_files.push_back(filepath);
}
@@ -1471,12 +1475,15 @@ namespace QuickMedia {
if(tabs[selected_tab].page->is_single_page()) {
if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->clear();
- if(new_tabs.size() == 1)
+ if(new_tabs.size() == 1 && !new_tabs[0].page) {
tabs[selected_tab].body = std::move(new_tabs[0].body);
- else
+ tabs[selected_tab].page->submit_body_item = prev_selected_item;
+ return;
+ } else if(new_tabs.empty()) {
loop_running = false;
- tabs[selected_tab].page->submit_body_item = prev_selected_item;
- return;
+ tabs[selected_tab].page->submit_body_item = prev_selected_item;
+ return;
+ }
}
if(new_tabs.empty()) {
@@ -2927,6 +2934,7 @@ namespace QuickMedia {
sf::Texture captcha_texture;
sf::Sprite captcha_sprite;
std::mutex captcha_image_mutex;
+ std::string attached_image_url;
auto attached_image_texture = std::make_unique<sf::Texture>();
sf::Sprite attached_image_sprite;
@@ -3153,21 +3161,21 @@ namespace QuickMedia {
video_content_page(thread_page, thread_page, selected_item->get_title(), true, thread_body->items, thread_body->get_selected_item());
redraw = true;
} else {
- load_image_future.cancel();
- downloading_image = true;
- navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE;
- load_image_future = AsyncTask<std::string>([&thread_body]() {
- std::string image_data;
- BodyItem *selected_item = thread_body->get_selected();
- if(!selected_item || selected_item->url.empty()) {
+ BodyItem *selected_item = thread_body->get_selected();
+ if(selected_item && !selected_item->url.empty()) {
+ load_image_future.cancel();
+ downloading_image = true;
+ navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE;
+ attached_image_url = selected_item->url;
+ load_image_future = AsyncTask<std::string>([attached_image_url]() {
+ std::string image_data;
+ if(download_to_string_cache(attached_image_url, image_data, {}) != DownloadResult::OK) {
+ show_notification("QuickMedia", "Failed to download image: " + attached_image_url, Urgency::CRITICAL);
+ image_data.clear();
+ }
return image_data;
- }
- if(download_to_string_cache(selected_item->url, image_data, {}) != DownloadResult::OK) {
- show_notification("QuickMedia", "Failed to download image: " + selected_item->url, Urgency::CRITICAL);
- image_data.clear();
- }
- return image_data;
- });
+ });
+ }
}
}
} else if(event.key.code == sf::Keyboard::U) {
@@ -3207,6 +3215,15 @@ namespace QuickMedia {
if(!clipboard.empty())
sf::Clipboard::setString(sf::String::fromUtf8(clipboard.begin(), clipboard.end()));
}
+ } else if(event.key.code == sf::Keyboard::I && event.key.control) {
+ BodyItem *selected_item = thread_body->get_selected();
+ if(selected_item && !selected_item->url.empty()) {
+ std::vector<Tab> saucenao_tabs;
+ saucenao_tabs.push_back(Tab{create_body(), std::make_unique<SaucenaoPage>(this, selected_item->url, false), nullptr});
+ page_loop(saucenao_tabs);
+ redraw = true;
+ frame_skip_text_entry = true;
+ }
}
BodyItem *selected_item = thread_body->get_selected();
@@ -3252,7 +3269,7 @@ namespace QuickMedia {
comment_input.move_caret_to_end();
}
- if(event.key.code == sf::Keyboard::I) {
+ if(event.key.code == sf::Keyboard::I && !event.key.control) {
frame_skip_text_entry = true;
navigation_stage = NavigationStage::REPLYING;
comment_input.set_editable(true);
@@ -3310,6 +3327,12 @@ namespace QuickMedia {
if(event.key.code == sf::Keyboard::Escape || event.key.code == sf::Keyboard::BackSpace) {
navigation_stage = NavigationStage::VIEWING_COMMENTS;
attached_image_texture.reset(new sf::Texture());
+ } else if(event.key.code == sf::Keyboard::I && event.key.control && !attached_image_url.empty()) {
+ std::vector<Tab> saucenao_tabs;
+ saucenao_tabs.push_back(Tab{create_body(), std::make_unique<SaucenaoPage>(this, attached_image_url, false), nullptr});
+ page_loop(saucenao_tabs);
+ redraw = true;
+ frame_skip_text_entry = true;
}
}
}
@@ -4885,7 +4908,7 @@ namespace QuickMedia {
chat_input.set_editable(false);
}
- if(event.key.code == sf::Keyboard::I) {
+ if(event.key.code == sf::Keyboard::I && !event.key.control) {
frame_skip_text_entry = true;
chat_input.set_editable(true);
chat_state = ChatState::TYPING_MESSAGE;
diff --git a/src/plugins/FileManager.cpp b/src/plugins/FileManager.cpp
index 47a8b57..52f9f4e 100644
--- a/src/plugins/FileManager.cpp
+++ b/src/plugins/FileManager.cpp
@@ -52,6 +52,8 @@ namespace QuickMedia {
if(std::filesystem::is_regular_file(new_path)) {
program->select_file(new_path);
+ if(selection_handler)
+ result_tabs = selection_handler();
return PluginResult::OK;
}
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 7332e21..f69484b 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -97,8 +97,6 @@ namespace QuickMedia {
if(!attributes_json.isObject())
continue;
- const Json::Value &description_json = attributes_json["description"];
-
const Json::Value &title_json = attributes_json["title"];
if(!title_json.isObject())
continue;
@@ -112,10 +110,6 @@ namespace QuickMedia {
auto body_item = BodyItem::create(std::move(title));
body_item->url = id_json.asString();
- if(description_json.isString()) {
- body_item->set_description(description_json.asString());
- body_item->set_description_color(sf::Color(179, 179, 179));
- }
result_items.push_back(std::move(body_item));
}
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 6f8b30b..2740a8a 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -1801,10 +1801,18 @@ namespace QuickMedia {
case ',':
case '@':
case ':':
+ case ';':
case '?':
case '!':
case '<':
case '>':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '\'':
+ case '"':
+ case '#':
case '\0':
return true;
default:
diff --git a/src/plugins/Saucenao.cpp b/src/plugins/Saucenao.cpp
index 302f25e..1278bed 100644
--- a/src/plugins/Saucenao.cpp
+++ b/src/plugins/Saucenao.cpp
@@ -4,8 +4,18 @@
namespace QuickMedia {
PluginResult SaucenaoPage::lazy_fetch(BodyItems &result_items) {
+ std::vector<CommandArg> additional_args;
+ if(is_local) {
+ additional_args.push_back({ "-F", "file=@" + path });
+ } else {
+ std::string url = path;
+ if(url[0] == '@')
+ url = "\\" + url;
+ additional_args.push_back({ "-F", "url=" + url });
+ }
+
std::string website_data;
- DownloadResult download_result = download_to_string("https://saucenao.com/search.php", website_data, {{ "-F", "file=@" + upload_filepath }}, true);
+ DownloadResult download_result = download_to_string("https://saucenao.com/search.php", website_data, std::move(additional_args), true);
if(download_result != DownloadResult::OK) return download_result_to_plugin_result(download_result);
QuickMediaHtmlSearch html_search;