diff options
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r-- | src/QuickMedia.cpp | 100 |
1 files changed, 97 insertions, 3 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index fd679ff..7402b97 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -67,6 +67,8 @@ static const std::pair<const char*, const char*> valid_plugins[] = { std::make_pair("soundcloud", "soundcloud_logo.png"), std::make_pair("pornhub", "pornhub_logo.png"), std::make_pair("spankbang", "spankbang_logo.png"), + std::make_pair("xvideos", "xvideos_logo.png"), + std::make_pair("xhamster", "xhamster_logo.png"), std::make_pair("4chan", "4chan_logo.png"), std::make_pair("nyaa.si", "nyaa_si_logo.png"), std::make_pair("matrix", "matrix_logo.png"), @@ -444,7 +446,7 @@ namespace QuickMedia { static void usage() { fprintf(stderr, "usage: quickmedia <plugin> [--no-video] [--use-system-mpv-config] [--dir <directory>] [-e <window>]\n"); fprintf(stderr, "OPTIONS:\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, " plugin The plugin to use. Should be either launcher, 4chan, manga, manganelo, manganelos, mangatown, mangakatana, mangadex, pornhub, spankbang, xvideos, xhamster, 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"); @@ -804,6 +806,90 @@ namespace QuickMedia { .related_media_thumbnail_handler({{"//div[class='right']//div[class='video-item']//img", "data-src", nullptr}}); } + // Returns size_t(-1) if not found + static size_t find_end_of_json_array(const char *str, size_t start, size_t size) { + if(size <= start || str[start] != '[') + return size_t(-1); + + bool inside_string = false; + bool escape = false; + int array_depth = 0; + for(size_t i = start; i < size; ++i) { + char c = str[i]; + if(c == '"' && !escape) { + inside_string = !inside_string; + } else if(c == '\\') { + escape = !escape; + } else if(c == '[' && !inside_string && !escape) { + ++array_depth; + } else if(c == ']' && !inside_string && !escape) { + --array_depth; + if(array_depth == 0) + return i + 1; + } else { + escape = false; + } + } + + return size_t(-1); + } + + static void add_xvideos_handlers(MediaGenericSearchPage *media_generic_search_page) { + media_generic_search_page->search_handler("https://www.xvideos.com/?k=%s&p=%p", 0) + .text_handler({{"//div[id='content']//div[class='thumb-under']//a", "title", "href", "/video"}}) + .thumbnail_handler({{"//div[id='content']//div[class='thumb']//img", "data-src", "/videos/"}}) + .related_media_custom_handler([](const std::string &html_source) { + std::vector<MediaRelatedItem> related_items; + size_t related_start = html_source.find("video_related=["); + if(related_start == std::string::npos) + return related_items; + + related_start += 14; // just before [ + size_t json_end = find_end_of_json_array(html_source.c_str(), related_start, html_source.size()); + if(json_end == size_t(-1)) + return related_items; + + 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(html_source.c_str() + related_start, html_source.c_str() + json_end, &json_root, &json_errors)) { + fprintf(stderr, "Failed to parse xvideos related json, error: %s\n", json_errors.c_str()); + return related_items; + } + + if(!json_root.isArray()) + return related_items; + + for(const Json::Value &json_item : json_root) { + if(!json_item.isObject()) + continue; + + const Json::Value &title_json = json_item["tf"]; + const Json::Value &url_json = json_item["u"]; + const Json::Value &thumbnail_url_json = json_item["i"]; + if(!title_json.isString() || !url_json.isString() || !thumbnail_url_json.isString()) + continue; + + MediaRelatedItem related_item; + related_item.title = title_json.asString(); + related_item.url = url_json.asString(); + related_item.thumbnail_url = thumbnail_url_json.asString(); + related_items.push_back(std::move(related_item)); + } + + return related_items; + }); + } + + static void add_xhamster_handlers(MediaGenericSearchPage *media_generic_search_page) { + media_generic_search_page->search_handler("https://xhamster.com/search/%s?page=%p", 1) + .text_handler({{"//div[class='video-thumb-info']//a", "text", "href", "/videos/"}}) + .thumbnail_handler({{"//img", "src", "/thumb-"}}) + .related_media_text_handler({{"//div[class='video-thumb-info']//a", "text", "href", "/videos/"}}) + .related_media_thumbnail_handler({{"//img", "src", "/thumb-"}}); + } + void Program::load_plugin_by_name(std::vector<Tab> &tabs, const char *start_dir) { if(!plugin_name || plugin_name[0] == '\0') return; @@ -935,13 +1021,21 @@ namespace QuickMedia { tabs.push_back(Tab{create_body(), std::move(recommended_page), std::move(recommended_search_bar)}); tabs.push_back(Tab{create_body(), std::make_unique<YoutubeSubscriptionsPage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "pornhub") == 0) { - auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.pornhub.com/", sf::Vector2i(320, 180)); + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.pornhub.com/", sf::Vector2i(320/1.5f, 180/1.5f)); add_pornhub_handlers(search_page.get()); tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 500)}); } else if(strcmp(plugin_name, "spankbang") == 0) { - auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://spankbang.com/", sf::Vector2i(500/2, 281/2)); + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://spankbang.com/", sf::Vector2i(500/2.5f, 281/2.5f)); add_spankbang_handlers(search_page.get()); tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 500)}); + } else if(strcmp(plugin_name, "xvideos") == 0) { + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.xvideos.com/", sf::Vector2i(352/1.5f, 198/1.5f)); + add_xvideos_handlers(search_page.get()); + tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 500)}); + } else if(strcmp(plugin_name, "xhamster") == 0) { + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://xhamster.com/", sf::Vector2i(240, 135)); + add_xhamster_handlers(search_page.get()); + tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 500)}); } else if(strcmp(plugin_name, "spotify") == 0) { tabs.push_back(Tab{create_body(), std::make_unique<SpotifyPodcastSearchPage>(this), create_search_bar("Search...", 350)}); no_video = true; |