diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/AsyncImageLoader.cpp | 1 | ||||
-rw-r--r-- | src/Config.cpp | 3 | ||||
-rw-r--r-- | src/Entry.cpp | 4 | ||||
-rw-r--r-- | src/FileAnalyzer.cpp | 2 | ||||
-rw-r--r-- | src/ImageViewer.cpp | 25 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 140 | ||||
-rw-r--r-- | src/Text.cpp | 4 | ||||
-rw-r--r-- | src/Utils.cpp | 15 | ||||
-rw-r--r-- | src/VideoPlayer.cpp | 41 | ||||
-rw-r--r-- | src/main.cpp | 2 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 70 | ||||
-rw-r--r-- | src/plugins/Youtube.cpp | 97 |
12 files changed, 301 insertions, 103 deletions
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 14cfe6a..a6706f5 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -27,6 +27,7 @@ namespace QuickMedia { bool ffmpeg_convert_image_format(const Path &thumbnail_path, const Path &destination_path) { + // TODO: Dont output as jpg, ffmpeg jpg is very slow const char *args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", thumbnail_path.data.c_str(), "--", destination_path.data.c_str(), nullptr}; return exec_program(args, nullptr, nullptr) == 0; } diff --git a/src/Config.cpp b/src/Config.cpp index ecc1fce..64d5019 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -406,6 +406,7 @@ namespace QuickMedia { get_json_value(json_root, "use_system_fonts", config->use_system_fonts); get_json_value(json_root, "use_system_mpv_config", config->use_system_mpv_config); + get_json_value(json_root, "system_mpv_profile", config->system_mpv_profile); get_json_value(json_root, "enable_shaders", config->enable_shaders); get_json_value(json_root, "theme", config->theme); get_json_value(json_root, "scale", config->scale); @@ -414,7 +415,7 @@ namespace QuickMedia { get_json_value(json_root, "low_latency_mode", config->low_latency_mode); } - const Config& get_config() { + Config& get_config() { init_config(); return *config; } diff --git a/src/Entry.cpp b/src/Entry.cpp index 5416724..8aa2bba 100644 --- a/src/Entry.cpp +++ b/src/Entry.cpp @@ -107,6 +107,10 @@ namespace QuickMedia { return text.getCaretIndex(); } + void Entry::set_caret_index(int index) { + text.setCaretIndex(index); + } + void Entry::set_position(const mgl::vec2f &pos) { background.set_position(pos); text.set_position(pos + mgl::vec2f(background_margin_horizontal * padding_scale, background_margin_vertical * padding_scale - (float)text.get_character_size() * 0.3f).floor()); diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp index 4deb8c3..1d24c5f 100644 --- a/src/FileAnalyzer.cpp +++ b/src/FileAnalyzer.cpp @@ -157,6 +157,7 @@ namespace QuickMedia { char size_arg_str[512]; snprintf(size_arg_str, sizeof(size_arg_str), "scale=%d:%d:force_original_aspect_ratio=decrease", width, height); + // TODO: Dont output as jpg, ffmpeg jpg is very slow const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", seconds_str, "-i", file.get_filepath().c_str(), "-frames:v", "1", "-vf", size_arg_str, "--", destination_path_tmp.data.c_str(), nullptr }; if(exec_program(program_args, nullptr, nullptr, allowed_exit_status, 2) != 0) { fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n"); @@ -168,6 +169,7 @@ namespace QuickMedia { if(fallback_first_frame && get_file_type(destination_path_tmp) == FileType::FILE_NOT_FOUND) return video_get_frame(file, destination_path, width, height, 0, false); } else { + // TODO: Dont output as jpg, ffmpeg jpg is very slow const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", seconds_str, "-i", file.get_filepath().c_str(), "-frames:v", "1", "--", destination_path_tmp.data.c_str(), nullptr }; if(exec_program(program_args, nullptr, nullptr, allowed_exit_status, 2) != 0) { fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n"); diff --git a/src/ImageViewer.cpp b/src/ImageViewer.cpp index fb9912c..454f34b 100644 --- a/src/ImageViewer.cpp +++ b/src/ImageViewer.cpp @@ -296,6 +296,9 @@ namespace QuickMedia { if(event.key.code == mgl::Keyboard::F) *fit_image_to_window = !*fit_image_to_window; + + if(event.key.code == mgl::Keyboard::B) + show_progress_bar = !show_progress_bar; } else if(event.type == mgl::Event::KeyReleased) { if(is_key_scroll_up(event.key)) up_pressed = false; @@ -426,16 +429,18 @@ namespace QuickMedia { const float font_height = page_text_character_size + 8.0f; const float background_height = font_height + 6.0f; - mgl::Rectangle page_text_background(mgl::vec2f(window_size.x, background_height)); - mgl::Color text_background_color = get_theme().shade_color; - text_background_color.a = 225; - page_text_background.set_color(text_background_color); - page_text_background.set_position(mgl::vec2f(0.0f, window_size.y - background_height)); - window->draw(page_text_background); - - auto page_text_bounds = page_text.get_bounds(); - page_text.set_position(mgl::vec2f(floor(window_size.x * 0.5f - page_text_bounds.size.x * 0.5f), floor(window_size.y - background_height * 0.5f - font_height * 0.55f))); - window->draw(page_text); + if(show_progress_bar) { + mgl::Rectangle page_text_background(mgl::vec2f(window_size.x, background_height)); + mgl::Color text_background_color = get_theme().shade_color; + text_background_color.a = 225; + page_text_background.set_color(text_background_color); + page_text_background.set_position(mgl::vec2f(0.0f, window_size.y - background_height)); + window->draw(page_text_background); + + auto page_text_bounds = page_text.get_bounds(); + page_text.set_position(mgl::vec2f(floor(window_size.x * 0.5f - page_text_bounds.size.x * 0.5f), floor(window_size.y - background_height * 0.5f - font_height * 0.55f))); + window->draw(page_text); + } // Free pages that are not visible on the screen int i = 0; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index f3e21da..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)"; @@ -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 @@ -3514,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; @@ -3626,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)) @@ -4490,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; } } } @@ -4514,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; @@ -4553,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 { @@ -6294,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)); @@ -6314,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); @@ -7126,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; @@ -7272,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) { @@ -7366,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; @@ -7405,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; diff --git a/src/Text.cpp b/src/Text.cpp index 2b62798..6333e8e 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -285,6 +285,10 @@ namespace QuickMedia int Text::getCaretIndex() const { return caretIndex; } + + void Text::setCaretIndex(int index) { + caretIndex = index; + } void Text::set_color(mgl::Color color, bool force_color) { diff --git a/src/Utils.cpp b/src/Utils.cpp index ca153ab..e6628d1 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -2,13 +2,11 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <locale.h> +#include <X11/Xlib.h> namespace QuickMedia { static bool qm_enable_touch = false; static bool qm_enable_touch_set = false; - static bool wayland_display_set = false; - static const char *wayland_display = nullptr; void show_virtual_keyboard() { if(!is_touch_enabled()) @@ -38,14 +36,9 @@ namespace QuickMedia { return qm_enable_touch; } - // TODO: Find a better way to detect this. This will return true on ubuntu when running gnome in x11 mode - bool is_running_wayland() { - if(wayland_display_set) - return wayland_display; - - wayland_display = getenv("WAYLAND_DISPLAY"); - wayland_display_set = true; - return wayland_display; + bool is_running_wayland(void *dpy) { + int opcode, event, error; + return XQueryExtension((Display*)dpy, "XWAYLAND", &opcode, &event, &error); } time_t iso_utc_to_unix_time(const char *time_str) { diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp index 3aa4eca..ea18289 100644 --- a/src/VideoPlayer.cpp +++ b/src/VideoPlayer.cpp @@ -177,7 +177,6 @@ namespace QuickMedia { const std::string config_dir = "--config-dir=" + startup_args.resource_root + "mpv"; const std::string input_conf_file = "--input-conf=" + startup_args.resource_root + "mpv/input.conf"; - const std::string ytdl_hook_file = "--scripts=" + startup_args.resource_root + "mpv/scripts/ytdl_hook.lua"; std::vector<const char*> args; // TODO: Resume playback if the last video played matches the first video played next time QuickMedia is launched @@ -218,7 +217,7 @@ namespace QuickMedia { args.push_back("--resume-playback=no"); } - if(is_running_wayland()) { + if(is_running_wayland(display)) { args.push_back("--gpu-context=x11egl"); fprintf(stderr, "Wayland detected. Launching mpv in x11egl mode\n"); } @@ -251,27 +250,51 @@ namespace QuickMedia { if(!startup_args.referer.empty()) args.push_back(referer_arg.c_str()); - std::string mpris_arg; - Path mpris_path = get_config_dir_xdg().join("mpv").join("scripts").join("mpris.so"); - if(get_file_type(mpris_path) == FileType::REGULAR) - mpris_arg = "--scripts=" + mpris_path.data; + std::string scripts; + std::string scripts_arg; + const Path mpris_path = get_config_dir_xdg().join("mpv").join("scripts").join("mpris.so"); + if(get_file_type(mpris_path) == FileType::REGULAR) { + if(!scripts.empty()) + scripts += ":"; + scripts += mpris_path.data; + } + + const Path mpris_system_path = "/etc/mpv/scripts/mpris.so"; + if(get_file_type(mpris_system_path) == FileType::REGULAR) { + if(!scripts.empty()) + scripts += ":"; + scripts += mpris_system_path.data; + } + + std::string profile_arg; if(startup_args.use_system_mpv_config) { + if(!scripts.empty()) + scripts += ":"; + scripts += startup_args.resource_root + "mpv/scripts/ytdl_hook.lua"; + args.push_back("--config=yes"); args.push_back("--load-scripts=yes"); args.push_back("--osc=yes"); args.push_back(input_conf_file.c_str()); - args.push_back(ytdl_hook_file.c_str()); + + if(!startup_args.system_mpv_profile.empty()) + profile_arg = "--profile=" + startup_args.system_mpv_profile; } else { args.insert(args.end(), { config_dir.c_str(), "--config=yes" }); + } - if(!mpris_arg.empty()) - args.push_back(mpris_arg.c_str()); + if(!scripts.empty()) { + scripts_arg = "--scripts=" + scripts; + args.push_back(scripts_arg.c_str()); } + if(!profile_arg.empty()) + args.push_back(profile_arg.c_str()); + std::string force_media_title_arg; if(!startup_args.title.empty()) { force_media_title_arg = "--force-media-title=" + startup_args.title; diff --git a/src/main.cpp b/src/main.cpp index ef0568b..3d675bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,10 @@ #include "../include/QuickMedia.hpp" #include <locale.h> +#include <malloc.h> int main(int argc, char **argv) { setlocale(LC_ALL, "C"); // Sigh... stupid C + mallopt(M_MMAP_THRESHOLD, 65536); QuickMedia::Program program; return program.run(argc, argv); } diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 7080bf9..c810d88 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -1370,6 +1370,35 @@ namespace QuickMedia { } } + const char* MatrixVideoPage::get_title() const { + return ""; + } + + std::string MatrixVideoPage::get_filename() { + return filename; + } + + std::string MatrixVideoPage::get_download_url_for_clipboard(int max_height) { + std::string download_url = get_download_url(max_height); + size_t index = download_url.find("?access_token="); + if(index == std::string::npos) { + index = download_url.find("&access_token="); + if(index == std::string::npos) + return download_url; + } + + size_t end = download_url.find('&', index + 14); + if(end == std::string::npos) { + end = download_url.size(); + } else { + ++index; + ++end; + } + + download_url.erase(index, end - index); + return download_url; + } + MatrixChatPage::MatrixChatPage(Program *program, std::string room_id, MatrixRoomsPage *rooms_page, std::string jump_to_event_id) : Page(program), room_id(std::move(room_id)), rooms_page(rooms_page), jump_to_event_id(std::move(jump_to_event_id)) { @@ -2474,16 +2503,16 @@ namespace QuickMedia { return media_url.substr(start, end - start); } - static std::string get_avatar_thumbnail_url(const std::string &homeserver, const std::string &mxc_id) { + static std::string get_avatar_thumbnail_url(const std::string &homeserver, const std::string &access_token, const std::string &mxc_id) { if(mxc_id.empty()) return ""; std::string size = std::to_string(int(32 * get_config().scale)); - return homeserver + "/_matrix/media/r0/thumbnail/" + mxc_id + "?width=" + size + "&height=" + size + "&method=crop"; + return homeserver + "/_matrix/client/v1/media/thumbnail/" + mxc_id + "?width=" + size + "&height=" + size + "&method=crop&access_token=" + access_token; } std::string Matrix::get_media_url(const std::string &mxc_id) { - return homeserver + "/_matrix/media/r0/download/" + thumbnail_url_extract_media_id(mxc_id); + return homeserver + "/_matrix/client/v1/media/download/" + thumbnail_url_extract_media_id(mxc_id) + "?access_token=" + access_token; } RoomExtraData& Matrix::get_room_extra_data(RoomData *room) { @@ -2502,7 +2531,7 @@ namespace QuickMedia { std::string display_name = display_name_json.IsString() ? display_name_json.GetString() : user_id; std::string avatar_url = thumbnail_url_extract_media_id(avatar_url_str); if(!avatar_url.empty()) - avatar_url = get_avatar_thumbnail_url(homeserver, avatar_url); // TODO: Remove the constant strings around to reduce memory usage (6.3mb) + avatar_url = get_avatar_thumbnail_url(homeserver, access_token, avatar_url); // TODO: Remove the constant strings around to reduce memory usage (6.3mb) //auto user_info = std::make_shared<UserInfo>(room_data, user_id, std::move(display_name), std::move(avatar_url)); // Overwrites user data //room_data->add_user(user_info); @@ -2599,7 +2628,7 @@ namespace QuickMedia { return found_resolution; } - static std::string message_content_extract_thumbnail_url(const rapidjson::Value &content_json, const std::string &homeserver) { + static std::string message_content_extract_thumbnail_url(const rapidjson::Value &content_json, const std::string &homeserver, const std::string &access_token) { const rapidjson::Value &info_json = GetMember(content_json, "info"); if(info_json.IsObject()) { const rapidjson::Value &thumbnail_url_json = GetMember(info_json, "thumbnail_url"); @@ -2609,7 +2638,7 @@ namespace QuickMedia { return ""; thumbnail_str.erase(thumbnail_str.begin(), thumbnail_str.begin() + 6); - return homeserver + "/_matrix/media/r0/download/" + std::move(thumbnail_str); + return homeserver + "/_matrix/client/v1/media/download/" + std::move(thumbnail_str) + "?access_token=" + access_token; } } @@ -3196,7 +3225,7 @@ namespace QuickMedia { body = user_display_name + " changed his profile picture"; std::string new_avatar_url_str = thumbnail_url_extract_media_id(new_avatar_url_json.GetString()); if(!new_avatar_url_str.empty()) - new_avatar_url_str = get_avatar_thumbnail_url(homeserver, new_avatar_url_str); // TODO: Remove the constant strings around to reduce memory usage (6.3mb) + new_avatar_url_str = get_avatar_thumbnail_url(homeserver, access_token, new_avatar_url_str); // TODO: Remove the constant strings around to reduce memory usage (6.3mb) new_avatar_url = new_avatar_url_str; update_user_display_info = room_data->set_user_avatar_url(user, std::move(new_avatar_url_str), timestamp); } else if((!new_avatar_url_json.IsString() || new_avatar_url_json.GetStringLength() == 0) && prev_avatar_url_json.IsString()) { @@ -3422,8 +3451,8 @@ namespace QuickMedia { if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) return nullptr; - message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6); - message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver); + message->url = homeserver + "/_matrix/client/v1/media/download/" + (url_json.GetString() + 6) + "?access_token=" + access_token; + message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver, access_token); message_content_extract_thumbnail_size(*content_json, message->thumbnail_size); message->type = MessageType::IMAGE; } else if(!content_type.IsString() || strcmp(content_type.GetString(), "m.text") == 0) { @@ -3433,8 +3462,8 @@ namespace QuickMedia { if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) return nullptr; - message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6); - message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver); + message->url = homeserver + "/_matrix/client/v1/media/download/" + (url_json.GetString() + 6) + "?access_token=" + access_token; + message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver, access_token); message_content_extract_thumbnail_size(*content_json, message->thumbnail_size); message->type = MessageType::VIDEO; if(message->thumbnail_url.empty()) @@ -3444,7 +3473,7 @@ namespace QuickMedia { if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) return nullptr; - message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6); + message->url = homeserver + "/_matrix/client/v1/media/download/" + (url_json.GetString() + 6) + "?access_token=" + access_token; message->type = MessageType::AUDIO; prefix = "🎵 Play "; } else if(strcmp(content_type.GetString(), "m.file") == 0) { @@ -3452,7 +3481,7 @@ namespace QuickMedia { if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) return nullptr; - message->url = homeserver + "/_matrix/media/r0/download/" + (url_json.GetString() + 6); + message->url = homeserver + "/_matrix/client/v1/media/download/" + (url_json.GetString() + 6) + "?access_token=" + access_token; message->type = MessageType::FILE; prefix = "💾 Download "; } else if(strcmp(content_type.GetString(), "m.emote") == 0) { // this is a /me message, TODO: show /me messages differently @@ -3467,7 +3496,7 @@ namespace QuickMedia { prefix = geo_uri_json.GetString() + std::string(" | "); message->type = MessageType::TEXT; - message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver); + message->thumbnail_url = message_content_extract_thumbnail_url(*content_json, homeserver, access_token); message_content_extract_thumbnail_size(*content_json, message->thumbnail_size); } else if(strcmp(content_type.GetString(), "m.server_notice") == 0) { // TODO: show server notices differently message->type = MessageType::TEXT; @@ -3627,7 +3656,7 @@ namespace QuickMedia { if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) continue; - update_room_avatar_url |= room_data->set_avatar_url(get_avatar_thumbnail_url(homeserver, thumbnail_url_extract_media_id(url_json.GetString())), item_timestamp); + update_room_avatar_url |= room_data->set_avatar_url(get_avatar_thumbnail_url(homeserver, access_token, thumbnail_url_extract_media_id(url_json.GetString())), item_timestamp); room_data->avatar_is_fallback = false; } else if(strcmp(type_json.GetString(), "m.room.topic") == 0) { const rapidjson::Value &content_json = GetMember(event_item_json, "content"); @@ -5072,7 +5101,10 @@ namespace QuickMedia { remote_homeserver_url = get_remote_homeserver_url(); char url[512]; - snprintf(url, sizeof(url), "%s/_matrix/media/r0/upload?filename=%s", remote_homeserver_url.c_str(), filename_escaped.c_str()); + snprintf(url, sizeof(url), "%s%s_matrix/media/r0/upload?filename=%s", + remote_homeserver_url.c_str(), + !remote_homeserver_url.empty() && remote_homeserver_url.back() == '/' ? "" : "/", + filename_escaped.c_str()); rapidjson::Document json_root; DownloadResult download_result = download_json(json_root, url, std::move(additional_args), true, &err_msg); @@ -5778,7 +5810,7 @@ namespace QuickMedia { if(avatar_url_json.IsString()) { std::string avatar_url = thumbnail_url_extract_media_id(avatar_url_json.GetString()); if(!avatar_url.empty()) - avatar_url = get_avatar_thumbnail_url(homeserver, avatar_url); + avatar_url = get_avatar_thumbnail_url(homeserver, access_token, avatar_url); if(!avatar_url.empty()) room_body_item->thumbnail_url = std::move(avatar_url); @@ -5850,7 +5882,7 @@ namespace QuickMedia { if(avatar_url_json.IsString()) { std::string avatar_url = thumbnail_url_extract_media_id(std::string(avatar_url_json.GetString(), avatar_url_json.GetStringLength())); if(!avatar_url.empty()) - avatar_url = get_avatar_thumbnail_url(homeserver, avatar_url); + avatar_url = get_avatar_thumbnail_url(homeserver, access_token, avatar_url); body_item->thumbnail_url = std::move(avatar_url); } body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; @@ -6089,7 +6121,7 @@ namespace QuickMedia { if(avatar_url_json.IsString()) avatar_url = std::string(avatar_url_json.GetString(), avatar_url_json.GetStringLength()); if(!avatar_url.empty()) - avatar_url = get_avatar_thumbnail_url(homeserver, thumbnail_url_extract_media_id(avatar_url)); // TODO: Remove the constant strings around to reduce memory usage (6.3mb) + avatar_url = get_avatar_thumbnail_url(homeserver, access_token, thumbnail_url_extract_media_id(avatar_url)); // TODO: Remove the constant strings around to reduce memory usage (6.3mb) room->set_user_avatar_url(user, avatar_url, 0); room->set_user_display_name(user, display_name, 0); diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 2c20f3f..7095e6b 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -21,7 +21,7 @@ extern "C" { #include <unistd.h> namespace QuickMedia { - static const char *youtube_client_version = "x-youtube-client-version: 2.20210622.10.00"; + static const char *youtube_client_version = "x-youtube-client-version: 2.20250327.01.00"; static const std::array<std::string, 11> invidious_urls = { "yewtu.be", "invidious.snopyta.org", @@ -3189,26 +3189,33 @@ namespace QuickMedia { } } - const Json::Value &playability_status_json = json_root["playabilityStatus"]; - if(playability_status_json.isObject()) { - const Json::Value &status_json = playability_status_json["status"]; - if(status_json.isString() && (strcmp(status_json.asCString(), "UNPLAYABLE") == 0 || strcmp(status_json.asCString(), "LOGIN_REQUIRED") == 0)) { - fprintf(stderr, "Failed to load youtube video, trying with yt-dlp instead\n"); - if(program->youtube_dl_extract_url(url, youtube_dl_video_fallback_url, youtube_dl_audio_fallback_url)) { - if(get_config().youtube.sponsorblock.enable) - sponsorblock_add_chapters(this, url, get_config().youtube.sponsorblock.min_votes, sponsor_segments); - use_youtube_dl_fallback = true; - return PluginResult::OK; - } else { - const Json::Value &reason_json = playability_status_json["reason"]; - if(reason_json.isString()) - err_str = reason_json.asString(); - fprintf(stderr, "Unable to play video, status: %s, reason: %s\n", status_json.asCString(), reason_json.isString() ? reason_json.asCString() : "Unknown"); - return PluginResult::ERR; - } - } + if(program->youtube_dl_extract_url(url, youtube_dl_video_fallback_url, youtube_dl_audio_fallback_url)) { + if(get_config().youtube.sponsorblock.enable) + sponsorblock_add_chapters(this, url, get_config().youtube.sponsorblock.min_votes, sponsor_segments); + use_youtube_dl_fallback = true; + return PluginResult::OK; } + // const Json::Value &playability_status_json = json_root["playabilityStatus"]; + // if(playability_status_json.isObject()) { + // const Json::Value &status_json = playability_status_json["status"]; + // if(status_json.isString() && (strcmp(status_json.asCString(), "UNPLAYABLE") == 0 || strcmp(status_json.asCString(), "LOGIN_REQUIRED") == 0)) { + // fprintf(stderr, "Failed to load youtube video, trying with yt-dlp instead\n"); + // if(program->youtube_dl_extract_url(url, youtube_dl_video_fallback_url, youtube_dl_audio_fallback_url)) { + // if(get_config().youtube.sponsorblock.enable) + // sponsorblock_add_chapters(this, url, get_config().youtube.sponsorblock.min_votes, sponsor_segments); + // use_youtube_dl_fallback = true; + // return PluginResult::OK; + // } else { + // const Json::Value &reason_json = playability_status_json["reason"]; + // if(reason_json.isString()) + // err_str = reason_json.asString(); + // fprintf(stderr, "Unable to play video, status: %s, reason: %s\n", status_json.asCString(), reason_json.isString() ? reason_json.asCString() : "Unknown"); + // return PluginResult::ERR; + // } + // } + // } + const Json::Value *streaming_data_json = &json_root["streamingData"]; if(!streaming_data_json->isObject()) return PluginResult::ERR; @@ -3254,7 +3261,51 @@ namespace QuickMedia { const int num_request_types = 1; std::string request_data[num_request_types] = { R"END( - {"context":{"client":{"hl":"en","gl":"US","clientName":"IOS","clientVersion":"17.33.2","deviceModel":"iPhone14,3"}},"videoId":"%VIDEO_ID%"} +{ + "context": { + "client": { + "hl": "en", + "gl": "SE", + "deviceMake": "", + "deviceModel": "", + "userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0,gzip(gfe)", + "clientName": "WEB", + "clientVersion": "2.20250327.01.00", + "osName": "X11", + "osVersion": "", + "originalUrl": "https://www.youtube.com/watch?v=%VIDEO_ID%", + "platform": "DESKTOP", + "clientFormFactor": "UNKNOWN_FORM_FACTOR", + "userInterfaceTheme": "USER_INTERFACE_THEME_DARK", + "timeZone": "Europe/Paris", + "browserName": "Firefox", + "browserVersion": "136.0", + "acceptHeader": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "clientScreen": "WATCH", + "mainAppWebInfo": { + "graftUrl": "/watch?v=%VIDEO_ID%", + "pwaInstallabilityStatus": "PWA_INSTALLABILITY_STATUS_UNKNOWN", + "webDisplayMode": "WEB_DISPLAY_MODE_BROWSER", + "isWebNativeShareAvailable": false + } + } + }, + "videoId": "%VIDEO_ID%", + "playbackContext": { + "contentPlaybackContext": { + "currentUrl": "/watch?v=%VIDEO_ID%", + "vis": 0, + "splay": false, + "autoCaptionsDefaultOn": false, + "autonavState": "STATE_OFF", + "html5Preference": "HTML5_PREF_WANTS", + "referer": "https://www.youtube.com/", + "lactMilliseconds": "-1" + } + }, + "racyCheckOk": false, + "contentCheckOk": false +} )END", }; @@ -3263,7 +3314,7 @@ R"END( }; std::string client_versions[num_request_types] = { - "2.20210622.10.00" + "2.20250327.01.00" }; for(int i = 0; i < num_request_types; ++i) { @@ -3280,7 +3331,7 @@ R"END( additional_args.insert(additional_args.end(), cookies.begin(), cookies.end()); Json::Value json_root; - DownloadResult download_result = download_json(json_root, "https://www.youtube.com/youtubei/v1/player?key=" + api_key + "&gl=US&hl=en&prettyPrint=false", additional_args, true); + DownloadResult download_result = download_json(json_root, "https://www.youtube.com/youtubei/v1/player?prettyPrint=false", additional_args, true); if(download_result != DownloadResult::OK) continue; @@ -3423,6 +3474,7 @@ R"END( continue; } + //video_format.base.url += "&alr=yes"; video_formats.push_back(std::move(video_format)); } else if(strncmp(youtube_format_base.mime_type.c_str(), "audio/", 6) == 0) { // Some youtube videos have multiple audio tracks and sometimes the audio tracks are in the same language @@ -3445,6 +3497,7 @@ R"END( continue; } + //audio_format.base.url += "&alr=yes"; audio_formats.push_back(std::move(audio_format)); } } |