From 78f01f5d041ba291aca05f768bb71eba71ebb58e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 16 Apr 2021 23:36:37 +0200 Subject: Add a combined manga search page, highlight urls ending with ? --- README.md | 2 +- TODO | 3 ++- plugins/MangaCombined.hpp | 27 +++++++++++++++++++++ plugins/Manganelo.hpp | 1 - src/NetUtils.cpp | 6 ++--- src/QuickMedia.cpp | 36 +++++++++++++++++++++++----- src/plugins/MangaCombined.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++ tests/main.cpp | 5 ++++ 8 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 plugins/MangaCombined.hpp create mode 100644 src/plugins/MangaCombined.cpp diff --git a/README.md b/README.md index 771c3d0..0368766 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Cache is stored under `$HOME/.cache/quickmedia`. ``` usage: quickmedia [--use-system-mpv-config] [--dir ] [-e ] OPTIONS: - plugin The plugin to use. Should be either launcher, 4chan, manganelo, manganelos, mangatown, mangakatana, mangadex, youtube, spotify, soundcloud, nyaa.si, matrix, file-manager or stdin + plugin The plugin to use. Should be either launcher, 4chan, manga, manganelo, manganelos, mangatown, mangakatana, mangadex, youtube, spotify, soundcloud, nyaa.si, matrix, file-manager or stdin --no-video Only play audio when playing a video. Disabled by default --use-system-mpv-config Use system mpv config instead of no config. Disabled by default --upscale-images Upscale low-resolution manga pages using waifu2x-ncnn-vulkan. Disabled by default diff --git a/TODO b/TODO index b29e434..c1d6fc9 100644 --- a/TODO +++ b/TODO @@ -133,4 +133,5 @@ Convert nyaa.si/spotify/soundcloud date from ISO date string to local time. When ui is scaled then the predicted thumbnail size will be wrong since its scaled in Body but not in the plugins where they are requested. Check if get_page handlers in pages need to check if next batch is valid. If the server returns empty next batch we shouldn't fetch the first page... Cloudflare kicks in when downloading manga on manganelo.. figure out a way to bypass it. This doesn't seem to happen when using python requests as is done in AutoMedia. -Replace cppcodec with another library for base64 url encoding/decoding. Its way too large for what it does. \ No newline at end of file +Replace cppcodec with another library for base64 url encoding/decoding. Its way too large for what it does. +Add a combined manga plugin that allows you to search for manga across all manga plugins. \ No newline at end of file diff --git a/plugins/MangaCombined.hpp b/plugins/MangaCombined.hpp new file mode 100644 index 0000000..b9627fe --- /dev/null +++ b/plugins/MangaCombined.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "Manga.hpp" +#include +#include + +namespace QuickMedia { + struct MangaPlugin { + std::unique_ptr page; + std::string title; + std::string service_name; + std::string logo_path; + }; + + class MangaCombinedSearchPage : public Page { + public: + MangaCombinedSearchPage(Program *program, std::vector search_pages); + const char* get_title() const override { return "All"; } + bool search_is_filter() override { return false; } + SearchResult search(const std::string &str, BodyItems &result_items) override; + PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; + PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; + sf::Vector2i get_thumbnail_max_size() override { return sf::Vector2i(101, 141); }; + private: + std::vector search_pages; + }; +} \ No newline at end of file diff --git a/plugins/Manganelo.hpp b/plugins/Manganelo.hpp index 530a1f2..ca44e05 100644 --- a/plugins/Manganelo.hpp +++ b/plugins/Manganelo.hpp @@ -1,7 +1,6 @@ #pragma once #include "Manga.hpp" -#include namespace QuickMedia { class ManganeloSearchPage : public Page { diff --git a/src/NetUtils.cpp b/src/NetUtils.cpp index 8bb5a0e..3539d46 100644 --- a/src/NetUtils.cpp +++ b/src/NetUtils.cpp @@ -1617,12 +1617,12 @@ namespace QuickMedia { contains_dot = true; } - if(url_start != std::string::npos && !is_valid_url && contains_dot && (is_whitespace(c) || c == '/' || c == ',' || c == ':' || c == ')' || c == '\0' || (c == '.' && i == str.size()))) { + if(url_start != std::string::npos && !is_valid_url && contains_dot && (is_whitespace(c) || c == '/' || c == ',' || c == ':' || c == '?' || c == ')' || c == '\0' || (c == '.' && i == str.size()))) { size_t tld_end = i - 1; char prev_char = str[i - 1]; // We want to remove the last . or , because the string could contain for example "click on this link: example.com. There you can..." // and we want those links to work, I guess? - if(prev_char == '.' || prev_char == ',' || prev_char == ':') + if(prev_char == '.' || prev_char == ',' || prev_char == ':' || prev_char == '?') --tld_end; else if(prev_char == ')' && parentheses_depth != 0) --tld_end; @@ -1669,7 +1669,7 @@ namespace QuickMedia { char prev_char = str[i - 1]; // We want to remove the last . or , because the string could contain for example "click on this link: example.com. There you can..." // and we want those links to work, I guess? - if(prev_char == '.' || prev_char == ',' || prev_char == ':') + if(prev_char == '.' || prev_char == ',' || prev_char == ':' || prev_char == '?') --url_length; else if(prev_char == ')' && parentheses_depth != 0) --url_length; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 5fcddba..0f69f85 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -2,6 +2,7 @@ #include "../plugins/Manganelo.hpp" #include "../plugins/Mangadex.hpp" #include "../plugins/MangaGeneric.hpp" +#include "../plugins/MangaCombined.hpp" #include "../plugins/Youtube.hpp" #include "../plugins/Pornhub.hpp" #include "../plugins/Spankbang.hpp" @@ -64,6 +65,7 @@ static const std::pair valid_plugins[] = { std::make_pair("mangatown", "mangatown_logo.png"), std::make_pair("mangakatana", "mangakatana_logo.png"), std::make_pair("mangadex", "mangadex_logo.png"), + std::make_pair("manga", nullptr), std::make_pair("youtube", "yt_logo_rgb_dark_small.png"), std::make_pair("spotify", "spotify_logo.png"), std::make_pair("soundcloud", "soundcloud_logo.png"), @@ -421,7 +423,7 @@ namespace QuickMedia { static void usage() { fprintf(stderr, "usage: quickmedia [--no-video] [--use-system-mpv-config] [--dir ] [-e ]\n"); fprintf(stderr, "OPTIONS:\n"); - fprintf(stderr, " plugin The plugin to use. Should be either launcher, 4chan, manganelo, manganelos, mangatown, mangakatana, mangadex, pornhub, spankbang, youtube, spotify, soundcloud, nyaa.si, matrix, file-manager or stdin\n"); + fprintf(stderr, " plugin The plugin to use. Should be either launcher, 4chan, manga, manganelo, manganelos, mangatown, mangakatana, mangadex, pornhub, spankbang, youtube, spotify, soundcloud, nyaa.si, matrix, file-manager or stdin\n"); fprintf(stderr, " --no-video Only play audio when playing a video. Disabled by default\n"); fprintf(stderr, " --use-system-mpv-config Use system mpv config instead of no config. Disabled by default\n"); fprintf(stderr, " --upscale-images Upscale low-resolution manga pages using waifu2x-ncnn-vulkan. Disabled by default\n"); @@ -436,16 +438,18 @@ namespace QuickMedia { } static bool is_manga_plugin(const char *plugin_name) { - return strcmp(plugin_name, "manganelo") == 0 || strcmp(plugin_name, "manganelos") == 0 || strcmp(plugin_name, "mangatown") == 0 || strcmp(plugin_name, "mangakatana") == 0 || strcmp(plugin_name, "mangadex") == 0; + return strcmp(plugin_name, "manga") == 0 || strcmp(plugin_name, "manganelo") == 0 || strcmp(plugin_name, "manganelos") == 0 || strcmp(plugin_name, "mangatown") == 0 || strcmp(plugin_name, "mangakatana") == 0 || strcmp(plugin_name, "mangadex") == 0; } static std::shared_ptr create_launcher_body_item(const char *title, const char *plugin_name, const std::string &thumbnail_url) { auto body_item = BodyItem::create(title); body_item->url = plugin_name; - body_item->thumbnail_url = thumbnail_url; - body_item->thumbnail_is_local = true; - body_item->thumbnail_size.x = 32; - body_item->thumbnail_size.y = 32; + 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; + } return body_item; } @@ -775,6 +779,7 @@ namespace QuickMedia { if(strcmp(plugin_name, "launcher") == 0) { auto pipe_body = create_body(); pipe_body->items.push_back(create_launcher_body_item("4chan", "4chan", resources_root + "icons/4chan_launcher.png")); + pipe_body->items.push_back(create_launcher_body_item("Manga (all)", "manga", "")); pipe_body->items.push_back(create_launcher_body_item("Mangadex", "mangadex", resources_root + "icons/mangadex_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("Mangakatana", "mangakatana", resources_root + "icons/mangakatana_launcher.png")); pipe_body->items.push_back(create_launcher_body_item("Manganelo", "manganelo", resources_root + "icons/manganelo_launcher.png")); @@ -833,6 +838,23 @@ namespace QuickMedia { auto search_bar = create_search_bar("Search...", SEARCH_DELAY_FILTER); auto history_page = std::make_unique(this, tabs.front().page.get(), search_bar.get(), HistoryType::MANGA); tabs.push_back(Tab{std::move(history_body), std::move(history_page), std::move(search_bar)}); + } else if(strcmp(plugin_name, "manga") == 0) { + auto manganelo = std::make_unique(this); + auto manganelos = std::make_unique(this, plugin_name, nullptr); + add_manganelos_handlers(manganelos.get()); + auto mangatown = std::make_unique(this, plugin_name, "https://www.mangatown.com"); + add_mangatown_handlers(mangatown.get()); + auto mangakatana = std::make_unique(this, plugin_name, "https://mangakatana.com", false); + add_mangakatana_handlers(mangakatana.get()); + + std::vector pages; + pages.push_back({std::move(manganelo), "Manganelo", "manganelo", resources_root + "images/" + get_plugin_logo_name("manganelo")}); + pages.push_back({std::move(manganelos), "Manganelos", "manganelos", resources_root + "images/" + get_plugin_logo_name("manganelos")}); + pages.push_back({std::move(mangatown), "Mangatown", "mangatown", resources_root + "images/" + get_plugin_logo_name("mangatown")}); + pages.push_back({std::move(mangakatana), "Mangakatana", "mangakatana", resources_root + "images/" + get_plugin_logo_name("mangakatana")}); + // TODO: Add mangadex + + tabs.push_back(Tab{create_body(), std::make_unique(this, std::move(pages)), create_search_bar("Search...", 400)}); } else if(strcmp(plugin_name, "nyaa.si") == 0) { auto category_page = std::make_unique(this); auto categories_body = create_body(); @@ -1087,6 +1109,8 @@ namespace QuickMedia { return true; } + // TODO: Manga combined + auto filename = filepath.filename(); const Json::Value &manga_name = body["name"]; if(!filename.empty() && manga_name.isString()) { diff --git a/src/plugins/MangaCombined.cpp b/src/plugins/MangaCombined.cpp new file mode 100644 index 0000000..8b989fb --- /dev/null +++ b/src/plugins/MangaCombined.cpp @@ -0,0 +1,55 @@ +#include "../../plugins/MangaCombined.hpp" + +namespace QuickMedia { + MangaCombinedSearchPage::MangaCombinedSearchPage(Program *program, std::vector search_pages) : + Page(program), search_pages(std::move(search_pages)) + { + + } + + SearchResult MangaCombinedSearchPage::search(const std::string &str, BodyItems &result_items) { + for(auto &search_page : search_pages) { + BodyItems search_page_body_items; + search_page.page->search(str, search_page_body_items); + if(search_page_body_items.empty()) + continue; + + auto title_item = BodyItem::create(""); + title_item->set_author("======================== " + search_page.title + " ========================"); + title_item->url = search_page.service_name; + + result_items.push_back(std::move(title_item)); + for(auto &new_body_item : search_page_body_items) { + new_body_item->userdata = search_page.page.get(); + } + result_items.insert(result_items.end(), std::move_iterator(search_page_body_items.begin()), std::move_iterator(search_page_body_items.end())); + } + return SearchResult::OK; + } + + PluginResult MangaCombinedSearchPage::get_page(const std::string &str, int page, BodyItems &result_items) { + for(auto &search_page : search_pages) { + BodyItems search_page_body_items; + search_page.page->get_page(str, page, search_page_body_items); + if(search_page_body_items.empty()) + continue; + + auto title_item = BodyItem::create(""); + title_item->set_author("======================== " + search_page.title + " ========================"); + title_item->url = search_page.service_name; + + result_items.push_back(std::move(title_item)); + for(auto &new_body_item : search_page_body_items) { + new_body_item->userdata = search_page.page.get(); + } + result_items.insert(result_items.end(), std::move_iterator(search_page_body_items.begin()), std::move_iterator(search_page_body_items.end())); + } + return PluginResult::OK; + } + + PluginResult MangaCombinedSearchPage::submit(const std::string &title, const std::string &url, std::vector &result_tabs) { + Page *page = (Page*)submit_body_item->userdata; + if(!page) return PluginResult::OK; + return page->submit(title, url, result_tabs); + } +} \ No newline at end of file diff --git a/tests/main.cpp b/tests/main.cpp index 306cdf2..ca09d4c 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -33,6 +33,11 @@ int main() { assert_equals(urls.size(), 1); assert_equals(urls[0], "example.com"); + str = "is it example.com? or not?"; + urls = QuickMedia::ranges_get_strings(str, QuickMedia::extract_urls(str)); + assert_equals(urls.size(), 1); + assert_equals(urls[0], "example.com"); + str = "these. are. not. websites."; urls = QuickMedia::ranges_get_strings(str, QuickMedia::extract_urls(str)); assert_equals(urls.size(), 0); -- cgit v1.2.3