aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Mangadex.cpp
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-09-12 15:33:49 +0200
committerdec05eba <dec05eba@protonmail.com>2021-09-12 15:33:49 +0200
commita9d69f57a0cd4f9cccff07b2890a860695d1e7ed (patch)
treef03e747dc6a0f52fbbef3772437ddf230a05c721 /src/plugins/Mangadex.cpp
parentde8682ce581cdc3f0886e3edf89b430a7215aea1 (diff)
Mangadex: add author/artist tabs for manga, optimize search (covers)
Diffstat (limited to 'src/plugins/Mangadex.cpp')
-rw-r--r--src/plugins/Mangadex.cpp235
1 files changed, 127 insertions, 108 deletions
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 34bcfc7..9209828 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -59,91 +59,8 @@ namespace QuickMedia {
return PluginResult::OK;
}
-
- static std::shared_ptr<BodyItem> relationship_get_body_item(const Json::Value &json, BodyItems &body_items) {
- for(const Json::Value &item_json : json) {
- if(!item_json.isObject())
- continue;
-
- const Json::Value &type_json = item_json["type"];
- if(!type_json.isString() || strcmp(type_json.asCString(), "manga") != 0)
- continue;
-
- const Json::Value &id_json = item_json["id"];
- if(!id_json.isString())
- continue;
-
- std::string id_str = id_json.asString();
- auto it = std::find_if(body_items.begin(), body_items.end(), [&id_str](const std::shared_ptr<BodyItem> &body_item) {
- return body_item->url == id_str;
- });
-
- if(it == body_items.end())
- continue;
-
- return *it;
- }
- return nullptr;
- }
-
- PluginResult MangadexSearchPage::get_cover_urls(BodyItems &body_items) {
- if(body_items.empty())
- return PluginResult::OK;
-
- std::string url = "https://api.mangadex.org/cover?limit=100&order[updatedAt]=desc";
- for(size_t i = 0; i < body_items.size(); ++i) {
- if(!body_items[i]->thumbnail_url.empty())
- url += "&ids[]=" + body_items[i]->thumbnail_url;
- }
-
- Json::Value json_root;
- if(download_json(json_root, url, {}, true) != DownloadResult::OK)
- return PluginResult::NET_ERR;
-
- if(!json_root.isObject())
- return PluginResult::OK;
-
- const Json::Value &results_json = json_root["results"];
- if(!results_json.isArray())
- return PluginResult::OK;
-
- std::shared_ptr<BodyItem> body_item;
- for(const Json::Value &result_item_json : results_json) {
- if(!result_item_json.isObject())
- continue;
-
- const Json::Value &result_json = result_item_json["result"];
- if(!result_json.isString() || strcmp(result_json.asCString(), "ok") != 0)
- continue;
-
- const Json::Value &relationships_json = result_item_json["relationships"];
- if(!relationships_json.isArray())
- continue;
-
- body_item = relationship_get_body_item(relationships_json, body_items);
- if(!body_item)
- continue;
-
- const Json::Value &data_json = result_item_json["data"];
- if(!data_json.isObject())
- continue;
-
- const Json::Value &attributes_json = data_json["attributes"];
- if(!attributes_json.isObject())
- continue;
-
- const Json::Value &filename_json = attributes_json["fileName"];
- if(!filename_json.isString())
- continue;
-
- body_item->thumbnail_url = "https://uploads.mangadex.org/covers/" + body_item->url + "/" + filename_json.asString() + ".256.jpg";
- body_item->thumbnail_size = {101, 141};
- }
-
- return PluginResult::OK;
- }
- static std::string relationships_get_cover_art(const Json::Value &relationships_json) {
+ static std::string relationships_get_cover_art_filename(const Json::Value &relationships_json) {
std::string result;
if(!relationships_json.isArray())
return result;
@@ -152,31 +69,53 @@ namespace QuickMedia {
if(!relationship_json.isObject())
continue;
- const Json::Value &id_json = relationship_json["id"];
const Json::Value &relationship_type_json = relationship_json["type"];
- if(!id_json.isString() || !relationship_type_json.isString() || strcmp(relationship_type_json.asCString(), "cover_art") != 0)
+ if(!relationship_type_json.isString() || strcmp(relationship_type_json.asCString(), "cover_art") != 0)
+ continue;
+
+ const Json::Value &attributes_json = relationship_json["attributes"];
+ if(!attributes_json.isObject())
continue;
- result = id_json.asString();
+ const Json::Value &file_name_json = attributes_json["fileName"];
+ if(!file_name_json.isString())
+ continue;
+
+ result = file_name_json.asString();
break;
}
return result;
}
- SearchResult MangadexSearchPage::search(const std::string &str, int page, BodyItems &result_items) {
- std::string url = "https://api.mangadex.org/manga?title=" + url_param_encode(str) + "&limit=20&offset=" + std::to_string(page * 20);
+ enum class SearchType {
+ TITLE,
+ AUTHOR
+ };
+
+ static PluginResult search_manga(Page *plugin_page, SearchType search_type, const std::string &query, int page, BodyItems &result_items) {
+ std::string url = "https://api.mangadex.org/manga?limit=20&includes[]=cover_art&offset=" + std::to_string(page * 20);
+ const std::string query_encoded = url_param_encode(query);
+ switch(search_type) {
+ case SearchType::TITLE:
+ url += "&title=" + query_encoded;
+ break;
+ case SearchType::AUTHOR:
+ url += "&authors[]=" + query_encoded;
+ url += "&artists[]=" + query_encoded;
+ break;
+ }
Json::Value json_root;
- if(download_json(json_root, url, {}, true) != DownloadResult::OK)
- return SearchResult::NET_ERR;
+ if(plugin_page->download_json(json_root, url, {}, true) != DownloadResult::OK)
+ return PluginResult::NET_ERR;
if(!json_root.isObject())
- return SearchResult::OK;
+ return PluginResult::OK;
const Json::Value &results_json = json_root["results"];
if(!results_json.isArray())
- return SearchResult::OK;
+ return PluginResult::OK;
for(const Json::Value &result_item_json : results_json) {
if(!result_item_json.isObject())
@@ -220,22 +159,22 @@ namespace QuickMedia {
}
}
- body_item->thumbnail_url = relationships_get_cover_art(result_item_json["relationships"]);
+ std::string cover_art_filename = relationships_get_cover_art_filename(data_json["relationships"]);
+ if(!cover_art_filename.empty())
+ body_item->thumbnail_url = "https://uploads.mangadex.org/covers/" + body_item->url + "/" + std::move(cover_art_filename) + ".256.jpg";
+ body_item->thumbnail_size = {101, 141};
result_items.push_back(std::move(body_item));
}
- // Intentionally ignore errors. This api shouldn't fail if we fail to get covers
- get_cover_urls(result_items);
-
- return SearchResult::OK;
+ return PluginResult::OK;
}
SearchResult MangadexSearchPage::search(const std::string &str, BodyItems &result_items) {
- return search(str, 0, result_items);
+ return plugin_result_to_search_result(search_manga(this, SearchType::TITLE, str, 0, result_items));
}
PluginResult MangadexSearchPage::get_page(const std::string &str, int page, BodyItems &result_items) {
- return search_result_to_plugin_result(search(str, page, result_items));
+ return search_manga(this, SearchType::TITLE, str, page, result_items);
}
static PluginResult get_chapters_for_manga(Page *page, const std::string &manga_id, int page_num, BodyItems &result_items, ChapterImageUrls &chapter_image_urls) {
@@ -327,23 +266,103 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ static void get_creators(const Json::Value &relationships_json, std::vector<Creator> &creators) {
+ if(!relationships_json.isArray())
+ return;
+
+ for(const Json::Value &relationship_json : relationships_json) {
+ if(!relationship_json.isObject())
+ continue;
+
+ const Json::Value &id_json = relationship_json["id"];
+ if(!id_json.isString())
+ continue;
+
+ const Json::Value &type_json = relationship_json["type"];
+ if(!type_json.isString() || (strcmp(type_json.asCString(), "author") != 0 && strcmp(type_json.asCString(), "artist") != 0))
+ continue;
+
+ const Json::Value &attributes_json = relationship_json["attributes"];
+ if(!attributes_json.isObject())
+ continue;
+
+ const Json::Value &name_json = attributes_json["name"];
+ if(!name_json.isString())
+ continue;
+
+ Creator creator;
+ creator.name = name_json.asString();
+ creator.url = id_json.asString();
+ creators.push_back(std::move(creator));
+ }
+ }
+
+ static PluginResult get_creators_for_manga(Page *page, const std::string &manga_id, std::vector<Creator> &creators) {
+ std::string request_url = "https://api.mangadex.org/manga/" + manga_id + "?includes[]=artist&includes[]=author";
+
+ Json::Value json_root;
+ if(page->download_json(json_root, request_url, {}, true) != DownloadResult::OK)
+ return PluginResult::NET_ERR;
+
+ if(!json_root.isObject())
+ return PluginResult::OK;
+
+ const Json::Value &result_json = json_root["result"];
+ if(!result_json.isString() || strcmp(result_json.asCString(), "ok") != 0)
+ return PluginResult::ERR;
+
+ const Json::Value &data_json = json_root["data"];
+ if(!data_json.isObject())
+ return PluginResult::ERR;
+
+ get_creators(data_json["relationships"], creators);
+ return PluginResult::OK;
+ }
+
+ // TODO: Do requests in parallel
PluginResult MangadexSearchPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- chapter_image_urls.clear();
- auto body = create_body();
+ PluginResult result;
+
BodyItems body_items;
- get_chapters_for_manga(this, url, 0, body_items, chapter_image_urls);
+ ChapterImageUrls chapter_image_urls;
+ result = get_chapters_for_manga(this, url, 0, body_items, chapter_image_urls);
+ if(result != PluginResult::OK)
+ return result;
+ auto body = create_body();
body->set_items(std::move(body_items));
- result_tabs.push_back(Tab{std::move(body), std::make_unique<MangadexChaptersPage>(program, this, title, url), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ result_tabs.push_back(Tab{std::move(body), std::make_unique<MangadexChaptersPage>(program, this, title, url, std::move(chapter_image_urls)), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+
+ std::vector<Creator> creators;
+ result = get_creators_for_manga(this, url, creators);
+ if(result != PluginResult::OK)
+ return result;
+
+ for(Creator &creator : creators) {
+ result_tabs.push_back(Tab{create_body(), std::make_unique<MangadexCreatorPage>(program, this, std::move(creator)), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
+ }
+
return PluginResult::OK;
}
PluginResult MangadexChaptersPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
- result_tabs.push_back(Tab{nullptr, std::make_unique<MangadexImagesPage>(program, search_page, content_title, url, title), nullptr});
+ result_tabs.push_back(Tab{nullptr, std::make_unique<MangadexImagesPage>(program, search_page, content_title, url, title, &chapter_image_urls), nullptr});
return PluginResult::OK;
}
PluginResult MangadexChaptersPage::get_page(const std::string&, int page, BodyItems &result_items) {
- return get_chapters_for_manga(this, content_url, page, result_items, search_page->chapter_image_urls);
+ return get_chapters_for_manga(this, content_url, page, result_items, chapter_image_urls);
+ }
+
+ PluginResult MangadexCreatorPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) {
+ return search_page->submit(title, url, result_tabs);
+ }
+
+ PluginResult MangadexCreatorPage::get_page(const std::string&, int page, BodyItems &result_items) {
+ return search_manga(this, SearchType::AUTHOR, creator.url, page, result_items);
+ }
+
+ PluginResult MangadexCreatorPage::lazy_fetch(BodyItems &result_items) {
+ return search_manga(this, SearchType::AUTHOR, creator.url, 0, result_items);
}
bool MangadexChaptersPage::extract_id_from_url(const std::string &url, std::string &manga_id) const {
@@ -401,8 +420,8 @@ namespace QuickMedia {
if(!base_url.empty() && base_url.back() != '/')
base_url += '/';
- auto it = search_page->chapter_image_urls.find(url);
- if(it == search_page->chapter_image_urls.end())
+ auto it = chapter_image_url_templates->find(url);
+ if(it == chapter_image_url_templates->end())
return ImageResult::ERR;
chapter_image_urls.resize(it->second.size());