From afc8c731bdf482e87304d2f5df6cfc55018806b6 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 9 Aug 2019 05:13:38 +0200 Subject: Add pornhub plugin --- README.md | 3 +- plugins/Pornhub.hpp | 16 ++++++ src/QuickMedia.cpp | 5 +- src/plugins/Pornhub.cpp | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 plugins/Pornhub.hpp create mode 100644 src/plugins/Pornhub.cpp diff --git a/README.md b/README.md index 535a7f4..80850fc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # QuickMedia -Native clients of websites with fast access to what you want to see. [Demo with manga](https://beta.lbry.tv/quickmedia_manga-2019-08-05_21.20.46/7). +Native clients of websites with fast access to what you want to see. [Demo with manga](https://beta.lbry.tv/quickmedia_manga-2019-08-05_21.20.46/7).\ +Currently supported websites: `youtube`, `manganelo` and `pornhub`. ## Controls Press `ESC` to go back to the previous menu.\ Press `Ctrl + T` when hovering over a manga chapter to start tracking manga after that chapter. This only works if AutoMedia is installed and diff --git a/plugins/Pornhub.hpp b/plugins/Pornhub.hpp new file mode 100644 index 0000000..edbfdab --- /dev/null +++ b/plugins/Pornhub.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Plugin.hpp" + +namespace QuickMedia { + class Pornhub : public Plugin { + public: + SuggestionResult update_search_suggestions(const std::string &text, BodyItems &result_items) override; + BodyItems get_related_media(const std::string &url) override; + bool search_suggestions_has_thumbnails() const override { return true; } + bool search_results_has_thumbnails() const override { return false; } + int get_search_delay() const override { return 500; } + bool search_suggestion_is_search() const override { return true; } + Page get_page_after_search() const override { return Page::VIDEO_CONTENT; } + }; +} \ No newline at end of file diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 4b3698d..691b62b 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1,6 +1,7 @@ #include "../include/QuickMedia.hpp" #include "../plugins/Manganelo.hpp" #include "../plugins/Youtube.hpp" +#include "../plugins/Pornhub.hpp" #include "../include/Scale.hpp" #include "../include/Program.h" #include "../include/VideoPlayer.hpp" @@ -72,7 +73,7 @@ namespace QuickMedia { static void usage() { fprintf(stderr, "usage: QuickMedia \n"); fprintf(stderr, "OPTIONS:\n"); - fprintf(stderr, "plugin The plugin to use. Should be either manganelo or youtube\n"); + fprintf(stderr, "plugin The plugin to use. Should be either manganelo, youtube or pornhub\n"); } int Program::run(int argc, char **argv) { @@ -85,6 +86,8 @@ namespace QuickMedia { current_plugin = new Manganelo(); else if(strcmp(argv[1], "youtube") == 0) current_plugin = new Youtube(); + else if(strcmp(argv[1], "pornhub") == 0) + current_plugin = new Pornhub(); else { usage(); return -1; diff --git a/src/plugins/Pornhub.cpp b/src/plugins/Pornhub.cpp new file mode 100644 index 0000000..4955efd --- /dev/null +++ b/src/plugins/Pornhub.cpp @@ -0,0 +1,139 @@ +#include "../../plugins/Pornhub.hpp" +#include +#include +#include + +namespace QuickMedia { + static bool begins_with(const char *str, const char *begin_with) { + return strncmp(str, begin_with, strlen(begin_with)) == 0; + } + + static bool contains(const char *str, const char *substr) { + return strstr(str, substr); + } + + static void iterate_suggestion_result(const Json::Value &value, BodyItems &result_items, int &iterate_count) { + ++iterate_count; + if(value.isArray()) { + for(const Json::Value &child : value) { + iterate_suggestion_result(child, result_items, iterate_count); + } + } else if(value.isString() && iterate_count > 2) { + std::string title = value.asString(); + auto item = std::make_unique(title); + result_items.push_back(std::move(item)); + } + } + + // TODO: Speed this up by using string.find instead of parsing html + SuggestionResult Pornhub::update_search_suggestions(const std::string &text, BodyItems &result_items) { + std::string url = "https://www.pornhub.com/video/search?search="; + url += url_param_encode(text); + + std::string website_data; + if(download_to_string(url, website_data) != DownloadResult::OK) + return SuggestionResult::NET_ERR; + + struct ItemData { + BodyItems *result_items; + size_t index; + }; + ItemData item_data = { &result_items, 0 }; + + QuickMediaHtmlSearch html_search; + int result = quickmedia_html_search_init(&html_search, website_data.c_str()); + if(result != 0) + goto cleanup; + + result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='phimage']//a", + [](QuickMediaHtmlNode *node, void *userdata) { + auto *result_items = (BodyItems*)userdata; + const char *href = quickmedia_html_node_get_attribute_value(node, "href"); + const char *title = quickmedia_html_node_get_attribute_value(node, "title"); + if(href && title && begins_with(href, "/view_video.php?viewkey")) { + auto item = std::make_unique(strip(title)); + item->url = std::string("https://www.pornhub.com") + href; + result_items->push_back(std::move(item)); + } + }, &result_items); + if(result != 0) + goto cleanup; + + result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='phimage']//img", + [](QuickMediaHtmlNode *node, void *userdata) { + ItemData *item_data = (ItemData*)userdata; + if(item_data->index >= item_data->result_items->size()) + return; + + const char *data_src = quickmedia_html_node_get_attribute_value(node, "data-src"); + if(data_src && contains(data_src, "phncdn.com/videos")) { + (*item_data->result_items)[item_data->index]->thumbnail_url = data_src; + ++item_data->index; + } + }, &item_data); + + // Attempt to skip promoted videos (that are not related to the search term) + if(result_items.size() >= 4) { + result_items.erase(result_items.begin(), result_items.begin() + 4); + } + + cleanup: + quickmedia_html_search_deinit(&html_search); + return result == 0 ? SuggestionResult::OK : SuggestionResult::ERR; + } + + BodyItems Pornhub::get_related_media(const std::string &url) { + BodyItems result_items; + + std::string website_data; + if(download_to_string(url, website_data) != DownloadResult::OK) + return result_items; + + struct ItemData { + BodyItems *result_items; + size_t index; + }; + ItemData item_data = { &result_items, 0 }; + + QuickMediaHtmlSearch html_search; + int result = quickmedia_html_search_init(&html_search, website_data.c_str()); + if(result != 0) + goto cleanup; + + result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='phimage']//a", + [](QuickMediaHtmlNode *node, void *userdata) { + auto *result_items = (BodyItems*)userdata; + const char *href = quickmedia_html_node_get_attribute_value(node, "href"); + const char *title = quickmedia_html_node_get_attribute_value(node, "title"); + if(href && title && begins_with(href, "/view_video.php?viewkey")) { + auto item = std::make_unique(strip(title)); + item->url = std::string("https://www.pornhub.com") + href; + result_items->push_back(std::move(item)); + } + }, &result_items); + if(result != 0) + goto cleanup; + + result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='phimage']//img", + [](QuickMediaHtmlNode *node, void *userdata) { + ItemData *item_data = (ItemData*)userdata; + if(item_data->index >= item_data->result_items->size()) + return; + + const char *src = quickmedia_html_node_get_attribute_value(node, "src"); + if(src && contains(src, "phncdn.com/videos")) { + (*item_data->result_items)[item_data->index]->thumbnail_url = src; + ++item_data->index; + } + }, &item_data); + + // Attempt to skip promoted videos (that are not related to the search term) + if(result_items.size() >= 4) { + result_items.erase(result_items.begin(), result_items.begin() + 4); + } + + cleanup: + quickmedia_html_search_deinit(&html_search); + return result_items; + } +} \ No newline at end of file -- cgit v1.2.3