aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/Body.hpp18
-rw-r--r--src/Body.cpp31
-rw-r--r--src/Program.c2
-rw-r--r--src/QuickMedia.cpp50
-rw-r--r--src/plugins/Mangadex.cpp13
-rw-r--r--src/plugins/Mangatown.cpp2
-rw-r--r--src/plugins/Youtube.cpp18
7 files changed, 107 insertions, 27 deletions
diff --git a/include/Body.hpp b/include/Body.hpp
index db502c5..c487ad9 100644
--- a/include/Body.hpp
+++ b/include/Body.hpp
@@ -19,6 +19,7 @@ namespace QuickMedia {
BodyItem(const BodyItem &other) {
title = other.title;
+ description = other.description;
url = other.url;
thumbnail_url = other.thumbnail_url;
attached_content_url = other.attached_content_url;
@@ -29,6 +30,10 @@ namespace QuickMedia {
title_text = std::make_unique<Text>(*other.title_text);
else
title_text = nullptr;
+ if(other.description_text)
+ description_text = std::make_unique<Text>(*other.description_text);
+ else
+ description_text = nullptr;
replies = other.replies;
post_number = other.post_number;
}
@@ -38,7 +43,14 @@ namespace QuickMedia {
dirty = true;
}
- std::string title;
+ void set_description(std::string new_description) {
+ description = std::move(new_description);
+ }
+
+ const std::string& get_title() const { return title; }
+ const std::string& get_description() const { return description; }
+
+ // TODO: Use a list of strings instead, not all plugins need all of these fields
std::string url;
std::string thumbnail_url;
std::string attached_content_url;
@@ -46,9 +58,13 @@ namespace QuickMedia {
bool visible;
bool dirty;
std::unique_ptr<Text> title_text;
+ std::unique_ptr<Text> description_text;
// Used by image boards for example. The elements are indices to other body items
std::vector<size_t> replies;
std::string post_number;
+ private:
+ std::string title;
+ std::string description;
};
using BodyItems = std::vector<std::unique_ptr<BodyItem>>;
diff --git a/src/Body.cpp b/src/Body.cpp
index 0297072..b4ab917 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -173,11 +173,16 @@ namespace QuickMedia {
if(body_item->dirty) {
body_item->dirty = false;
if(body_item->title_text)
- body_item->title_text->setString(body_item->title);
+ body_item->title_text->setString(body_item->get_title());
else
- body_item->title_text = std::make_unique<Text>(body_item->title, font, 14, size.x - 50 - image_padding_x * 2.0f);
+ body_item->title_text = std::make_unique<Text>(body_item->get_title(), bold_font, 16, size.x - 50 - image_padding_x * 2.0f);
//body_item->title_text->updateGeometry(); // TODO: Call this to make getHeight work on first frame (called below)
}
+
+ if(!body_item->get_description().empty() && !body_item->description_text) {
+ body_item->description_text = std::make_unique<Text>(body_item->get_description(), font, 14, size.x - 50 - image_padding_x * 2.0f);
+ body_item->description_text->updateGeometry();
+ }
}
// Find the starting row that can be drawn to make selected row visible as well
@@ -191,6 +196,9 @@ namespace QuickMedia {
if(!item->author.empty()) {
item_height += author_text.getCharacterSize() + 2.0f;
}
+ if(item->description_text) {
+ item_height += item->description_text->getHeight();
+ }
if(draw_thumbnails && !item->thumbnail_url.empty()) {
auto &item_thumbnail = item_thumbnail_textures[item->thumbnail_url];
item_thumbnail.referenced = false;
@@ -234,6 +242,9 @@ namespace QuickMedia {
if(!item->author.empty()) {
item_height += author_text.getCharacterSize() + 2.0f;
}
+ if(item->description_text) {
+ item_height += item->description_text->getHeight();
+ }
if(draw_thumbnails && !item->thumbnail_url.empty()) {
float image_height = image_max_height;
if(item_thumbnail.texture && item_thumbnail.texture->getNativeHandle() != 0) {
@@ -318,13 +329,19 @@ namespace QuickMedia {
//title_text.setString(item->title);
//title_text.setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y));
//window.draw(title_text);
- item->title_text->setString(item->title);
item->title_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 4.0f));
item->title_text->setMaxWidth(size.x - text_offset_x - image_padding_x * 2.0f);
item->title_text->draw(window);
- // TODO: Do the same for non-manga content
- const Json::Value &item_progress = content_progress[item->title];
+ if(item->description_text) {
+ item->description_text->setPosition(std::floor(item_pos.x + text_offset_x), std::floor(item_pos.y + padding_y - 4.0f + item->title_text->getHeight()));
+ item->description_text->setMaxWidth(size.x - text_offset_x - image_padding_x * 2.0f);
+ item->description_text->draw(window);
+ }
+
+ // TODO: Do the same for non-manga content.
+ // TODO: Cache this instead of hash access every item every frame.
+ const Json::Value &item_progress = content_progress[item->get_title()];
if(item_progress.isObject()) {
const Json::Value &current_json = item_progress["current"];
const Json::Value &total_json = item_progress["total"];
@@ -366,7 +383,9 @@ namespace QuickMedia {
}
for(auto &item : items) {
- item->visible = string_find_case_insensitive(item->title, text);
+ item->visible = string_find_case_insensitive(item->get_title(), text);
+ if(!item->visible && !item->get_description().empty())
+ item->visible = string_find_case_insensitive(item->get_description(), text);
}
}
diff --git a/src/Program.c b/src/Program.c
index 47b0e33..fe2ae3f 100644
--- a/src/Program.c
+++ b/src/Program.c
@@ -53,7 +53,7 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
int result = 0;
int status;
- char buffer[2048];
+ char buffer[4097];
for(;;) {
ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer) - 1);
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index a8788d2..da280d8 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -101,7 +101,7 @@ namespace QuickMedia {
if(!selected_item)
return SearchResult::ERR;
- selected_title = selected_item->title;
+ selected_title = selected_item->get_title();
selected_url = selected_item->url;
if(!skip_search) {
output_body->clear_items();
@@ -366,11 +366,34 @@ namespace QuickMedia {
RECOMMENDED
};
+ // Returns relative time as a string (approximation)
+ static std::string timestamp_to_relative_time_str(time_t seconds) {
+ time_t minutes = seconds / 60;
+ time_t hours = minutes / 60;
+ time_t days = hours / 24;
+ time_t months = days / 30;
+ time_t years = days / 365;
+
+ if(years >= 1)
+ return std::to_string(years) + " year" + (years == 1 ? "" : "s") + " ago";
+ else if(months >= 1)
+ return std::to_string(months) + " month" + (months == 1 ? "" : "s") + " ago";
+ else if(days >= 1)
+ return std::to_string(days) + " day" + (days == 1 ? "" : "s") + " ago";
+ else if(hours >= 1)
+ return std::to_string(hours) + " hour" + (hours == 1 ? "" : "s") + " ago";
+ else if(minutes >= 1)
+ return std::to_string(minutes) + " minute" + (minutes == 1 ? "" : "s") + " ago";
+ else
+ return std::to_string(seconds) + " second" + (seconds == 1 ? "" : "s") + " ago";
+ }
+
static void fill_history_items_from_json(const Json::Value &history_json, BodyItems &history_items) {
assert(history_json.isArray());
BodyItems body_items;
+ time_t time_now = time(NULL);
for(const Json::Value &item : history_json) {
if(!item.isObject())
continue;
@@ -385,13 +408,14 @@ namespace QuickMedia {
continue;
std::string title_str = title.asString();
- //const Json::Value &timestamp = item["timestamp"];
- //if(!timestamp.isNumeric())
- // continue;
+ const Json::Value &timestamp = item["timestamp"];
+ if(!timestamp.isNumeric())
+ continue;
auto body_item = std::make_unique<BodyItem>(std::move(title_str));
body_item->url = "https://www.youtube.com/watch?v=" + video_id_str;
body_item->thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg";
+ body_item->set_description(timestamp_to_relative_time_str(std::max(0l, time_now - timestamp.asInt64())));
body_items.push_back(std::move(body_item));
}
@@ -1024,7 +1048,7 @@ namespace QuickMedia {
existing_recommendation["recommended_timestamp"] = time_now;
} else {
Json::Value new_content_object(Json::objectValue);
- new_content_object["title"] = body_item->title;
+ new_content_object["title"] = body_item->get_title();
new_content_object["recommended_timestamp"] = time_now;
new_content_object["recommended_count"] = 1;
recommended_json[recommended_video_id] = std::move(new_content_object);
@@ -1155,7 +1179,7 @@ namespace QuickMedia {
for(auto it = related_media_body->items.begin(), end = related_media_body->items.end(); it != end; ++it) {
if(watched_videos.find((*it)->url) == watched_videos.end()) {
new_video_url = (*it)->url;
- new_video_title = (*it)->title;
+ new_video_title = (*it)->get_title();
break;
}
}
@@ -1237,7 +1261,7 @@ namespace QuickMedia {
video_player = std::make_unique<VideoPlayer>(current_plugin->use_tor, use_system_mpv_config, video_event_callback, on_window_create);
content_url = selected_item->url;
- content_title = selected_item->title;
+ content_title = selected_item->get_title();
load_video_error_check();
}
}
@@ -1342,7 +1366,7 @@ namespace QuickMedia {
void Program::select_episode(BodyItem *item, bool start_from_beginning) {
images_url = item->url;
- chapter_title = item->title;
+ chapter_title = item->get_title();
image_index = 0;
switch(image_view_mode) {
case ImageViewMode::SINGLE:
@@ -1406,10 +1430,10 @@ namespace QuickMedia {
else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::T && sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) {
BodyItem *selected_item = body->get_selected();
if(selected_item) {
- if(track_media(TrackMediaType::HTML, content_title, selected_item->title, content_url) == 0) {
- show_notification("Media tracker", "You are now tracking \"" + content_title + "\" after \"" + selected_item->title + "\"", Urgency::LOW);
+ if(track_media(TrackMediaType::HTML, content_title, selected_item->get_title(), content_url) == 0) {
+ show_notification("Media tracker", "You are now tracking \"" + content_title + "\" after \"" + selected_item->get_title() + "\"", Urgency::LOW);
} else {
- show_notification("Media tracker", "Failed to track media \"" + content_title + "\", chapter: \"" + selected_item->title + "\"", Urgency::CRITICAL);
+ show_notification("Media tracker", "Failed to track media \"" + content_title + "\", chapter: \"" + selected_item->get_title() + "\"", Urgency::CRITICAL);
}
}
}
@@ -1829,7 +1853,7 @@ namespace QuickMedia {
if(!selected_item)
return false;
- content_episode = selected_item->title;
+ content_episode = selected_item->get_title();
content_url = selected_item->url;
current_page = Page::CONTENT_DETAILS;
body->clear_items();
@@ -1931,7 +1955,7 @@ namespace QuickMedia {
if(!selected_item)
return false;
- content_episode = selected_item->title;
+ content_episode = selected_item->get_title();
content_url = selected_item->url;
current_page = Page::IMAGE_BOARD_THREAD;
body->clear_items();
diff --git a/src/plugins/Mangadex.cpp b/src/plugins/Mangadex.cpp
index 853f501..7688355 100644
--- a/src/plugins/Mangadex.cpp
+++ b/src/plugins/Mangadex.cpp
@@ -213,6 +213,19 @@ namespace QuickMedia {
}
}, &body_item_image_context);
+ body_item_image_context.index = 0;
+ result = quickmedia_html_find_nodes_xpath(&html_search, "//div[class='pl-1']",
+ [](QuickMediaHtmlNode *node, void *userdata) {
+ auto *item_data = (BodyItemImageContext*)userdata;
+ const char *text = quickmedia_html_node_get_text(node);
+ if(text && item_data->index < item_data->body_items->size()) {
+ std::string desc = strip(text);
+ std::replace_if(desc.begin(), desc.end(), [](int c) { return c == '\n'; }, ' ');
+ (*item_data->body_items)[item_data->index]->set_description(std::move(desc));
+ item_data->index++;
+ }
+ }, &body_item_image_context);
+
cleanup:
quickmedia_html_search_deinit(&html_search);
return result == 0 ? SuggestionResult::OK : SuggestionResult::ERR;
diff --git a/src/plugins/Mangatown.cpp b/src/plugins/Mangatown.cpp
index 8dcf8d3..cb3217b 100644
--- a/src/plugins/Mangatown.cpp
+++ b/src/plugins/Mangatown.cpp
@@ -33,7 +33,7 @@ namespace QuickMedia {
int chapter_num = result_items.size();
for(auto &body_item : result_items) {
- body_item->title = "Ch. " + std::to_string(chapter_num);
+ body_item->set_title("Ch. " + std::to_string(chapter_num));
chapter_num--;
}
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index 46128ec..3b4a752 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -62,11 +62,8 @@ namespace QuickMedia {
auto body_item = std::make_unique<BodyItem>(title);
/* TODO: Make date a different color */
- /* TODO: Do not append to title, messes up history.. */
- /*if(date) {
- body_item->title += '\n';
- body_item->title += date;
- }*/
+ if(date)
+ body_item->set_description(date);
body_item->url = "https://www.youtube.com/watch?v=" + video_id_str;
body_item->thumbnail_url = std::move(thumbnail_url);
added_videos.insert(video_id_str);
@@ -447,6 +444,14 @@ namespace QuickMedia {
std::string thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg";
+ const char *date = nullptr;
+ const Json::Value &published_time_text_json = compact_video_renderer_json["publishedTimeText"];
+ if(published_time_text_json.isObject()) {
+ const Json::Value &text_json = published_time_text_json["simpleText"];
+ if(text_json.isString())
+ date = text_json.asCString();
+ }
+
const char *title = nullptr;
const Json::Value &title_json = compact_video_renderer_json["title"];
if(title_json.isObject()) {
@@ -460,6 +465,9 @@ namespace QuickMedia {
return nullptr;
auto body_item = std::make_unique<BodyItem>(title);
+ /* TODO: Make date a different color */
+ if(date)
+ body_item->set_description(date);
body_item->url = "https://www.youtube.com/watch?v=" + video_id_str;
body_item->thumbnail_url = std::move(thumbnail_url);
added_videos.insert(video_id_str);