diff options
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r-- | src/QuickMedia.cpp | 170 |
1 files changed, 131 insertions, 39 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 648f231..d702fc9 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -339,7 +339,7 @@ namespace QuickMedia { } static void usage() { - fprintf(stderr, "usage: quickmedia [plugin|url] [--no-video] [--upscale-images] [--upscale-images-always] [--dir <directory>] [--instance <instance>] [-e <window>] [--video-max-height <height>]\n"); + fprintf(stderr, "usage: quickmedia [plugin|url] [--no-video] [--upscale-images] [--upscale-images-always] [--dir <directory>] [--instance <instance>] [-e <window>] [--video-max-height <height>] [--theme <theme>]\n"); fprintf(stderr, "OPTIONS:\n"); fprintf(stderr, " plugin|url The plugin to use. Should be either launcher, 4chan, manga, manganelo, manganelos, mangatown, mangakatana, mangadex, onimanga, local-manga, local-anime, youtube, peertube, lbry, soundcloud, nyaa.si, matrix, saucenao, hotexamples, anilist, dramacool, file-manager, stdin, pornhub, spankbang, xvideos or xhamster. This can also be a youtube url, youtube channel url or a 4chan thread url\n"); fprintf(stderr, " --no-video Only play audio when playing a video. Disabled by default\n"); @@ -349,6 +349,7 @@ namespace QuickMedia { fprintf(stderr, " --instance <instance> The instance to use for peertube\n"); fprintf(stderr, " -e <window-id> Embed QuickMedia into another window\n"); fprintf(stderr, " --video-max-height <height> Media plugins will try to select a video source that is this size or smaller\n"); + fprintf(stderr, " --theme The theme to use. If this is specified then it overrides the theme selected in the QuickMedia config file. Optional.\n"); fprintf(stderr, "EXAMPLES:\n"); fprintf(stderr, " quickmedia\n"); fprintf(stderr, " quickmedia --upscale-images-always manganelo\n"); @@ -409,6 +410,7 @@ namespace QuickMedia { std::string program_path = Path(argv[0]).parent().data; std::string instance; std::string download_filename; + std::string theme; bool no_dialog = false; for(int i = 1; i < argc; ++i) { @@ -523,6 +525,15 @@ namespace QuickMedia { } } else if(strcmp(argv[i], "--no-dialog") == 0) { no_dialog = true; + } else if(strcmp(argv[i], "--theme") == 0) { + if(i < argc - 1) { + theme = argv[i + 1]; + ++i; + } else { + fprintf(stderr, "Missing theme after --theme argument\n"); + usage(); + return -1; + } } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { usage(); return 0; @@ -602,7 +613,7 @@ namespace QuickMedia { }; no_video = force_no_video; - init(parent_window, program_path, no_dialog); + init(parent_window, program_path, no_dialog, theme); if(strcmp(plugin_name, "download") == 0) { if(!url) { @@ -687,7 +698,7 @@ namespace QuickMedia { return focused_monitor_center; } - void Program::init(mgl::WindowHandle parent_window, std::string &program_path, bool no_dialog) { + void Program::init(mgl::WindowHandle parent_window, std::string &program_path, bool no_dialog, const std::string &theme) { disp = XOpenDisplay(NULL); if (!disp) { show_notification("QuickMedia", "Failed to open display to X11 server", Urgency::CRITICAL); @@ -699,6 +710,9 @@ namespace QuickMedia { // Initialize config and theme early to prevent possible race condition on initialize get_config(); + if(!theme.empty()) + get_config().theme = theme; + get_theme(); mgl::vec2i monitor_size; @@ -953,7 +967,7 @@ namespace QuickMedia { string_split(urls_str, ',', [&arrays](const char *str, size_t size) { std::string url(str, size); url = strip(url); - if(!url.empty() && (arrays.empty() || arrays.back() != url)) + if(url.find(".com") != std::string::npos && (arrays.empty() || arrays.back() != url)) arrays.push_back(std::move(url)); return true; }); @@ -1095,6 +1109,8 @@ namespace QuickMedia { if(!youtube_dl_name) return false; + const int video_max_height = video_get_max_height(); + std::string ytdl_format; if(no_video) ytdl_format = "(bestaudio/best)"; @@ -1790,7 +1806,7 @@ namespace QuickMedia { // 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, [&](const Path &filepath, FileType) { + for_files_in_dir_sort_last_modified(content_storage_dir, [&](const Path &filepath, FileType, time_t last_modified_seconds) { // 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 @@ -1812,12 +1828,12 @@ namespace QuickMedia { if(!manga_name.isString()) return true; - time_t last_modified_time = 0; - file_get_last_modified_time_seconds(filepath.data.c_str(), &last_modified_time); + if(last_modified_seconds == 0) + file_get_last_modified_time_seconds(filepath.data.c_str(), &last_modified_seconds); // TODO: Add thumbnail auto body_item = BodyItem::create(manga_name.asString()); - body_item->set_description("Last read " + seconds_to_relative_time_str(now - last_modified_time)); + body_item->set_description("Last read " + seconds_to_relative_time_str(now - last_modified_seconds)); body_item->set_description_color(get_theme().faded_text_color); auto thumbnail_it = manga_id_to_thumbnail_url_map.find(manga_id); @@ -3086,7 +3102,10 @@ namespace QuickMedia { } static bool url_should_download_with_youtube_dl(const std::string &url) { - return url.find("pornhub.com") != std::string::npos + return url.find("youtube.com") != std::string::npos + || url.find("youtu.be") != std::string::npos + || url.find("googlevideo.com") != std::string::npos + || url.find("pornhub.com") != std::string::npos || url.find("xhamster.com") != std::string::npos || url.find("spankbang.com") != std::string::npos // TODO: Remove when youtube-dl is no longer required to download soundcloud music @@ -3330,18 +3349,21 @@ namespace QuickMedia { std::string audio_url; bool has_embedded_audio = true; + bool update_duration_retry = false; auto update_video_duration_handler = [&]() { if(!video_player) return; - if(update_duration) { + if(update_duration || update_duration_retry) { update_duration = false; successfully_fetched_video_duration = false; double file_duration = 0.0; video_player->get_duration(&file_duration); video_info.duration = std::max(video_info.duration, file_duration); - if(video_info.duration > 0.001) + if(video_info.duration > 0.001) { + update_duration_retry = false; successfully_fetched_video_duration = true; + } } }; @@ -3361,6 +3383,9 @@ namespace QuickMedia { update_time_pos = true; } + if(update_duration || (update_duration_retry && update_time_pos)) + update_video_duration_handler(); + if(update_time_pos) { update_time_pos = false; const double prev_video_time_pos = video_time_pos; @@ -3382,8 +3407,6 @@ namespace QuickMedia { } } } - - update_video_duration_handler(); }; auto load_video_error_check = [&](std::string start_time = "", bool reuse_media_source = false) mutable { @@ -3510,6 +3533,7 @@ namespace QuickMedia { startup_args.parent_window = window.get_system_handle(); startup_args.no_video = is_audio_only; startup_args.use_system_mpv_config = get_config().use_system_mpv_config || video_page->is_local(); + startup_args.system_mpv_profile = get_config().system_mpv_profile; startup_args.use_system_input_config = video_page->is_local(); startup_args.keep_open = is_matrix && !is_youtube; startup_args.resume = false; @@ -3589,6 +3613,7 @@ namespace QuickMedia { //video_player->set_paused(false); } else if(strcmp(event_name, "start-file") == 0) { update_duration = true; + update_duration_retry = true; added_recommendations = false; time_watched_timer.restart(); video_loaded = true; @@ -3621,7 +3646,7 @@ namespace QuickMedia { mgl::Clock cursor_hide_timer; auto save_video_url_to_clipboard = [this, video_page]() { - std::string url = video_page->get_download_url(video_get_max_height()); + std::string url = video_page->get_download_url_for_clipboard(video_get_max_height()); if(video_url_supports_timestamp(url)) { double time_in_file = 0.0; if(video_player && (video_player->get_time_in_file(&time_in_file) != VideoPlayer::Error::OK)) @@ -4230,6 +4255,15 @@ namespace QuickMedia { } } + { + FileAnalyzer file_analyzer; + if(file_analyzer.load_file(image_filepath_tmp.data.c_str(), false) && file_analyzer.get_content_type() == ContentType::IMAGE_WEBP) { + Path new_filepath = image_filepath_tmp.data + ".png"; + if(ffmpeg_convert_image_format(image_filepath_tmp, new_filepath)) + image_filepath_tmp = std::move(new_filepath); + } + } + bool rename_immediately = true; if(upscale_image_action == UpscaleImageAction::LOW_RESOLUTION) { int screen_width, screen_height; @@ -4476,6 +4510,9 @@ namespace QuickMedia { } else if(event.key.code == mgl::Keyboard::F) { fit_image_to_window = !fit_image_to_window; redraw = true; + } else if(event.key.code == mgl::Keyboard::B) { + show_manga_bottom_bar = !show_manga_bottom_bar; + redraw = true; } } } @@ -4500,7 +4537,7 @@ namespace QuickMedia { } const float font_height = chapter_text_character_size + 8.0f; - const float bottom_panel_height = font_height + 6.0f; + const float bottom_panel_height = show_manga_bottom_bar ? font_height + 6.0f : 0.0f; mgl::vec2f content_size; content_size.x = window_size.x; @@ -4539,13 +4576,15 @@ namespace QuickMedia { window.draw(image); } - chapter_text_background.set_size(mgl::vec2f(window_size.x, bottom_panel_height)); - chapter_text_background.set_position(mgl::vec2f(0.0f, std::floor(window_size.y - bottom_panel_height))); - window.draw(chapter_text_background); + if(show_manga_bottom_bar) { + chapter_text_background.set_size(mgl::vec2f(window_size.x, bottom_panel_height)); + chapter_text_background.set_position(mgl::vec2f(0.0f, std::floor(window_size.y - bottom_panel_height))); + window.draw(chapter_text_background); - auto text_bounds = chapter_text.get_bounds(); - chapter_text.set_position(vec2f_floor(window_size.x * 0.5f - text_bounds.size.x * 0.5f, window_size.y - bottom_panel_height * 0.5f - font_height * 0.55f)); - window.draw(chapter_text); + auto text_bounds = chapter_text.get_bounds(); + chapter_text.set_position(vec2f_floor(window_size.x * 0.5f - text_bounds.size.x * 0.5f, window_size.y - bottom_panel_height * 0.5f - font_height * 0.55f)); + window.draw(chapter_text); + } window.display(); } else { @@ -6280,7 +6319,9 @@ namespace QuickMedia { "/me [text]: Send a message of type \"m.emote\".\n" "/react [text]: React to the selected message (also works if you are replying to a message).\n" "/id: Show the room id.\n" - "/encrypt [text]: Send a message encrypted with gpg. gpg needs to be installed to do this. Uses the gpg key specified by the user id in your config variable \"matrix.gpg_user_id\"."; + "/whoami: Show your user id.\n" + "/encrypt [text]: Send a message encrypted with gpg. gpg needs to be installed to do this. Uses the gpg key specified by the user id in your config variable \"matrix.gpg_user_id\".\n" + "/bot [text]: Send a message as a bot would."; message->timestamp = time(nullptr) * 1000; // TODO: What if the user has broken local time? matrix->append_system_message(current_room, std::move(message)); @@ -6300,9 +6341,24 @@ namespace QuickMedia { chat_state = ChatState::NAVIGATING; matrix->get_room_extra_data(current_room).editing_message_id.clear(); return true; + } else if(text == "/whoami") { + auto message = std::make_shared<Message>(); + message->type = MessageType::SYSTEM; + message->user = me; + message->body = me->user_id; + message->timestamp = time(nullptr) * 1000; // TODO: What if the user has broken local time? + matrix->append_system_message(current_room, std::move(message)); + + chat_input.set_editable(false); + chat_state = ChatState::NAVIGATING; + matrix->get_room_extra_data(current_room).editing_message_id.clear(); + return true; } else if(strncmp(text.c_str(), "/me ", 4) == 0) { msgtype = "m.emote"; text.erase(text.begin(), text.begin() + 4); + } else if(strncmp(text.c_str(), "/bot ", 5) == 0) { + msgtype = "m.notice"; + text.erase(text.begin(), text.begin() + 5); } else if(strncmp(text.c_str(), "/react ", 7) == 0) { msgtype = "m.reaction"; text.erase(text.begin(), text.begin() + 7); @@ -7112,6 +7168,30 @@ namespace QuickMedia { } }; + auto start_typing = [&] { + RoomExtraData &room_extra_data = matrix->get_room_extra_data(current_room); + frame_skip_text_entry = true; + chat_input.set_editable(true); + if(get_config().matrix.clear_message_on_escape || !room_extra_data.editing_message_id.empty()) { + chat_input.set_text(""); + room_extra_data.editing_message_id.clear(); + } + chat_state = ChatState::TYPING_MESSAGE; + }; + + auto start_replying = [&](std::shared_ptr<BodyItem> selected_body_item) { + RoomExtraData &room_extra_data = matrix->get_room_extra_data(current_room); + chat_state = ChatState::REPLYING; + currently_operating_on_item = selected_body_item; + chat_input.set_editable(true); + if(get_config().matrix.clear_message_on_escape || !room_extra_data.editing_message_id.empty()) { + chat_input.set_text(""); + room_extra_data.editing_message_id.clear(); + } + replying_to_text.set_string("Replying to:"); + frame_skip_text_entry = true; + }; + for(size_t i = 0; i < tabs.size(); ++i) { tabs[i].body->on_top_reached = on_top_reached; tabs[i].body->on_bottom_reached = on_bottom_reached; @@ -7258,14 +7338,16 @@ namespace QuickMedia { } if(event.key.code == mgl::Keyboard::I && !event.key.control) { - RoomExtraData &room_extra_data = matrix->get_room_extra_data(current_room); - frame_skip_text_entry = true; - chat_input.set_editable(true); - if(get_config().matrix.clear_message_on_escape || !room_extra_data.editing_message_id.empty()) { - chat_input.set_text(""); - room_extra_data.editing_message_id.clear(); + start_typing(); + } + + if(event.key.code == mgl::Keyboard::G && !event.key.control) { + start_typing(); + std::string chat_text = chat_input.get_text(); + if(!string_starts_with(chat_text, "/encrypt")) { + chat_input.set_text("/encrypt " + chat_text); + chat_input.set_caret_index(chat_input.get_caret_index() + 9); } - chat_state = ChatState::TYPING_MESSAGE; } if(event.key.control && event.key.code == mgl::Keyboard::V) { @@ -7352,21 +7434,27 @@ namespace QuickMedia { // TODO: Show inline notification show_notification("QuickMedia", "You can't reply to a message that hasn't been sent yet"); } else { - RoomExtraData &room_extra_data = matrix->get_room_extra_data(current_room); - chat_state = ChatState::REPLYING; - currently_operating_on_item = selected; - chat_input.set_editable(true); - if(get_config().matrix.clear_message_on_escape || !room_extra_data.editing_message_id.empty()) { - chat_input.set_text(""); - room_extra_data.editing_message_id.clear(); - } - replying_to_text.set_string("Replying to:"); - frame_skip_text_entry = true; + start_replying(std::move(selected)); } } } } + if(event.key.code == mgl::Keyboard::G && event.key.control) { + std::shared_ptr<BodyItem> selected = tabs[selected_tab].body->get_selected_shared(); + if(static_cast<Message*>(selected->userdata)->event_id.empty()) { + // TODO: Show inline notification + show_notification("QuickMedia", "You can't reply to a message that hasn't been sent yet"); + } else { + start_replying(std::move(selected)); + std::string chat_text = chat_input.get_text(); + if(!string_starts_with(chat_text, "/encrypt")) { + chat_input.set_text("/encrypt " + chat_text); + chat_input.set_caret_index(chat_input.get_caret_index() + 9); + } + } + } + if(event.key.code == mgl::Keyboard::B && event.key.control) { // Reload room, goes to latest message l0l move_room = true; @@ -7391,6 +7479,10 @@ namespace QuickMedia { } else { RoomExtraData &room_extra_data = matrix->get_room_extra_data(current_room); std::string body_text_unformatted = Text::to_printable_string(selected->get_description()); + MatrixChatBodyItemData *matrix_chat_body_item = static_cast<MatrixChatBodyItemData*>(selected->extra.get()); + const char *encrypt_prefix = "🔒 "; + if(string_starts_with(body_text_unformatted, encrypt_prefix) && matrix_chat_body_item) + body_text_unformatted.replace(0, strlen(encrypt_prefix), "/encrypt "); chat_state = ChatState::EDITING; currently_operating_on_item = selected; |