From 3364e8c6291907ea88e45314c5add9624b81c110 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 7 Dec 2019 18:10:12 +0100 Subject: Add support for viewing videos/gifs on image boards (4chan) --- src/Body.cpp | 2 +- src/QuickMedia.cpp | 83 ++++++++++++++++++++++++++++++++---------------- src/StringUtils.cpp | 6 ++++ src/plugins/Fourchan.cpp | 11 ++----- 4 files changed, 65 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/Body.cpp b/src/Body.cpp index be5267a..27e09f9 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -309,7 +309,7 @@ namespace QuickMedia { if(current_json.isNumeric() && total_json.isNumeric()) { progress_text.setString(std::string("Page: ") + std::to_string(current_json.asInt()) + "/" + std::to_string(total_json.asInt())); auto bounds = progress_text.getLocalBounds(); - progress_text.setPosition(std::floor(item_pos.x + size.x - bounds.width - padding_x), std::floor(item_pos.y)); + progress_text.setPosition(std::floor(item_pos.x + size.x - bounds.width - padding_x), std::floor(item_pos.y + padding_y)); window.draw(progress_text); } } diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index f3b4b7b..e2fc237 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -434,6 +434,9 @@ namespace QuickMedia { watched_videos.clear(); if(content_url.empty()) next_page = Page::SEARCH_RESULT; + else { + page_stack.push(Page::SEARCH_SUGGESTION); + } } else if(next_page == Page::CONTENT_LIST) { content_list_url = content_url; } else if(next_page == Page::IMAGE_BOARD_THREAD_LIST) { @@ -661,6 +664,8 @@ namespace QuickMedia { search_bar->onTextUpdateCallback = nullptr; search_bar->onTextSubmitCallback = nullptr; + Page previous_page = pop_page_stack(); + Display* disp = XOpenDisplay(NULL); if (!disp) throw std::runtime_error("Failed to open display to X11 server"); @@ -689,18 +694,18 @@ namespace QuickMedia { std::unique_ptr video_player; - auto load_video_error_check = [this, &video_player]() { + auto load_video_error_check = [this, &video_player, previous_page]() { watched_videos.insert(content_url); VideoPlayer::Error err = video_player->load_video(content_url.c_str(), window.getSystemHandle()); if(err != VideoPlayer::Error::OK) { std::string err_msg = "Failed to play url: "; err_msg += content_url; show_notification("Video player", err_msg.c_str(), Urgency::CRITICAL); - current_page = Page::SEARCH_SUGGESTION; + current_page = previous_page; } }; - video_player = std::make_unique(current_plugin->use_tor, [this, &video_player, &seekable, &load_video_error_check](const char *event_name) { + video_player = std::make_unique(current_plugin->use_tor, [this, &video_player, &seekable, &load_video_error_check, previous_page](const char *event_name) { bool end_of_file = false; if(strcmp(event_name, "pause") == 0) { double time_remaining = 0.0; @@ -725,7 +730,7 @@ namespace QuickMedia { // If there are no videos to play, then dont play any... if(new_video_url.empty()) { show_notification("Video player", "No more related videos to play"); - current_page = Page::SEARCH_SUGGESTION; + current_page = previous_page; return; } @@ -759,7 +764,7 @@ namespace QuickMedia { while (current_page == Page::VIDEO_CONTENT) { while (window.pollEvent(event)) { - base_event_handler(event, Page::SEARCH_SUGGESTION); + base_event_handler(event, previous_page); if(event.type == sf::Event::Resized) { if(video_player_ui_window) ui_resize = true; @@ -798,11 +803,11 @@ namespace QuickMedia { VideoPlayer::Error update_err = video_player->update(); if(update_err == VideoPlayer::Error::FAIL_TO_CONNECT_TIMEOUT) { show_notification("Video player", "Failed to connect to mpv ipc after 5 seconds", Urgency::CRITICAL); - current_page = Page::SEARCH_SUGGESTION; + current_page = previous_page; return; } else if(update_err != VideoPlayer::Error::OK) { show_notification("Video player", "Unexpected error while updating", Urgency::CRITICAL); - current_page = Page::SEARCH_SUGGESTION; + current_page = previous_page; return; } @@ -899,6 +904,15 @@ namespace QuickMedia { } } + Page Program::pop_page_stack() { + if(!page_stack.empty()) { + Page previous_page = page_stack.top(); + page_stack.pop(); + return previous_page; + } + return Page::EXIT; + } + void Program::episode_list_page() { search_bar->onTextUpdateCallback = [this](const std::string &text) { body->filter_search_fuzzy(text); @@ -1422,6 +1436,10 @@ namespace QuickMedia { } } + static bool is_url_video(const std::string &url) { + return string_ends_with(url, ".webm") || string_ends_with(url, ".mp4") || string_ends_with(url, ".gif"); + } + void Program::image_board_thread_page() { assert(current_plugin->is_image_board()); // TODO: Support image board other than 4chan. To make this work, the captcha code needs to be changed @@ -1608,28 +1626,37 @@ namespace QuickMedia { } else if(event.key.code == sf::Keyboard::P) { // TODO: Make this work when thumbnail is preview for a video/gif instead of a static image BodyItem *selected_item = body->get_selected(); - if(selected_item && !selected_item->fullsize_image_url.empty()) { - navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; - load_image_future = std::async(std::launch::async, [this, &image_board, &attached_image_texture, &attached_image_sprite, &attachment_load_mutex]() -> bool { - BodyItem *selected_item = body->get_selected(); - if(!selected_item || selected_item->fullsize_image_url.empty()) { - return false; - } - std::string image_data; - if(download_to_string(selected_item->fullsize_image_url, image_data, {}, image_board->use_tor) != DownloadResult::OK) { - show_notification(image_board->name, "Failed to download image: " + selected_item->fullsize_image_url, Urgency::CRITICAL); - return false; - } + if(selected_item && !selected_item->attached_content_url.empty()) { + if(is_url_video(selected_item->attached_content_url)) { + page_stack.push(Page::IMAGE_BOARD_THREAD); + current_page = Page::VIDEO_CONTENT; + std::string prev_content_url = content_url; + content_url = selected_item->attached_content_url; + video_content_page(); + content_url = std::move(prev_content_url); + } else { + navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; + load_image_future = std::async(std::launch::async, [this, &image_board, &attached_image_texture, &attached_image_sprite, &attachment_load_mutex]() -> bool { + BodyItem *selected_item = body->get_selected(); + if(!selected_item || selected_item->attached_content_url.empty()) { + return false; + } + std::string image_data; + if(download_to_string(selected_item->attached_content_url, image_data, {}, image_board->use_tor) != DownloadResult::OK) { + show_notification(image_board->name, "Failed to download image: " + selected_item->attached_content_url, Urgency::CRITICAL); + return false; + } - std::lock_guard lock(attachment_load_mutex); - if(!attached_image_texture->loadFromMemory(image_data.data(), image_data.size())) { - show_notification(image_board->name, "Failed to load image downloaded from url: " + selected_item->fullsize_image_url, Urgency::CRITICAL); - return false; - } - attached_image_texture->setSmooth(true); - attached_image_sprite.setTexture(*attached_image_texture, true); - return true; - }); + std::lock_guard lock(attachment_load_mutex); + if(!attached_image_texture->loadFromMemory(image_data.data(), image_data.size())) { + show_notification(image_board->name, "Failed to load image downloaded from url: " + selected_item->attached_content_url, Urgency::CRITICAL); + return false; + } + attached_image_texture->setSmooth(true); + attached_image_sprite.setTexture(*attached_image_texture, true); + return true; + }); + } } } diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 16d3b48..981b227 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -1,4 +1,5 @@ #include "../include/StringUtils.hpp" +#include namespace QuickMedia { void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func) { @@ -47,4 +48,9 @@ namespace QuickMedia { return str.substr(start, end - start + 1); } + + bool string_ends_with(const std::string &str, const std::string &ends_with_str) { + size_t ends_len = ends_with_str.size(); + return ends_len == 0 || (str.size() >= ends_len && memcmp(&str[str.size() - ends_len], ends_with_str.data(), ends_len) == 0); + } } \ No newline at end of file diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp index 17fefdc..f19d3aa 100644 --- a/src/plugins/Fourchan.cpp +++ b/src/plugins/Fourchan.cpp @@ -52,11 +52,6 @@ namespace QuickMedia { return SuggestionResult::OK; } - static bool string_ends_with(const std::string &str, const std::string &ends_with_str) { - size_t ends_len = ends_with_str.size(); - return ends_len == 0 || (str.size() >= ends_len && memcmp(&str[str.size() - ends_len], ends_with_str.data(), ends_len) == 0); - } - struct CommentPiece { enum class Type { TEXT, @@ -236,7 +231,7 @@ namespace QuickMedia { const Json::Value &tim = thread["tim"]; if(tim.isNumeric() && ext.isString()) { std::string ext_str = ext.asString(); - if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg") { + if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg" || ext_str == ".webm" || ext_str == ".mp4" || ext_str == ".gif") { } else { fprintf(stderr, "TODO: Support file extension: %s\n", ext_str.c_str()); } @@ -347,7 +342,7 @@ namespace QuickMedia { const Json::Value &tim = post["tim"]; if(tim.isNumeric() && ext.isString()) { std::string ext_str = ext.asString(); - if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg") { + if(ext_str == ".png" || ext_str == ".jpg" || ext_str == ".jpeg" || ext_str == ".webm" || ext_str == ".mp4" || ext_str == ".gif") { } else { fprintf(stderr, "TODO: Support file extension: %s\n", ext_str.c_str()); } @@ -355,7 +350,7 @@ namespace QuickMedia { // thumbnails always has .jpg extension even if they are gifs or webm. std::string tim_str = std::to_string(tim.asInt64()); body_item->thumbnail_url = fourchan_image_url + list_url + "/" + tim_str + "s.jpg"; - body_item->fullsize_image_url = fourchan_image_url + list_url + "/" + tim_str + ext_str; + body_item->attached_content_url = fourchan_image_url + list_url + "/" + tim_str + ext_str; } ++body_item_index; -- cgit v1.2.3