From acb6ac0a04e800a79876908fd1fdb98dc7e93678 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 13 May 2021 23:30:20 +0200 Subject: Show local time for nyaa.si, soundcloud and spotify. Add sukebei --- src/plugins/NyaaSi.cpp | 138 +++++++++++++++++++++++++++++++-------------- src/plugins/Soundcloud.cpp | 20 +++++++ src/plugins/Spotify.cpp | 11 +++- src/plugins/Youtube.cpp | 22 +------- 4 files changed, 128 insertions(+), 63 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp index 7a84346..2afedb3 100644 --- a/src/plugins/NyaaSi.cpp +++ b/src/plugins/NyaaSi.cpp @@ -39,16 +39,86 @@ namespace QuickMedia { return body_item; } + void get_nyaa_si_categories(BodyItems &result_items) { + result_items.push_back(create_front_page_item("All categories", "0_0")); + result_items.push_back(create_front_page_item("Anime", "1_0")); + result_items.push_back(create_front_page_item(" Anime - Music video", "1_1")); + result_items.push_back(create_front_page_item(" Anime - English translated", "1_2")); + result_items.push_back(create_front_page_item(" Anime - Non-english translated", "1_3")); + result_items.push_back(create_front_page_item(" Anime - Raw", "1_4")); + result_items.push_back(create_front_page_item("Audio", "2_0")); + result_items.push_back(create_front_page_item(" Audio - Lossless", "2_1")); + result_items.push_back(create_front_page_item(" Anime - Lossy", "2_2")); + result_items.push_back(create_front_page_item("Literature", "3_0")); + result_items.push_back(create_front_page_item(" Literature - English translated", "3_1")); + result_items.push_back(create_front_page_item(" Literature - Non-english translated", "3_1")); + result_items.push_back(create_front_page_item(" Literature - Raw", "3_3")); + result_items.push_back(create_front_page_item("Live Action", "4_0")); + result_items.push_back(create_front_page_item(" Live Action - English translated", "4_1")); + result_items.push_back(create_front_page_item(" Live Action - Non-english translated", "4_3")); + result_items.push_back(create_front_page_item(" Live Action - Idol/Promotional video", "4_2")); + result_items.push_back(create_front_page_item(" Live Action - Raw", "4_4")); + result_items.push_back(create_front_page_item("Pictures", "5_0")); + result_items.push_back(create_front_page_item(" Pictures - Graphics", "5_1")); + result_items.push_back(create_front_page_item(" Pictures - Photos", "5_2")); + result_items.push_back(create_front_page_item("Software", "6_0")); + result_items.push_back(create_front_page_item(" Software - Applications", "6_1")); + result_items.push_back(create_front_page_item(" Software - Games", "6_2")); + } + + void get_sukebei_categories(BodyItems &result_items) { + result_items.push_back(create_front_page_item("All categories", "0_0")); + result_items.push_back(create_front_page_item("Art", "1_0")); + result_items.push_back(create_front_page_item(" Anime", "1_1")); + result_items.push_back(create_front_page_item(" Doujinshi", "1_2")); + result_items.push_back(create_front_page_item(" Games", "1_3")); + result_items.push_back(create_front_page_item(" Manga", "1_4")); + result_items.push_back(create_front_page_item(" Pictures", "1_5")); + result_items.push_back(create_front_page_item("Real Life", "2_0")); + result_items.push_back(create_front_page_item(" Photobooks and Pictures", "2_1")); + result_items.push_back(create_front_page_item(" Videos", "2_2")); + } + + static time_t nyaa_si_time_to_unix_time(const char *time_str) { + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int minute = 0; + sscanf(time_str, "%d-%d-%d %d:%d", &year, &month, &day, &hour, &minute); + if(year == 0) return 0; + + struct tm time; + memset(&time, 0, sizeof(time)); + time.tm_year = year - 1900; + time.tm_mon = month - 1; + time.tm_mday = day; + time.tm_hour = hour; + time.tm_min = minute; + time.tm_sec = 0; + return timegm(&time); + } + + static std::string unix_time_to_local_time_str(time_t unix_time) { + struct tm time_tm; + localtime_r(&unix_time, &time_tm); + char time_str[128] = {0}; + strftime(time_str, sizeof(time_str) - 1, "%Y-%m-%d %H:%M", &time_tm); + return time_str; + } + // TODO: Also show the number of comments for each torrent. TODO: Optimize? // TODO: Show each field as seperate columns instead of seperating by | - static SearchResult search_page(const std::string &list_url, const std::string &text, int page, BodyItems &result_items) { - std::string full_url = "https://nyaa.si/?c=" + list_url + "&f=0&p=" + std::to_string(page) + "&q="; + static SearchResult search_page(const std::string &domain, const std::string &list_url, const std::string &text, int page, BodyItems &result_items) { + std::string full_url = "https://" + domain + "/?c=" + list_url + "&f=0&p=" + std::to_string(page) + "&q="; full_url += url_param_encode(text); std::string website_data; if(download_to_string(full_url, website_data, {}, true) != DownloadResult::OK) return SearchResult::NET_ERR; + const bool is_sukebei = (domain == "sukebei.nyaa.si"); + size_t tbody_begin = website_data.find(""); if(tbody_begin == std::string::npos) return SearchResult::OK; @@ -161,11 +231,11 @@ namespace QuickMedia { index = tr_end + 5; - std::string description = "Size: " + size + " | Published: " + timestamp + " | Seeders: " + seeders + " | Leechers: " + leechers + " | Completed: " + completed; + std::string description = "Size: " + size + " | Published: " + unix_time_to_local_time_str(nyaa_si_time_to_unix_time(timestamp.c_str())) + " | Seeders: " + seeders + " | Leechers: " + leechers + " | Completed: " + completed; auto body_item = BodyItem::create(std::move(title)); - body_item->thumbnail_url = "https://nyaa.si/static/img/icons/nyaa/" + website_data.substr(category_begin + 4, category_end - (category_begin + 4)) + ".png"; + body_item->thumbnail_url = "https://" + domain + "/static/img/icons/" + (is_sukebei ? "sukebei" : "nyaa") + "/" + website_data.substr(category_begin + 4, category_end - (category_begin + 4)) + ".png"; body_item->set_description(std::move(description)); - body_item->url = "https://nyaa.si" + std::move(view_url); + body_item->url = "https://" + domain + std::move(view_url); if(is_trusted) body_item->set_title_color(sf::Color(43, 255, 47)); else if(is_remake) @@ -178,51 +248,31 @@ namespace QuickMedia { } PluginResult NyaaSiCategoryPage::submit(const std::string &title, const std::string &url, std::vector &result_tabs) { + std::string domain = is_sukebei ? "sukebei.nyaa.si" : "nyaa.si"; + BodyItems result_items; - SearchResult search_result = search_page(url, "", 1, result_items); + SearchResult search_result = search_page(domain, url, "", 1, result_items); if(search_result != SearchResult::OK) return search_result_to_plugin_result(search_result); auto body = create_body(); body->items = std::move(result_items); - result_tabs.push_back(Tab{std::move(body), std::make_unique(program, strip(title), url), create_search_bar("Search...", 300)}); + result_tabs.push_back(Tab{std::move(body), std::make_unique(program, strip(title), url, std::move(domain)), create_search_bar("Search...", 300)}); return PluginResult::OK; } - void NyaaSiCategoryPage::get_categories(BodyItems &result_items) { - result_items.push_back(create_front_page_item("All categories", "0_0")); - result_items.push_back(create_front_page_item("Anime", "1_0")); - result_items.push_back(create_front_page_item(" Anime - Music video", "1_1")); - result_items.push_back(create_front_page_item(" Anime - English translated", "1_2")); - result_items.push_back(create_front_page_item(" Anime - Non-english translated", "1_3")); - result_items.push_back(create_front_page_item(" Anime - Raw", "1_4")); - result_items.push_back(create_front_page_item("Audio", "2_0")); - result_items.push_back(create_front_page_item(" Audio - Lossless", "2_1")); - result_items.push_back(create_front_page_item(" Anime - Lossy", "2_2")); - result_items.push_back(create_front_page_item("Literature", "3_0")); - result_items.push_back(create_front_page_item(" Literature - English translated", "3_1")); - result_items.push_back(create_front_page_item(" Literature - Non-english translated", "3_1")); - result_items.push_back(create_front_page_item(" Literature - Raw", "3_3")); - result_items.push_back(create_front_page_item("Live Action", "4_0")); - result_items.push_back(create_front_page_item(" Live Action - English translated", "4_1")); - result_items.push_back(create_front_page_item(" Live Action - Non-english translated", "4_3")); - result_items.push_back(create_front_page_item(" Live Action - Idol/Promotional video", "4_2")); - result_items.push_back(create_front_page_item(" Live Action - Raw", "4_4")); - result_items.push_back(create_front_page_item("Pictures", "5_0")); - result_items.push_back(create_front_page_item(" Pictures - Graphics", "5_1")); - result_items.push_back(create_front_page_item(" Pictures - Photos", "5_2")); - result_items.push_back(create_front_page_item("Software", "6_0")); - result_items.push_back(create_front_page_item(" Software - Applications", "6_1")); - result_items.push_back(create_front_page_item(" Software - Games", "6_2")); - } - SearchResult NyaaSiSearchPage::search(const std::string &str, BodyItems &result_items) { - return search_page(category_id, str, 1, result_items); + return search_page(domain, category_id, str, 1, result_items); } PluginResult NyaaSiSearchPage::get_page(const std::string &str, int page, BodyItems &result_items) { - return search_result_to_plugin_result(search_page(category_id, str, 1 + page, result_items)); + return search_result_to_plugin_result(search_page(domain, category_id, str, 1 + page, result_items)); } + struct ResultItemExtra { + BodyItems *result_items; + const std::string *domain; + }; + PluginResult NyaaSiSearchPage::submit(const std::string&, const std::string &url, std::vector &result_tabs) { size_t comments_start_index; std::string title; @@ -232,6 +282,10 @@ namespace QuickMedia { std::string magnet_url; std::string description; + ResultItemExtra result_item_extra; + result_item_extra.result_items = &result_items; + result_item_extra.domain = &domain; + std::string website_data; if(download_to_string(url, website_data, {}, true) != DownloadResult::OK) return PluginResult::NET_ERR; @@ -261,16 +315,16 @@ namespace QuickMedia { result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='panel-body']//div[class='row']//a", [](QuickMediaHtmlNode *node, void *userdata) { - auto *item_data = (BodyItems*)userdata; + ResultItemExtra *item_data = (ResultItemExtra*)userdata; const char *href = quickmedia_html_node_get_attribute_value(node, "href"); const char *text = quickmedia_html_node_get_text(node); - if(item_data->empty() && href && text && strncmp(href, "/user/", 6) == 0) { + if(item_data->result_items->empty() && href && text && strncmp(href, "/user/", 6) == 0) { auto body_item = BodyItem::create(""); body_item->set_description("Submitter: " + strip(text)); - body_item->url = "https://nyaa.si/" + std::string(href); - item_data->push_back(std::move(body_item)); + body_item->url = "https://" + *item_data->domain + "/" + std::string(href); + item_data->result_items->push_back(std::move(body_item)); } - }, &result_items); + }, &result_item_extra); if(result != 0) goto cleanup; @@ -313,7 +367,7 @@ namespace QuickMedia { goto cleanup; if(magnet_url.empty()) { - fprintf(stderr, "Error: nyaa.si: failed to get magnet link\n"); + fprintf(stderr, "Error: %s: failed to get magnet link\n", domain.c_str()); result = -1; goto cleanup; } diff --git a/src/plugins/Soundcloud.cpp b/src/plugins/Soundcloud.cpp index 9a5fe64..9c2d5f4 100644 --- a/src/plugins/Soundcloud.cpp +++ b/src/plugins/Soundcloud.cpp @@ -1,6 +1,7 @@ #include "../../plugins/Soundcloud.hpp" #include "../../include/NetUtils.hpp" #include "../../include/StringUtils.hpp" +#include "../../include/Utils.hpp" #include "../../include/Scale.hpp" #include @@ -64,6 +65,14 @@ namespace QuickMedia { return ""; } + static std::string unix_time_to_local_time_str(time_t unix_time) { + struct tm time_tm; + localtime_r(&unix_time, &time_tm); + char time_str[128] = {0}; + strftime(time_str, sizeof(time_str) - 1, "%Y-%m-%d %H:%M", &time_tm); + return time_str; + } + static std::shared_ptr parse_collection_item(const Json::Value &item_json) { std::string title; @@ -105,6 +114,12 @@ namespace QuickMedia { auto body_item = BodyItem::create(std::move(title)); std::string description; + const Json::Value &last_modified_json = item_json["last_modified"]; + if(last_modified_json.isString()) { + const time_t unix_time = iso_utc_to_unix_time(last_modified_json.asCString()); + description = "Updated " + unix_time_to_local_time_str(unix_time); + } + const Json::Value &media_json = item_json["media"]; if(media_json.isObject()) body_item->url = get_best_transcoding_audio_url(media_json); @@ -129,6 +144,8 @@ namespace QuickMedia { } num_tracks = tracks_json.size(); + if(!description.empty()) + description += '\n'; description = "Playlist with " + std::to_string(num_tracks) + " track" + (num_tracks == 1 ? "" : "s"); body_item->extra = std::move(playlist); body_item->url = "track"; @@ -167,6 +184,9 @@ namespace QuickMedia { body_item->thumbnail_size.x = 100; body_item->thumbnail_size.y = 100; body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; + } else { + body_item->thumbnail_size.x = 100; + body_item->thumbnail_size.y = 100; } if(username_json.isString()) { diff --git a/src/plugins/Spotify.cpp b/src/plugins/Spotify.cpp index f56ed6c..d41446b 100644 --- a/src/plugins/Spotify.cpp +++ b/src/plugins/Spotify.cpp @@ -1,5 +1,6 @@ #include "../../plugins/Spotify.hpp" #include "../../include/NetUtils.hpp" +#include "../../include/Utils.hpp" #include "../../include/Scale.hpp" namespace QuickMedia { @@ -171,6 +172,14 @@ namespace QuickMedia { return result; } + static std::string unix_time_to_local_time_str(time_t unix_time) { + struct tm time_tm; + localtime_r(&unix_time, &time_tm); + char time_str[128] = {0}; + strftime(time_str, sizeof(time_str) - 1, "%Y-%m-%d %H:%M", &time_tm); + return time_str; + } + PluginResult SpotifyEpisodeListPage::get_page(const std::string &, int page, BodyItems &result_items) { std::string request_url = "https://api-partner.spotify.com/pathfinder/v1/query?operationName=queryShowEpisodes&variables="; request_url += url_param_encode("{\"uri\":\"" + url + "\",\"offset\":" + std::to_string(page * 50) + ",\"limit\":50}"); @@ -242,7 +251,7 @@ namespace QuickMedia { if(release_data_json.isObject()) { const Json::Value &iso_string_json = release_data_json["isoString"]; if(iso_string_json.isString()) - time += iso_string_json.asString(); + time += unix_time_to_local_time_str(iso_utc_to_unix_time(iso_string_json.asCString())); } const Json::Value &duration_json = episode_json["duration"]; diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 0fcf61d..a93dcb6 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -4,6 +4,7 @@ #include "../../include/StringUtils.hpp" #include "../../include/Scale.hpp" #include "../../include/Notification.hpp" +#include "../../include/Utils.hpp" extern "C" { #include } @@ -1375,26 +1376,7 @@ namespace QuickMedia { subscription_data.subscription_entry.back().video_id.assign(html_parser->text_stripped.data, html_parser->text_stripped.size); } else if(string_view_equals(&html_parser->tag_name, "published") && parse_type == HTML_PARSE_TAG_END) { std::string published_str(html_parser->text_stripped.data, html_parser->text_stripped.size); - - int year = 0; - int month = 0; - int day = 0; - int hour = 0; - int minute = 0; - int second = 0; - sscanf(published_str.c_str(), "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second); - if(year == 0) return; - - struct tm time; - memset(&time, 0, sizeof(time)); - time.tm_year = year - 1900; - time.tm_mon = month - 1; - time.tm_mday = day; - time.tm_hour = hour; - time.tm_min = minute; - time.tm_sec = second; - - subscription_data.subscription_entry.back().published = timegm(&time); + subscription_data.subscription_entry.back().published = iso_utc_to_unix_time(published_str.c_str()); } }, &subscription_data); -- cgit v1.2.3