From 11f644afe434ce6b6d570c9da2a95590321871b3 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 31 Oct 2020 07:12:16 +0100 Subject: Make matrix posting, deleting, replying, edit async and video check if streamable async --- src/QuickMedia.cpp | 279 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 171 insertions(+), 108 deletions(-) (limited to 'src/QuickMedia.cpp') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 3b99d55..e16f8a4 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1086,7 +1086,6 @@ namespace QuickMedia { redraw = true; } else { // TODO: Show the exact cause of error (get error message from curl). - // TODO: Make asynchronous show_notification("QuickMedia", std::string("Submit failed for page ") + tabs[selected_tab].page->get_title(), Urgency::CRITICAL); } }; @@ -1543,12 +1542,65 @@ namespace QuickMedia { static bool video_url_is_non_streamable_mp4(const char *url) { std::string result; const char *args[] = { "curl", "-sLf", "-r", "0-40", "-H", useragent_str, "--", url, nullptr }; - exec_program(args, accumulate_string_limit_head, &result); + exec_program(args, accumulate_string_limit_head, &result, 40); return (result.size() >= 40) && (memcmp(&result[4], "ftypisom", 8) == 0 || memcmp(&result[4], "ftypmp42", 8) == 0 || memcmp(&result[4], "ftymp42", 7) == 0 || memcmp(&result[4], "fty3gp5", 7) == 0) && (memcmp(&result[36], "moov", 4) != 0); } + TaskResult Program::run_task_with_loading_screen(std::function callback) { + std::promise result_promise; + std::future future = result_promise.get_future(); + std::thread task_thread([](std::promise &&promise, std::function callback) { + promise.set_value(callback()); + }, std::move(result_promise), std::move(callback)); + + sf::Sprite load_sprite(loading_icon); + sf::Vector2u loading_icon_size = loading_icon.getSize(); + load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); + + window_size.x = window.getSize().x; + window_size.y = window.getSize().y; + sf::Clock timer; + sf::Event event; + while(window.isOpen()) { + while(window.pollEvent(event)) { + if(event.type == sf::Event::Closed) { + program_kill_in_thread(task_thread.get_id()); + task_thread.join(); + current_page = PageType::EXIT; + window.close(); + return TaskResult::CANCEL; + } else if(event.type == sf::Event::Resized) { + window_size.x = event.size.width; + window_size.y = event.size.height; + sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); + window.setView(sf::View(visible_area)); + } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + program_kill_in_thread(task_thread.get_id()); + task_thread.join(); + return TaskResult::CANCEL; + } + } + + if(is_future_ready(future)) { + bool future_result = future.get(); + task_thread.join(); + if(!future_result) + return TaskResult::FALSE; + break; + } + + window.clear(back_color); + load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); + load_sprite.setRotation(timer.getElapsedTime().asSeconds() * 400.0); + window.draw(load_sprite); + window.display(); + } + + return TaskResult::TRUE; + } + #define CLEANMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask|Mod4Mask|Mod5Mask)) void Program::video_content_page(Page *page, std::string video_url, std::string video_title, bool download_if_streaming_fails) { @@ -1563,15 +1615,8 @@ namespace QuickMedia { load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); bool video_url_is_local = false; - if(download_if_streaming_fails && video_url_is_non_streamable_mp4(video_url.c_str())) { - fprintf(stderr, "%s is detected to be a non-streamable mp4 file, downloading it before playing it...\n", video_url.c_str()); + if(download_if_streaming_fails) { Path video_cache_dir = get_cache_dir().join("video"); - if(create_directory_recursive(video_cache_dir) != 0) { - show_notification("QuickMedia", "Failed to create video cache directory", Urgency::CRITICAL); - current_page = previous_page; - return; - } - Path video_path = video_cache_dir; SHA256 sha256; sha256.add(video_url.data(), video_url.size()); @@ -1581,55 +1626,39 @@ namespace QuickMedia { video_url = std::move(video_path.data); video_url_is_local = true; } else { - std::promise download_result_promise; - std::future download_future = download_result_promise.get_future(); - std::thread download_thread([this, &video_path, video_url](std::promise &&promise) { - promise.set_value(download_to_file(video_url, video_path.data, {}, use_tor, true)); - }, std::move(download_result_promise)); - - window_size.x = window.getSize().x; - window_size.y = window.getSize().y; - sf::Clock timer; - sf::Event event; - while(window.isOpen()) { - while(window.pollEvent(event)) { - if(event.type == sf::Event::Closed) { - program_kill_in_thread(download_thread.get_id()); - download_thread.join(); - current_page = previous_page; - window.close(); - return; - } else if(event.type == sf::Event::Resized) { - window_size.x = event.size.width; - window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { - program_kill_in_thread(download_thread.get_id()); - download_thread.join(); - current_page = previous_page; - return; - } + TaskResult video_is_not_streamble_result = run_task_with_loading_screen([video_url]() { + return video_url_is_non_streamable_mp4(video_url.c_str()); + }); + if(video_is_not_streamble_result == TaskResult::TRUE) { + fprintf(stderr, "%s is detected to be a non-streamable mp4 file, downloading it before playing it...\n", video_url.c_str()); + if(create_directory_recursive(video_cache_dir) != 0) { + show_notification("QuickMedia", "Failed to create video cache directory", Urgency::CRITICAL); + current_page = previous_page; + return; } - if(is_future_ready(download_future)) { - DownloadResult download_result = download_future.get(); - download_thread.join(); - if(download_result != DownloadResult::OK) { + TaskResult download_file_result = run_task_with_loading_screen([this, &video_path, video_url]() { + return download_to_file(video_url, video_path.data, {}, use_tor, true) == DownloadResult::OK; + }); + switch(download_file_result) { + case TaskResult::TRUE: { + video_url = std::move(video_path.data); + video_url_is_local = true; + break; + } + case TaskResult::FALSE: { show_notification("QuickMedia", "Failed to download " + video_url, Urgency::CRITICAL); current_page = previous_page; return; } - video_url = std::move(video_path.data); - video_url_is_local = true; - break; + case TaskResult::CANCEL: { + current_page = previous_page; + return; + } } - - window.clear(back_color); - load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); - load_sprite.setRotation(timer.getElapsedTime().asSeconds() * 400.0); - window.draw(load_sprite); - window.display(); + } else if(video_is_not_streamble_result == TaskResult::CANCEL) { + current_page = previous_page; + return; } } } @@ -1778,6 +1807,8 @@ namespace QuickMedia { } }; + bool fullscreen = false; + while (current_page == PageType::VIDEO_CONTENT && window.isOpen()) { while (window.pollEvent(event)) { base_event_handler(event, previous_page, related_media_body.get(), nullptr, true, false); @@ -1816,6 +1847,7 @@ namespace QuickMedia { related_media_body->clear_cache(); } else if(event.key.code == sf::Keyboard::F && event.key.control) { window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE); + fullscreen = !fullscreen; } else if(event.key.code == sf::Keyboard::Enter) { BodyItem *selected_item = related_media_body->get_selected(); if(!selected_item) @@ -1844,6 +1876,7 @@ namespace QuickMedia { current_page = previous_page; } else if(pressed_keysym == XK_f && pressing_ctrl) { window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE); + fullscreen = !fullscreen; } else if(pressed_keysym == XK_r && pressing_ctrl && strcmp(plugin_name, "4chan") != 0) { if(!related_media_window) { related_media_window_size.x = window_size.x * RELATED_MEDIA_WINDOW_WIDTH; @@ -1935,7 +1968,8 @@ namespace QuickMedia { } window.setMouseCursorVisible(true); - window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET); + if(fullscreen) + window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET); auto window_size_u = window.getSize(); window_size.x = window_size_u.x; @@ -3244,10 +3278,11 @@ namespace QuickMedia { tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(all_messages, matrix->get_me(current_room).get())); modify_related_messages_in_current_room(all_messages); tabs[MESSAGES_TAB_INDEX].body->select_last_item(); + all_messages.clear(); std::vector pinned_events; matrix->get_all_pinned_events(current_room, pinned_events); - process_pinned_events(pinned_events); + process_pinned_events(std::move(pinned_events)); tabs[PINNED_TAB_INDEX].body->select_last_item(); room_name_text.setString(static_cast(current_room->userdata)->get_title()); @@ -3260,7 +3295,17 @@ namespace QuickMedia { chat_input.draw_background = false; chat_input.set_editable(false); - chat_input.on_submit_callback = [this, &tabs, &chat_input, &selected_tab, ¤t_room, &new_page, &chat_state, ¤tly_operating_on_item](std::string text) mutable { + MessageQueue> post_task_queue; + std::thread post_thread([&post_task_queue]() { + while(true) { + std::optional> post_task_opt = post_task_queue.pop_wait(); + if(!post_task_opt) + break; + post_task_opt.value()(); + } + }); + + chat_input.on_submit_callback = [this, &chat_input, &selected_tab, ¤t_room, &new_page, &chat_state, ¤tly_operating_on_item, &post_task_queue](std::string text) mutable { if(!current_room) return false; @@ -3290,41 +3335,33 @@ namespace QuickMedia { } if(chat_state == ChatState::TYPING_MESSAGE) { - // TODO: Make asynchronous - if(matrix->post_message(current_room, text, std::nullopt, std::nullopt, msgtype) == PluginResult::OK) { - chat_input.set_editable(false); - chat_state = ChatState::NAVIGATING; - if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible()) - tabs[MESSAGES_TAB_INDEX].body->select_last_item(); - return true; - } else { - show_notification("QuickMedia", "Failed to post matrix message", Urgency::CRITICAL); - return false; - } + post_task_queue.push([this, ¤t_room, text, msgtype]() { + if(matrix->post_message(current_room, text, std::nullopt, std::nullopt, msgtype) != PluginResult::OK) + show_notification("QuickMedia", "Failed to post matrix message", Urgency::CRITICAL); + }); + chat_input.set_editable(false); + chat_state = ChatState::NAVIGATING; + return true; } else if(chat_state == ChatState::REPLYING) { - // TODO: Make asynchronous - if(matrix->post_reply(current_room, text, currently_operating_on_item->userdata) == PluginResult::OK) { - chat_input.set_editable(false); - chat_state = ChatState::NAVIGATING; - currently_operating_on_item = nullptr; - if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible()) - tabs[MESSAGES_TAB_INDEX].body->select_last_item(); - return true; - } else { - show_notification("QuickMedia", "Failed to post matrix reply", Urgency::CRITICAL); - return false; - } + void *related_to_message = currently_operating_on_item->userdata; + post_task_queue.push([this, ¤t_room, text, related_to_message]() { + if(matrix->post_reply(current_room, text, related_to_message) != PluginResult::OK) + show_notification("QuickMedia", "Failed to post matrix reply", Urgency::CRITICAL); + }); + chat_input.set_editable(false); + chat_state = ChatState::NAVIGATING; + currently_operating_on_item = nullptr; + return true; } else if(chat_state == ChatState::EDITING) { - // TODO: Make asynchronous - if(matrix->post_edit(current_room, text, currently_operating_on_item->userdata) == PluginResult::OK) { - chat_input.set_editable(false); - chat_state = ChatState::NAVIGATING; - currently_operating_on_item = nullptr; - return true; - } else { - show_notification("QuickMedia", "Failed to post matrix edit", Urgency::CRITICAL); - return false; - } + void *related_to_message = currently_operating_on_item->userdata; + post_task_queue.push([this, ¤t_room, text, related_to_message]() { + if(matrix->post_edit(current_room, text, related_to_message) != PluginResult::OK) + show_notification("QuickMedia", "Failed to post matrix edit", Urgency::CRITICAL); + }); + chat_input.set_editable(false); + chat_state = ChatState::NAVIGATING; + currently_operating_on_item = nullptr; + return true; } } return false; @@ -3464,7 +3501,7 @@ namespace QuickMedia { watched_videos.clear(); current_page = PageType::VIDEO_CONTENT; // TODO: Add title - video_content_page(video_page.get(), url, "No title", true); + video_content_page(video_page.get(), url, "No title", false); redraw = true; } else { const char *launch_program = "xdg-open"; @@ -3526,7 +3563,7 @@ namespace QuickMedia { bool prev_no_video = no_video; no_video = is_audio; // TODO: Add title - video_content_page(video_page.get(), *selected_url, "No title", true); + video_content_page(video_page.get(), *selected_url, "No title", message_type == MessageType::VIDEO); no_video = prev_no_video; redraw = true; return true; @@ -3662,12 +3699,20 @@ namespace QuickMedia { if(event.key.control && event.key.code == sf::Keyboard::V) { frame_skip_text_entry = true; - // TODO: Make asynchronous. // TODO: Upload multiple files. - std::string err_msg; - if(matrix->post_file(current_room, sf::Clipboard::getString(), err_msg) != PluginResult::OK) { - std::string desc = "Failed to upload media to room, error: " + err_msg; - show_notification("QuickMedia", desc.c_str(), Urgency::CRITICAL); + std::string selected_file = sf::Clipboard::getString(); + TaskResult post_file_result = run_task_with_loading_screen([this, ¤t_room, selected_file]() { + std::string err_msg; + if(matrix->post_file(current_room, selected_file, err_msg) == PluginResult::OK) { + return true; + } else { + show_notification("QuickMedia", "Failed to upload media to room, error: " + err_msg, Urgency::CRITICAL); + return false; + } + }); + if(post_file_result == TaskResult::TRUE) { + if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible()) + tabs[MESSAGES_TAB_INDEX].body->select_last_item(); } } @@ -3713,12 +3758,14 @@ namespace QuickMedia { frame_skip_text_entry = true; BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { - // TODO: Make asynchronous - std::string err_msg; - if(matrix->delete_message(current_room, selected->userdata, err_msg) != PluginResult::OK) { - // TODO: Show inline notification - show_notification("QuickMedia", "Failed to delete message, reason: " + err_msg, Urgency::CRITICAL); - } + void *selected_message = selected->userdata; + post_task_queue.push([this, ¤t_room, selected_message]() { + std::string err_msg; + if(matrix->delete_message(current_room, selected_message, err_msg) != PluginResult::OK) { + // TODO: Show inline notification + show_notification("QuickMedia", "Failed to delete message, reason: " + err_msg, Urgency::CRITICAL); + } + }); } else { // TODO: Show inline notification show_notification("QuickMedia", "No message selected for deletion"); @@ -3796,6 +3843,10 @@ namespace QuickMedia { new_page = PageType::CHAT; if(current_room) { + for(ChatTab &tab : tabs) { + tab.body->clear_cache(); + } + auto file_manager_page = std::make_unique(this); file_manager_page->set_current_directory(get_home_dir().data); auto file_manager_body = create_body(); @@ -3809,15 +3860,19 @@ namespace QuickMedia { if(selected_files.empty()) { fprintf(stderr, "No files selected!\n"); } else { - // TODO: Make asynchronous. // TODO: Upload multiple files. - std::string err_msg; - if(matrix->post_file(current_room, selected_files[0], err_msg) == PluginResult::OK) { + TaskResult post_file_result = run_task_with_loading_screen([this, ¤t_room]() { + std::string err_msg; + if(matrix->post_file(current_room, selected_files[0], err_msg) == PluginResult::OK) { + return true; + } else { + show_notification("QuickMedia", "Failed to upload media to room, error: " + err_msg, Urgency::CRITICAL); + return false; + } + }); + if(post_file_result == TaskResult::TRUE) { if(tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible()) tabs[MESSAGES_TAB_INDEX].body->select_last_item(); - } else { - std::string desc = "Failed to upload media to room, error: " + err_msg; - show_notification("QuickMedia", desc.c_str(), Urgency::CRITICAL); } } redraw = true; @@ -3835,10 +3890,15 @@ namespace QuickMedia { typing_state_queue.close(); if(typing_state_thread.joinable()) typing_state_thread.join(); + post_task_queue.close(); + if(post_thread.joinable()) + post_thread.join(); new_page = PageType::CHAT; matrix->stop_sync(); matrix->logout(); - tabs[MESSAGES_TAB_INDEX].body->clear_cache(); + for(ChatTab &tab : tabs) { + tab.body->clear_cache(); + } // TODO: Instead of doing this, exit this current function and navigate to chat login page instead. // This doesn't currently work because at the end of this function there are futures that need to wait // and one of them is /sync, which has a timeout of 30 seconds. That timeout has to be killed somehow. @@ -4142,6 +4202,9 @@ namespace QuickMedia { typing_state_queue.close(); if(typing_state_thread.joinable()) typing_state_thread.join(); + post_task_queue.close(); + if(post_thread.joinable()) + post_thread.join(); for(auto &body_item : tabs[PINNED_TAB_INDEX].body->items) { delete (PinnedEventData*)body_item->userdata; -- cgit v1.2.3