aboutsummaryrefslogtreecommitdiff
path: root/src/QuickMedia.cpp
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-02-03 21:09:26 +0100
committerdec05eba <dec05eba@protonmail.com>2022-02-03 21:09:26 +0100
commit9f12d8a7f6e4cdf0cb95130b69da2b368cc9cbb5 (patch)
tree6b0c85fca617329a94fc94c0f4a247ff7ecd38fd /src/QuickMedia.cpp
parentf7f45ddc492b992cc49a92f620e37316e4d1fed4 (diff)
Add thumbnail to manga history page. Start on new manga history format (one json file)
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r--src/QuickMedia.cpp180
1 files changed, 116 insertions, 64 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index cbe8ea4..a5c423d 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -204,6 +204,7 @@ namespace QuickMedia {
LazyFetchPage(program), search_page(search_page), history_type(history_type) {}
const char* get_title() const override { return "History"; }
PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) override {
+ search_page->submit_body_item = submit_body_item;
return search_page->submit(title, url, result_tabs);
}
PluginResult lazy_fetch(BodyItems &result_items) override {
@@ -1064,7 +1065,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1075,7 +1076,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1086,7 +1087,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1097,7 +1098,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1108,7 +1109,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1119,7 +1120,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1130,7 +1131,7 @@ namespace QuickMedia {
tabs.push_back(Tab{create_body(), std::make_unique<BookmarksPage>(this, search_page.get()), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
tabs.push_back(Tab{create_body(), std::move(search_page), create_search_bar("Search...", 400)});
- auto history_page = std::make_unique<HistoryPage>(this, tabs.front().page.get(), HistoryType::MANGA);
+ auto history_page = std::make_unique<HistoryPage>(this, tabs.back().page.get(), HistoryType::MANGA);
tabs.push_back(Tab{create_body(), std::move(history_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)});
start_tab_index = 1;
@@ -1330,27 +1331,41 @@ namespace QuickMedia {
LOGIN
};
- static void fill_history_items_from_json(const Json::Value &history_json, BodyItems &history_items) {
+ static void fill_youtube_history_items_from_json(const Json::Value &history_json, BodyItems &history_items) {
assert(history_json.isArray());
- time_t time_now = time(NULL);
+ std::vector<const Json::Value*> history_json_items;
for(const Json::Value &item : history_json) {
if(!item.isObject())
continue;
- const Json::Value &video_id = item["id"];
+ const Json::Value &timestamp = item["timestamp"];
+ if(!timestamp.isNumeric())
+ continue;
+
+ history_json_items.push_back(&item);
+ }
+
+ std::sort(history_json_items.begin(), history_json_items.end(), [](const Json::Value *val1, const Json::Value *val2) {
+ const Json::Value &timestamp1 = (*val1)["timestamp"];
+ const Json::Value &timestamp2 = (*val2)["timestamp"];
+ return timestamp1.asInt64() > timestamp2.asInt64();
+ });
+
+ time_t time_now = time(NULL);
+ for(const Json::Value *item : history_json_items) {
+ const Json::Value &video_id = (*item)["id"];
if(!video_id.isString())
continue;
- std::string video_id_str = video_id.asString();
- const Json::Value &title = item["title"];
+ const Json::Value &title = (*item)["title"];
if(!title.isString())
continue;
- std::string title_str = title.asString();
- const Json::Value &timestamp = item["timestamp"];
- if(!timestamp.isNumeric())
- continue;
+ const Json::Value &timestamp = (*item)["timestamp"];
+
+ std::string title_str = title.asString();
+ std::string video_id_str = video_id.asString();
auto body_item = BodyItem::create(std::move(title_str));
body_item->url = "https://www.youtube.com/watch?v=" + video_id_str;
@@ -1360,32 +1375,28 @@ namespace QuickMedia {
body_item->thumbnail_size = mgl::vec2i(192, 108);
history_items.push_back(std::move(body_item));
}
-
- std::reverse(history_items.begin(), history_items.end());
}
- static Path get_video_history_filepath(const char *plugin_name) {
- Path video_history_dir = get_storage_dir().join("history");
- if(create_directory_recursive(video_history_dir) != 0) {
- std::string err_msg = "Failed to create video history directory ";
- err_msg += video_history_dir.data;
- show_notification("QuickMedia", err_msg.c_str(), Urgency::CRITICAL);
+ static Path get_history_filepath(const char *plugin_name) {
+ Path history_dir = get_storage_dir().join("history");
+ if(create_directory_recursive(history_dir) != 0) {
+ show_notification("QuickMedia", "Failed to create history directory " + history_dir.data, Urgency::CRITICAL);
exit(1);
}
- Path video_history_filepath = video_history_dir;
- return video_history_filepath.join(plugin_name).append(".json");
+ Path history_filepath = history_dir;
+ return history_filepath.join(plugin_name).append(".json");
}
// This is not cached because we could have multiple instances of QuickMedia running the same plugin!
// TODO: Find a way to optimize this
- Json::Value Program::load_video_history_json() {
- Path video_history_filepath = get_video_history_filepath(plugin_name);
+ Json::Value Program::load_history_json() {
+ Path history_filepath = get_history_filepath(plugin_name);
Json::Value json_result;
- FileType file_type = get_file_type(video_history_filepath);
+ FileType file_type = get_file_type(history_filepath);
if(file_type == FileType::REGULAR) {
- if(!read_file_as_json(video_history_filepath, json_result) || !json_result.isArray()) {
- show_notification("QuickMedia", "Failed to read " + video_history_filepath.data, Urgency::CRITICAL);
+ if(!read_file_as_json(history_filepath, json_result) || !json_result.isArray()) {
+ show_notification("QuickMedia", "Failed to read " + history_filepath.data, Urgency::CRITICAL);
abort();
}
} else {
@@ -1406,8 +1417,21 @@ namespace QuickMedia {
abort();
}
+ Json::Value history_json = load_history_json();
+ std::unordered_map<std::string, std::string> manga_id_to_thumbnail_url_map;
+ for(const Json::Value &history_item : history_json) {
+ const Json::Value &id = history_item["id"];
+ const Json::Value &thumbnail_url = history_item["thumbnail_url"];
+
+ if(!id.isString() || !thumbnail_url.isString())
+ continue;
+
+ manga_id_to_thumbnail_url_map[id.asString()] = thumbnail_url.asString();
+ }
+
+ // TODO: Remove this once manga history file has been in use for a few months and is filled with history
time_t now = time(NULL);
- for_files_in_dir_sort_last_modified(content_storage_dir, [&history_items, plugin_name, now](const Path &filepath) {
+ for_files_in_dir_sort_last_modified(content_storage_dir, [&history_items, plugin_name, &manga_id_to_thumbnail_url_map, now](const Path &filepath) {
// This can happen when QuickMedia crashes/is killed while writing to storage.
// In that case, the storage wont be corrupt but there will be .tmp files.
// TODO: Remove these .tmp files if they exist during startup
@@ -1423,6 +1447,7 @@ namespace QuickMedia {
// TODO: Manga combined
const char *filename = filepath.filename();
+ std::string manga_id = base64_url_decode(filename);
const Json::Value &manga_name = body["name"];
if(!manga_name.isString())
@@ -1436,20 +1461,26 @@ namespace QuickMedia {
body_item->set_description("Last read " + seconds_to_relative_time_str(now - last_modified_time));
body_item->set_description_color(get_theme().faded_text_color);
+ auto thumbnail_it = manga_id_to_thumbnail_url_map.find(manga_id);
+ if(thumbnail_it != manga_id_to_thumbnail_url_map.end()) {
+ body_item->thumbnail_url = thumbnail_it->second;
+ body_item->thumbnail_size = {101, 141};
+ }
+
if(strcmp(plugin_name, "manganelo") == 0)
- body_item->url = "https://manganelo.com/manga/" + base64_url_decode(filename);
+ body_item->url = "https://manganelo.com/manga/" + manga_id;
else if(strcmp(plugin_name, "manganelos") == 0)
- body_item->url = "http://manganelos.com/manga/" + base64_url_decode(filename);
+ body_item->url = "http://manganelos.com/manga/" + manga_id;
else if(strcmp(plugin_name, "mangadex") == 0)
- body_item->url = base64_url_decode(filename);
+ body_item->url = manga_id;
else if(strcmp(plugin_name, "mangatown") == 0)
- body_item->url = "https://mangatown.com/manga/" + base64_url_decode(filename);
+ body_item->url = "https://mangatown.com/manga/" + manga_id;
else if(strcmp(plugin_name, "mangakatana") == 0)
- body_item->url = "https://mangakatana.com/manga/" + base64_url_decode(filename);
+ body_item->url = "https://mangakatana.com/manga/" + manga_id;
else if(strcmp(plugin_name, "onimanga") == 0)
- body_item->url = "https://onimanga.com/" + base64_url_decode(filename);
+ body_item->url = "https://onimanga.com/" + manga_id;
else if(strcmp(plugin_name, "readm") == 0)
- body_item->url = "https://readm.org/manga/" + base64_url_decode(filename);
+ body_item->url = "https://readm.org/manga/" + manga_id;
else
fprintf(stderr, "Error: Not implemented: filename to manga chapter list\n");
@@ -1459,7 +1490,7 @@ namespace QuickMedia {
}
void Program::youtube_get_watch_history(BodyItems &history_items) {
- fill_history_items_from_json(load_video_history_json(), history_items);
+ fill_youtube_history_items_from_json(load_history_json(), history_items);
}
static void get_body_dimensions(const mgl::vec2i &window_size, SearchBar *search_bar, mgl::vec2f &body_pos, mgl::vec2f &body_size, bool has_tabs = false) {
@@ -1494,6 +1525,7 @@ namespace QuickMedia {
bool Program::load_manga_content_storage(const char *service_name, const std::string &manga_title, const std::string &manga_url, const std::string &manga_id) {
Path content_storage_dir = get_storage_dir().join(service_name);
+ this->manga_id = manga_id;
manga_id_base64 = base64_url_encode(manga_id);
content_storage_file = content_storage_dir.join(manga_id_base64);
content_storage_json.clear();
@@ -1629,7 +1661,9 @@ namespace QuickMedia {
}
std::string bookmark_title = body_item->get_title();
- if(bookmark_title.empty()) bookmark_title = body_item->get_author();
+ if(bookmark_title.empty())
+ bookmark_title = body_item->get_author();
+
show_notification("QuickMedia", "Removed " + bookmark_title + " from bookmarks");
removed = true;
return true;
@@ -2357,12 +2391,10 @@ namespace QuickMedia {
return false;
}
- static int watch_history_get_item_by_id(const Json::Value &video_history_json, const char *id) {
- assert(video_history_json.isArray());
+ static Json::Value* history_get_item_by_id(Json::Value &history_json, const char *id) {
+ assert(history_json.isArray());
- int index = -1;
- for(const Json::Value &item : video_history_json) {
- ++index;
+ for(Json::Value &item : history_json) {
if(!item.isObject())
continue;
@@ -2371,10 +2403,10 @@ namespace QuickMedia {
continue;
if(strcmp(id, id_json.asCString()) == 0)
- return index;
+ return &item;
}
- return -1;
+ return nullptr;
}
enum class WindowFullscreenState {
@@ -2924,25 +2956,21 @@ namespace QuickMedia {
return;
}
- Json::Value video_history_json = load_video_history_json();
+ Json::Value video_history_json = load_history_json();
- int existing_index = watch_history_get_item_by_id(video_history_json, video_id.c_str());
- if(existing_index != -1) {
- Json::Value removed;
- /* TODO: Optimize. This is slow */
- video_history_json.removeIndex(existing_index, &removed);
- }
-
time_t time_now = time(NULL);
+ Json::Value *json_item = history_get_item_by_id(video_history_json, video_id.c_str());
+ if(json_item) {
+ (*json_item)["timestamp"] = (Json::Int64)time_now;
+ } else {
+ Json::Value new_content_object(Json::objectValue);
+ new_content_object["id"] = video_id;
+ new_content_object["title"] = video_title;
+ new_content_object["timestamp"] = (Json::Int64)time_now;
+ video_history_json.append(std::move(new_content_object));
+ }
- Json::Value new_content_object(Json::objectValue);
- new_content_object["id"] = video_id;
- new_content_object["title"] = video_title;
- new_content_object["timestamp"] = (Json::Int64)time_now;
-
- video_history_json.append(std::move(new_content_object));
-
- Path video_history_filepath = get_video_history_filepath(plugin_name);
+ Path video_history_filepath = get_history_filepath(plugin_name);
save_json_to_file_atomic(video_history_filepath, video_history_json);
}
};
@@ -3483,6 +3511,28 @@ namespace QuickMedia {
}
}
+ void Program::update_manga_history(const std::string &manga_id, const std::string &thumbnail_url) {
+ Json::Value history_json = load_history_json();
+ const time_t time_now = time(NULL);
+
+ Json::Value *json_item = history_get_item_by_id(history_json, manga_id.c_str());
+ if(json_item) {
+ (*json_item)["timestamp"] = (Json::Int64)time_now;
+ if(!thumbnail_url.empty())
+ (*json_item)["thumbnail_url"] = thumbnail_url;
+ } else {
+ Json::Value new_content_object(Json::objectValue);
+ new_content_object["id"] = manga_id;
+ new_content_object["timestamp"] = (Json::Int64)time_now;
+ if(!thumbnail_url.empty())
+ new_content_object["thumbnail_url"] = thumbnail_url;
+ history_json.append(std::move(new_content_object));
+ }
+
+ Path history_filepath = get_history_filepath(plugin_name);
+ save_json_to_file_atomic(history_filepath, history_json);
+ }
+
void Program::save_manga_progress(MangaImagesPage *images_page, Json::Value &json_chapters, Json::Value &json_chapter, int &latest_read) {
image_index = std::max(0, std::min(image_index, num_manga_pages));
@@ -3540,6 +3590,7 @@ namespace QuickMedia {
Json::Value json_chapter;
int latest_read;
save_manga_progress(images_page, json_chapters, json_chapter, latest_read);
+ update_manga_history(manga_id, images_page->thumbnail_url);
if(image_index < num_manga_pages) {
std::string error_msg;
@@ -3751,6 +3802,7 @@ namespace QuickMedia {
if(!save_json_to_file_atomic(content_storage_file, content_storage_json)) {
show_notification("QuickMedia", "Failed to save manga progress", Urgency::CRITICAL);
}
+ update_manga_history(manga_id, images_page->thumbnail_url);
}
}