aboutsummaryrefslogtreecommitdiff
path: root/src/QuickMedia.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r--src/QuickMedia.cpp100
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;