aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/DramaCool.cpp412
-rw-r--r--src/plugins/HotExamples.cpp2
-rw-r--r--src/plugins/Lbry.cpp6
-rw-r--r--src/plugins/MangaGeneric.cpp2
-rw-r--r--src/plugins/Manganelo.cpp16
-rw-r--r--src/plugins/MediaGeneric.cpp6
-rw-r--r--src/plugins/MyAnimeList.cpp10
-rw-r--r--src/plugins/NyaaSi.cpp8
-rw-r--r--src/plugins/Peertube.cpp8
-rw-r--r--src/plugins/Saucenao.cpp4
-rw-r--r--src/plugins/Soundcloud.cpp8
-rw-r--r--src/plugins/Youtube.cpp6
12 files changed, 447 insertions, 41 deletions
diff --git a/src/plugins/DramaCool.cpp b/src/plugins/DramaCool.cpp
new file mode 100644
index 0000000..9b419f9
--- /dev/null
+++ b/src/plugins/DramaCool.cpp
@@ -0,0 +1,412 @@
+#include "../../plugins/DramaCool.hpp"
+#include "../../include/Theme.hpp"
+#include "../../include/StringUtils.hpp"
+#include "../../include/M3U8.hpp"
+#include <json/value.h>
+#include <quickmedia/HtmlSearch.h>
+
+// TODO: Add bookmarks page, history, track watch progress, automatically go to next episode, subscribe, etc.
+
+namespace QuickMedia {
+ SearchResult DramaCoolSearchPage::search(const std::string &str, BodyItems &result_items) {
+ if(str.empty())
+ return SearchResult::OK;
+
+ std::vector<CommandArg> additional_args = {
+ { "-H", "x-requested-with: XMLHttpRequest" }
+ };
+
+ Json::Value json_root;
+ DownloadResult result = download_json(json_root, "https://dramacool.cr/search?keyword=" + url_param_encode(str) + "&type=movies", std::move(additional_args), true);
+ if(result != DownloadResult::OK) return download_result_to_search_result(result);
+
+ if(!json_root.isArray())
+ return SearchResult::ERR;
+
+ for(const Json::Value &json_item : json_root) {
+ if(!json_item.isObject())
+ continue;
+
+ const Json::Value &name_json = json_item["name"];
+ const Json::Value &status_json = json_item["status"];
+ const Json::Value &cover_url_json = json_item["cover"];
+ const Json::Value &url_json = json_item["url"];
+
+ if(!name_json.isString() || !url_json.isString())
+ continue;
+
+ auto body_item = BodyItem::create(name_json.asString());
+ if(status_json.isString()) {
+ body_item->set_description(status_json.asString());
+ body_item->set_description_color(get_theme().faded_text_color);
+ }
+ if(cover_url_json.isString()) {
+ body_item->thumbnail_url = "https://imagecdn.me/" + url_param_encode(cover_url_json.asString());
+ body_item->thumbnail_size = { 150, 225 };
+ }
+ body_item->url = "https://dramacool.cr" + url_json.asString();
+
+ result_items.push_back(std::move(body_item));
+ }
+
+ return SearchResult::OK;
+ }
+
+ static bool node_get_inner_text(const QuickMediaHtmlChildNode *node, QuickMediaStringView &str) {
+ if(!node || !node->node.is_tag || !node->node.first_child || node->node.first_child->node.is_tag)
+ return false;
+
+ str = node->node.first_child->node.name;
+ return true;
+ }
+
+ PluginResult DramaCoolSearchPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) {
+ std::string website_data;
+ DownloadResult result = download_to_string(args.url, website_data, {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+
+ BodyItems result_items;
+
+ QuickMediaHtmlSearch html_search;
+ int res = quickmedia_html_search_init(&html_search, website_data.c_str(), website_data.size());
+ if(res != 0)
+ return PluginResult::ERR;
+
+ quickmedia_html_find_nodes_xpath(&html_search, "//div[class='content']//a",
+ [](QuickMediaMatchNode *node, void *userdata) {
+ auto *result_items = (BodyItems*)userdata;
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
+ if(!href.data || !memmem(href.data, href.size, ".html", 5))
+ return 0;
+
+ QuickMediaHtmlChildNode *sub_node = node->node->first_child;
+ if(!sub_node)
+ return 0;
+
+ QuickMediaHtmlChildNode *title_node = sub_node->next;
+ if(!title_node)
+ return 0;
+
+ QuickMediaHtmlChildNode *time_node = title_node->next;
+ if(!time_node)
+ return 0;
+
+ QuickMediaStringView sub_str;
+ QuickMediaStringView title_str;
+ QuickMediaStringView time_str;
+ if(!node_get_inner_text(sub_node, sub_str) || !node_get_inner_text(title_node, title_str) || !node_get_inner_text(time_node, time_str))
+ return 0;
+
+ std::string title(title_str.data, title_str.size);
+ html_unescape_sequences(title);
+
+ std::string time(time_str.data, time_str.size);
+ html_unescape_sequences(time);
+
+ const bool is_subbed = sub_str.size == 3 && memcmp(sub_str.data, "SUB", 3) == 0;
+
+ auto body_item = BodyItem::create(std::move(title));
+ body_item->set_description((is_subbed ? "Subbed" : "Raw") + std::string(" • ") + time);
+ body_item->set_description_color(get_theme().faded_text_color);
+ body_item->url = "https://dramacool.cr" + std::string(href.data, href.size);
+ result_items->push_back(std::move(body_item));
+
+ return 0;
+ }, &result_items);
+
+ quickmedia_html_search_deinit(&html_search);
+
+ auto body = create_body();
+ body->set_items(std::move(result_items));
+ result_tabs.push_back(Tab{ std::move(body), std::make_unique<DramaCoolEpisodesPage>(program), create_search_bar("Search...", SEARCH_DELAY_FILTER) });
+ return PluginResult::OK;
+ }
+
+ struct VideoSources {
+ //std::string streamsss;
+ std::string streamtape;
+ std::string mixdrop;
+ std::string mp4upload;
+ };
+
+ static bool dembed_extract_video_source(const std::string &website_data, const std::string &video_source_url, std::string &video_source) {
+ size_t st_start = website_data.find(video_source_url);
+ if(st_start == std::string::npos)
+ return false;
+
+ st_start += video_source_url.size();
+ size_t st_end = website_data.find("\"", st_start);
+ if(st_end == std::string::npos)
+ return false;
+
+ video_source = "https://" + video_source_url + website_data.substr(st_start, st_end - st_start);
+ return true;
+ }
+
+ static void dembed_extract_video_sources(const std::string &website_data, VideoSources &video_sources) {
+ //dembed_extract_video_source(website_data, "streamsss.net", video_sources.streamsss);
+ dembed_extract_video_source(website_data, "streamtape.com", video_sources.streamtape);
+ dembed_extract_video_source(website_data, "mixdrop.co", video_sources.mixdrop);
+ dembed_extract_video_source(website_data, "www.mp4upload.com", video_sources.mp4upload);
+ }
+
+ // TODO: Re-add. It's broken right now (because of the json_url has incorrect value I guess)
+ /*
+ static bool streamsss_extract_video_url(Page *page, const std::string &streamsss_url, std::string &url) {
+ size_t url_end = streamsss_url.find("/e");
+ if(url_end == std::string::npos)
+ return false;
+
+ size_t id_start = streamsss_url.find("e/");
+ if(id_start == std::string::npos)
+ return false;
+
+ id_start += 2;
+ size_t id_end = streamsss_url.find("?", id_start);
+ if(id_end == std::string::npos)
+ id_end = streamsss_url.size();
+
+ const std::string url_base = streamsss_url.substr(0, url_end);
+ const std::string id = streamsss_url.substr(id_start, id_end - id_start);
+
+ std::ostringstream id_hex;
+ id_hex << std::hex;
+ for(size_t i = 0; i < id.size(); ++i)
+ id_hex << (int)(unsigned char)id[i];
+
+ const std::string json_url = url_base + "/sources43/566d337678566f743674494a7c7c" + id_hex.str() + "7c7c346b6767586d6934774855537c7c73747265616d7362/6565417268755339773461447c7c346133383438333436313335376136323337373433383634376337633465366534393338373136643732373736343735373237613763376334363733353737303533366236333463353333363534366137633763373337343732363536313664373336327c7c6b586c3163614468645a47617c7c73747265616d7362";
+
+ Json::Value json_root;
+ DownloadResult result = page->download_json(json_root, json_url, {}, true);
+ if(result != DownloadResult::OK)
+ return false;
+
+ if(!json_root.isObject())
+ return false;
+
+ const Json::Value &stream_data_json = json_root["stream_data"];
+ if(!stream_data_json.isObject())
+ return false;
+
+ const Json::Value &file_json = stream_data_json["file"];
+ if(!file_json.isString())
+ return false;
+
+ // TODO: Record resolution and duration
+ std::string website_data;
+ result = download_to_string(file_json.asString(), website_data, {}, true);
+ if(result != DownloadResult::OK)
+ return false;
+
+ url = M3U8Stream::get_highest_resolution_stream(m3u8_get_streams(website_data)).url;
+ return true;
+ }
+ */
+ static bool streamtape_extract_video_url(const std::string &website_data, std::string &url) {
+ size_t id_start = website_data.find("getElementById('robotlink')");
+ if(id_start == std::string::npos)
+ return false;
+
+ id_start += 27;
+ id_start = website_data.find("id=", id_start);
+ if(id_start == std::string::npos)
+ return false;
+
+ id_start += 3;
+ size_t id_end = website_data.find("'", id_start);
+ if(id_end == std::string::npos)
+ return false;
+
+ url = "https://streamtape.com/get_video?id=" + website_data.substr(id_start, id_end - id_start);
+ return true;
+ }
+
+ static bool mixdrop_extract_mdcore_script(const std::string &website_data, const std::string &prefix, std::string &mdcore_script) {
+ size_t script_start = website_data.find(prefix);
+ if(script_start == std::string::npos)
+ return false;
+
+ script_start += prefix.size();
+ size_t script_end = website_data.find("\"", script_start);
+ if(script_end == std::string::npos)
+ return false;
+
+ mdcore_script = website_data.substr(script_start, script_end - script_start);
+ return true;
+ }
+
+ static bool mixdrop_extract_mdcore_parts(const std::string &website_data, std::vector<std::string> &parts) {
+ size_t mdcore_start = website_data.find(",'|");
+ if(mdcore_start == std::string::npos) {
+ mdcore_start = website_data.find("MDCore|");
+ if(mdcore_start == std::string::npos)
+ return false;
+ } else {
+ mdcore_start += 2;
+ }
+
+ size_t mdcore_end = website_data.find("'", mdcore_start);
+ if(mdcore_end == std::string::npos)
+ return false;
+
+ std::string mdcore_str = website_data.substr(mdcore_start, mdcore_end - mdcore_start);
+ string_split(mdcore_str, '|', [&parts](const char *str, size_t size) {
+ parts.emplace_back(str, size);
+ return true;
+ });
+
+ return true;
+ }
+
+ static int mdcore_number_get(char c) {
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ else if(c >= 'a' && c <= 'z')
+ return 10 + (c - 'a');
+ else
+ return -1;
+ }
+
+ static bool mixdrop_extract_video_url(const std::string &website_data, const std::string &prefix, std::string &url) {
+ std::string mdcore_script;
+ if(!mixdrop_extract_mdcore_script(website_data, prefix, mdcore_script))
+ return false;
+
+ std::vector<std::string> mdcore_parts;
+ if(!mixdrop_extract_mdcore_parts(website_data, mdcore_parts))
+ return false;
+
+ for(size_t i = 0; i < mdcore_script.size();) {
+ char c = mdcore_script[i];
+ int index = mdcore_number_get(c);
+
+ if(index >= 0) {
+ ++i;
+ while(i < mdcore_script.size() && ((mdcore_script[i] >= '0' && mdcore_script[i] <= '9') || (mdcore_script[i] >= 'a' && mdcore_script[i] <= 'z'))) {
+ // 36 = 0-9 + a-z
+ index = (index * 36) + mdcore_number_get(mdcore_script[i]);
+ ++i;
+ }
+ } else {
+ url += c;
+ ++i;
+ continue;
+ }
+
+ if(index >= (int)mdcore_parts.size() || mdcore_parts[index].empty())
+ url += c;
+ else
+ url += mdcore_parts[index];
+ }
+
+ if(string_starts_with(url, "//"))
+ url = "http:" + url;
+
+ return true;
+ }
+
+ PluginResult DramaCoolEpisodesPage::submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) {
+ std::string website_data;
+ DownloadResult result = download_to_string(args.url, website_data, {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+
+ std::string video_sources_url;
+
+ QuickMediaHtmlSearch html_search;
+ int res = quickmedia_html_search_init(&html_search, website_data.c_str(), website_data.size());
+ if(res != 0)
+ return PluginResult::ERR;
+
+ quickmedia_html_find_nodes_xpath(&html_search, "//div",
+ [](QuickMediaMatchNode *node, void *userdata) {
+ auto *video_sources_url = (std::string*)userdata;
+ QuickMediaStringView klass = quickmedia_html_node_get_attribute_value(node->node, "class");
+ if(!klass.data || !memmem(klass.data, klass.size, "watch_video", 11))
+ return 0;
+
+ QuickMediaHtmlChildNode *iframe_node = node->node->first_child;
+ if(!iframe_node || !iframe_node->node.is_tag || iframe_node->node.name.size != 6 || memcmp(iframe_node->node.name.data, "iframe", 6) != 0)
+ return 0;
+
+ QuickMediaStringView src = quickmedia_html_node_get_attribute_value(&iframe_node->node, "src");
+ if(!src.data)
+ return 0;
+
+ video_sources_url->assign(src.data, src.size);
+ return 1;
+ }, &video_sources_url);
+
+ quickmedia_html_search_deinit(&html_search);
+
+ if(video_sources_url.empty())
+ return PluginResult::ERR;
+
+ if(string_starts_with(video_sources_url, "//"))
+ video_sources_url = "https:" + video_sources_url;
+
+ result = download_to_string(video_sources_url, website_data, {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+
+ // TODO: Extract all video sources and allow the user to select which one to use.
+ // Streamtape or mixdrop may not be available or if it's available it may not load properly
+ // or the quality may be worse or slower than other sources.
+ // We also want to load the high resolution version of the video.
+ // TODO: Make videos sources work even when captions are not embedded.
+ VideoSources video_sources;
+ dembed_extract_video_sources(website_data, video_sources);
+
+ std::string video_url;
+ std::string referer;
+
+ if(!video_sources.streamtape.empty() && video_url.empty()) {
+ result = download_to_string(video_sources.streamtape, website_data, {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+ streamtape_extract_video_url(website_data, video_url);
+
+ if(!video_url.empty())
+ referer = "https://streamtape.com";
+ }
+
+ if(!video_sources.mixdrop.empty() && video_url.empty()) {
+ result = download_to_string(video_sources.mixdrop, website_data, {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+ mixdrop_extract_video_url(website_data, "0.f=\"", video_url);
+
+ if(!video_url.empty())
+ referer = "https://mixdrop.co";
+ }
+
+ if(!video_sources.mp4upload.empty() && video_url.empty()) {
+ result = download_to_string(video_sources.mp4upload, website_data, {}, true);
+ if(result != DownloadResult::OK) return download_result_to_plugin_result(result);
+ // mp4upload uses the same algorithm as mixdrop but with different format
+ mixdrop_extract_video_url(website_data, "2.25(\"", video_url);
+
+ if(!video_url.empty())
+ referer = "https://www.mp4upload.com";
+ }
+
+ if(video_url.empty())
+ return PluginResult::ERR;
+
+ /*
+ if(!video_sources.streamsss.empty() && video_url.empty()) {
+ streamsss_extract_video_url(this, video_sources.streamsss, video_url);
+ }
+ */
+
+ result_tabs.push_back(Tab{ nullptr, std::make_unique<DramaCoolVideoPage>(program, std::move(video_url), args.title, std::move(referer)), nullptr });
+
+ return PluginResult::OK;
+ }
+
+ PluginResult DramaCoolVideoPage::load(const SubmitArgs&, VideoInfo &video_info, std::string &err_str) {
+ video_info.title = title;
+ video_info.channel_url.clear();
+ video_info.duration = 0.0;
+ video_info.chapters.clear();
+ video_info.referer = referer;
+ err_str.clear();
+ return PluginResult::OK;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/HotExamples.cpp b/src/plugins/HotExamples.cpp
index fbefad5..7cb811b 100644
--- a/src/plugins/HotExamples.cpp
+++ b/src/plugins/HotExamples.cpp
@@ -44,7 +44,7 @@ namespace QuickMedia {
quickmedia_html_find_nodes_xpath(&html_search, "//div[class='search-result row']//div[class='header']//a",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItems*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
if(href.data && memmem(href.data, href.size, "/examples/", 10)) {
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(text.data) {
diff --git a/src/plugins/Lbry.cpp b/src/plugins/Lbry.cpp
index 069bd89..6be0148 100644
--- a/src/plugins/Lbry.cpp
+++ b/src/plugins/Lbry.cpp
@@ -395,11 +395,11 @@ namespace QuickMedia {
return "";
}
- PluginResult LbryVideoPage::load(const SubmitArgs &args, std::string &title, std::string&, double &duration, std::vector<MediaChapter>&, std::string &err_str) {
+ PluginResult LbryVideoPage::load(const SubmitArgs &args, VideoInfo &video_info, std::string &err_str) {
streaming_url.clear();
- title = args.title;
+ video_info.title = args.title;
//title = this->title;
- duration = 0.0;
+ video_info.duration = 0.0;
return video_get_stream_url(this, url, streaming_url, err_str);
}
} \ No newline at end of file
diff --git a/src/plugins/MangaGeneric.cpp b/src/plugins/MangaGeneric.cpp
index 767f077..2c77773 100644
--- a/src/plugins/MangaGeneric.cpp
+++ b/src/plugins/MangaGeneric.cpp
@@ -52,7 +52,7 @@ namespace QuickMedia {
if(strcmp(field_name, "text") == 0)
return quickmedia_html_node_get_text(node);
else
- return quickmedia_html_node_get_attribute_value(node, field_name);
+ return quickmedia_html_node_get_attribute_value(node->node, field_name);
}
static void body_items_prepend_website_url(BodyItems &body_items, const std::string &website_url) {
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index 5a75d5d..ef7f52c 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -106,7 +106,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//ul[class='row-content-chapter']//a",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItems*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(href.data && text.data) {
auto item = BodyItem::create(std::string(text.data, text.size));
@@ -119,7 +119,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='chapter-list']//a",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItems*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(href.data && text.data) {
auto item = BodyItem::create(std::string(text.data, text.size));
@@ -136,7 +136,7 @@ namespace QuickMedia {
quickmedia_html_find_nodes_xpath(&html_search, "//ul[class='row-content-chapter']//span",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItemContext*)userdata;
- QuickMediaStringView class_attr = quickmedia_html_node_get_attribute_value(node, "class");
+ QuickMediaStringView class_attr = quickmedia_html_node_get_attribute_value(node->node, "class");
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(text.data && class_attr.data && string_view_contains(class_attr, "chapter-time") && item_data->index < item_data->body_items->size()) {
std::string uploaded_date(text.data, text.size);
@@ -150,7 +150,7 @@ namespace QuickMedia {
quickmedia_html_find_nodes_xpath(&html_search, "//a[class='a-h']",
[](QuickMediaMatchNode *node, void *userdata) {
std::vector<Creator> *creators = (std::vector<Creator>*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(href.data && text.data && string_view_contains(href, "/author/story/")) {
Creator creator;
@@ -281,8 +281,8 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='search-story-item']//a[class='item-img']",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItems*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
- QuickMediaStringView title = quickmedia_html_node_get_attribute_value(node, "title");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
+ QuickMediaStringView title = quickmedia_html_node_get_attribute_value(node->node, "title");
if(href.data && title.data && string_view_contains(href, "/manga/")) {
auto body_item = BodyItem::create(std::string(title.data, title.size));
body_item->url.assign(href.data, href.size);
@@ -301,7 +301,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='search-story-item']//a[class='item-img']//img",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItemContext*)userdata;
- QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node, "src");
+ QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node->node, "src");
if(src.data && item_data->index < item_data->body_items->size()) {
(*item_data->body_items)[item_data->index]->thumbnail_url.assign(src.data, src.size);
(*item_data->body_items)[item_data->index]->thumbnail_size = {101, 141};
@@ -334,7 +334,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='container-chapter-reader']/img",
[](QuickMediaMatchNode *node, void *userdata) {
auto *urls = (std::vector<std::string>*)userdata;
- QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node, "src");
+ QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node->node, "src");
if(src.data) {
std::string image_url(src.data, src.size);
urls->push_back(std::move(image_url));
diff --git a/src/plugins/MediaGeneric.cpp b/src/plugins/MediaGeneric.cpp
index 4ff55da..cc869ef 100644
--- a/src/plugins/MediaGeneric.cpp
+++ b/src/plugins/MediaGeneric.cpp
@@ -20,7 +20,7 @@ namespace QuickMedia {
if(strcmp(field_name, "text") == 0)
return quickmedia_html_node_get_text(node);
else
- return quickmedia_html_node_get_attribute_value(node, field_name);
+ return quickmedia_html_node_get_attribute_value(node->node, field_name);
}
static void body_items_prepend_website_url(BodyItems &body_items, const std::string &website_url) {
@@ -224,9 +224,9 @@ namespace QuickMedia {
return video_url;
}
- PluginResult MediaGenericVideoPage::load(const SubmitArgs&, std::string&, std::string&, double &duration, std::vector<MediaChapter>&, std::string &err_msg) {
+ PluginResult MediaGenericVideoPage::load(const SubmitArgs&, VideoInfo &video_info, std::string &err_msg) {
video_url.clear();
- duration = 0.0;
+ video_info.duration = 0.0;
if(!search_page->video_custom_handler) {
video_url = url;
return PluginResult::OK;
diff --git a/src/plugins/MyAnimeList.cpp b/src/plugins/MyAnimeList.cpp
index b982e3c..aa72750 100644
--- a/src/plugins/MyAnimeList.cpp
+++ b/src/plugins/MyAnimeList.cpp
@@ -179,7 +179,7 @@ namespace QuickMedia {
quickmedia_html_find_nodes_xpath(&html_search, "//img[itemprop='image']",
[](QuickMediaMatchNode *node, void *userdata) {
std::string *thumbnail_url = (std::string*)userdata;
- QuickMediaStringView data_src = quickmedia_html_node_get_attribute_value(node, "data-src");
+ QuickMediaStringView data_src = quickmedia_html_node_get_attribute_value(node->node, "data-src");
if(data_src.data) {
thumbnail_url->assign(data_src.data, data_src.size);
html_unescape_sequences(*thumbnail_url);
@@ -223,12 +223,6 @@ namespace QuickMedia {
return alt.substr(index + 2);
}
- static QuickMediaStringView quickmedia_html_node_get_attribute_value(QuickMediaHtmlNode *node, const char *attribute_name) {
- QuickMediaMatchNode match_node;
- match_node.node = node;
- return quickmedia_html_node_get_attribute_value(&match_node, attribute_name);
- }
-
PluginResult MyAnimeListRecommendationsPage::lazy_fetch(BodyItems &result_items) {
std::string website_data;
DownloadResult download_result = download_to_string(url + "/userrecs", website_data, {}, true);
@@ -242,7 +236,7 @@ namespace QuickMedia {
quickmedia_html_find_nodes_xpath(&html_search, "//div[class='picSurround']/a",
[](QuickMediaMatchNode *node, void *userdata) {
BodyItems *result_items = (BodyItems*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
if(!href.data)
return 0;
diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp
index fd24e71..b56ef69 100644
--- a/src/plugins/NyaaSi.cpp
+++ b/src/plugins/NyaaSi.cpp
@@ -367,7 +367,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='panel-body']//div[class='row']//a",
[](QuickMediaMatchNode *node, void *userdata) {
ResultItemExtra *item_data = (ResultItemExtra*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(item_data->result_items->empty() && href.data && text.data && href.size >= 6 && memcmp(href.data, "/user/", 6) == 0) {
auto body_item = BodyItem::create("");
@@ -410,7 +410,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='container']//a",
[](QuickMediaMatchNode *node, void *userdata) {
std::string *magnet_url = (std::string*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
if(magnet_url->empty() && href.data && href.size >= 8 && memcmp(href.data, "magnet:?", 8) == 0) {
magnet_url->assign(href.data, href.size);
}
@@ -434,7 +434,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[id='comments']//a",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItems*)userdata;
- QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node, "href");
+ QuickMediaStringView href = quickmedia_html_node_get_attribute_value(node->node, "href");
QuickMediaStringView text = quickmedia_html_node_get_text(node);
if(href.data && text.data && href.size >= 6 && memcmp(href.data, "/user/", 6) == 0) {
auto body_item = BodyItem::create(std::string(text.data, text.size));
@@ -454,7 +454,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//div[id='comments']//img[class='avatar']",
[](QuickMediaMatchNode *node, void *userdata) {
auto *item_data = (BodyItemContext*)userdata;
- QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node, "src");
+ QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node->node, "src");
if(src.data && item_data->index < item_data->body_items->size()) {
(*item_data->body_items)[item_data->index]->thumbnail_url.assign(src.data, src.size);
(*item_data->body_items)[item_data->index]->thumbnail_size = mgl::vec2i(120, 120);
diff --git a/src/plugins/Peertube.cpp b/src/plugins/Peertube.cpp
index 4e19f1a..4fb2ac4 100644
--- a/src/plugins/Peertube.cpp
+++ b/src/plugins/Peertube.cpp
@@ -370,7 +370,7 @@ namespace QuickMedia {
}
// TODO: Media chapters
- PluginResult PeertubeVideoPage::load(const SubmitArgs&, std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter>&, std::string &err_str) {
+ PluginResult PeertubeVideoPage::load(const SubmitArgs&, VideoInfo &video_info, std::string &err_str) {
Json::Value json_root;
std::string err_msg;
DownloadResult download_result = download_json(json_root, server + "/api/v1/videos/" + url, {}, true, &err_msg);
@@ -384,17 +384,17 @@ namespace QuickMedia {
const Json::Value &name_json = json_root["name"];
if(name_json.isString())
- title = name_json.asString();
+ video_info.title = name_json.asString();
const Json::Value &duration_json = json_root["duration"];
if(duration_json.isInt64())
- duration = duration_json.asInt64();
+ video_info.duration = duration_json.asInt64();
const Json::Value &channel_json = json_root["channel"];
if(channel_json.isObject()) {
const Json::Value &channel_url_json = channel_json["url"];
if(channel_url_json.isString())
- channel_url = channel_url_json.asString();
+ video_info.channel_url = channel_url_json.asString();
}
video_sources.clear();
diff --git a/src/plugins/Saucenao.cpp b/src/plugins/Saucenao.cpp
index 6833bea..ac2b622 100644
--- a/src/plugins/Saucenao.cpp
+++ b/src/plugins/Saucenao.cpp
@@ -131,8 +131,8 @@ namespace QuickMedia {
quickmedia_html_find_nodes_xpath(&html_search, "//td[class='resulttableimage']//img",
[](QuickMediaMatchNode *node, void *userdata) {
BodyItemContext *item_data = (BodyItemContext*)userdata;
- QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node, "src");
- QuickMediaStringView data_src = quickmedia_html_node_get_attribute_value(node, "data-src");
+ QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node->node, "src");
+ QuickMediaStringView data_src = quickmedia_html_node_get_attribute_value(node->node, "data-src");
QuickMediaStringView image_url = data_src.data ? data_src : src;
if(image_url.data && item_data->index < item_data->body_items->size()) {
(*item_data->body_items)[item_data->index]->thumbnail_url.assign(image_url.data, image_url.size);
diff --git a/src/plugins/Soundcloud.cpp b/src/plugins/Soundcloud.cpp
index bc89448..0d4d8e1 100644
--- a/src/plugins/Soundcloud.cpp
+++ b/src/plugins/Soundcloud.cpp
@@ -361,7 +361,7 @@ namespace QuickMedia {
result = quickmedia_html_find_nodes_xpath(&html_search, "//script",
[](QuickMediaMatchNode *node, void *userdata) {
std::vector<std::string> *script_sources = (std::vector<std::string>*)userdata;
- QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node, "src");
+ QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node->node, "src");
if(src.data && (memmem(src.data, src.size, "sndcdn.com", 10) || memmem(src.data, src.size, "soundcloud.com", 14)))
script_sources->push_back(std::string(src.data, src.size));
return 0;
@@ -477,14 +477,14 @@ namespace QuickMedia {
return PluginResult::OK;
}
- PluginResult SoundcloudAudioPage::load(const SubmitArgs &args, std::string &title, std::string&, double &duration, std::vector<MediaChapter>&, std::string&) {
+ PluginResult SoundcloudAudioPage::load(const SubmitArgs &args, VideoInfo &video_info, std::string&) {
SoundcloudTrack *track = static_cast<SoundcloudTrack*>(args.extra.get());
if(track)
permalink_url = track->permalink_url;
- title = args.title;
+ video_info.title = args.title;
//title = this->title;
- duration = 0.0;
+ video_info.duration = 0.0;
return PluginResult::OK;
}
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index a0c15de..93cb266 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -2423,7 +2423,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- PluginResult YoutubeVideoPage::load(const SubmitArgs&, std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) {
+ PluginResult YoutubeVideoPage::load(const SubmitArgs&, VideoInfo &video_info, std::string &err_str) {
std::string video_id;
if(!youtube_url_extract_id(url, video_id)) {
fprintf(stderr, "Failed to extract youtube id from %s\n", url.c_str());
@@ -2530,10 +2530,10 @@ R"END(
if(download_result != DownloadResult::OK)
continue;
- PluginResult result = parse_video_response(json_root, title, channel_url, chapters, err_str);
+ PluginResult result = parse_video_response(json_root, video_info.title, video_info.channel_url, video_info.chapters, err_str);
if(result == PluginResult::OK) {
err_str.clear();
- duration = video_details.duration;
+ video_info.duration = video_details.duration;
return PluginResult::OK;
}
}