aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md45
-rw-r--r--TODO13
m---------depends/html-parser0
m---------depends/html-search0
m---------depends/jsoncpp0
m---------depends/mglpp0
m---------depends/rapidjson0
-rw-r--r--example-config.json2
-rw-r--r--include/Config.hpp3
-rw-r--r--include/Entry.hpp1
-rw-r--r--include/ImageViewer.hpp2
-rw-r--r--include/QuickMedia.hpp3
-rw-r--r--include/Text.hpp1
-rw-r--r--include/Utils.hpp2
-rw-r--r--include/VideoPlayer.hpp1
-rwxr-xr-xinstall.sh36
-rw-r--r--meson.build138
-rw-r--r--meson_options.txt3
-rwxr-xr-xmeson_post_install.sh6
-rw-r--r--plugins/Matrix.hpp7
-rw-r--r--plugins/Page.hpp1
-rw-r--r--project.conf2
-rw-r--r--src/AsyncImageLoader.cpp1
-rw-r--r--src/Config.cpp3
-rw-r--r--src/Entry.cpp4
-rw-r--r--src/FileAnalyzer.cpp2
-rw-r--r--src/ImageViewer.cpp25
-rw-r--r--src/QuickMedia.cpp140
-rw-r--r--src/Text.cpp4
-rw-r--r--src/Utils.cpp15
-rw-r--r--src/VideoPlayer.cpp41
-rw-r--r--src/main.cpp2
-rw-r--r--src/plugins/Matrix.cpp70
-rw-r--r--src/plugins/Youtube.cpp97
-rw-r--r--uninstall.sh10
m---------video_player/jsoncpp0
-rw-r--r--video_player/src/main.cpp48
37 files changed, 542 insertions, 186 deletions
diff --git a/README.md b/README.md
index c9781ff..c694c94 100644
--- a/README.md
+++ b/README.md
@@ -5,28 +5,10 @@ A rofi inspired native client for web services.
Currently supported web services: `youtube`, `peertube`, `lbry`, `soundcloud`, `nyaa.si`, `manganelo`, `manganelos`, `mangatown`, `mangakatana`, `mangadex`, `onimanga`, `4chan`, `matrix`, `saucenao`, `hotexamples`, `anilist`, `dramacool` and _others_.\
QuickMedia also supports reading local manga and watching local anime, see [local manga](#local-manga) and [local anime](#local-anime)
## Usage
-```
-usage: quickmedia [plugin|] [--no-video] [--upscale-images] [--upscale-images-always] [--dir <directory>] [--instance <instance>] [-e <window>] [--video-max-height <height>]
-OPTIONS:
- 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 or stdin. This can also be a youtube url, youtube channel url or a 4chan thread url
- --no-video Only play audio when playing a video. Disabled by default
- --upscale-images Upscale low-resolution manga pages using waifu2x-ncnn-vulkan. Disabled by default
- --upscale-images-always Upscale manga pages using waifu2x-ncnn-vulkan, no matter what the original image resolution is. Disabled by default
- --dir <directory> Set the start directory when using file-manager
- --instance <instance> The instance to use for peertube
- -e <window-id> Embed QuickMedia into another window
- --video-max-height <height> Media plugins will try to select a video source that is this size or smaller
-EXAMPLES:
- quickmedia
- quickmedia --upscale-images-always manganelo
- quickmedia https://www.youtube.com/watch?v=jHg91NVHh3s
- echo -e "hello\nworld" | quickmedia stdin
- tabbed -c -k quickmedia launcher -e
-```
+Run `qm --help` to see the usage options.
## Installation
-If you are running arch linux then you can install QuickMedia from aur: [https://aur.archlinux.org/packages/quickmedia-git/](https://aur.archlinux.org/packages/quickmedia-git/), otherwise you will need to first install [sibs](https://git.dec05eba.com/sibs/) and then run `./install.sh` as root.\
+If you are running arch linux then you can install QuickMedia from aur: [https://aur.archlinux.org/packages/quickmedia-git/](https://aur.archlinux.org/packages/quickmedia-git/), otherwise you will need to run `./install.sh` as root.\
`tar` and curl needs to be installed to run the `install.sh` script.\
-Installing `lld` (the LLVM linker) can improve compile times.\
There is also an unofficial ebuild for gentoo users. On gentoo you can install QuickMedia by running these commands:
```bash
eselect repository add overlay-from-plan9 git https://git.plan9.rocks/cat/overlay-from-plan9
@@ -34,15 +16,17 @@ emerge --sync overlay-from-plan9
emerge -av quickmedia
```
## Dependencies
+Meson needs to be installed to run the build script.
### Libraries
`libglvnd (LibGL.so)`, `libx11`, `libxrandr`, `libmpv`
### Executables
-`curl`
+`curl`.\
+`yt-dlp` needs to be installed right now to play youtube videos. This requirement will be removed in the future.
### Fonts
`noto-fonts` (when `use_system_fonts` config is not set to `true`)
### Optional
`noto-fonts-cjk` needs to be installed to view chinese, japanese and korean characters (when `use_system_fonts` config is not set to `true`).\
-`yt-dlp/youtube-dl` needs to be installed to download soundcloud music, fallback for youtube (for example for age restricted videos) or to play/download xxx videos.\
+`yt-dlp` needs to be installed to download soundcloud music or to play/download xxx videos.\
`notify-send` (which is part of `libnotify`) needs to be installed to show notifications (on Linux and other systems that uses d-bus notification system).\
[automedia](https://git.dec05eba.com/AutoMedia/) needs to be installed when tracking anime/manga with `Ctrl + T`.\
`waifu2x-ncnn-vulkan` needs to be installed when using the `--upscale-images` or `--upscale-images-always` option.\
@@ -105,6 +89,7 @@ Type text and then wait and QuickMedia will automatically search.\
`Home`: Go to the first page.\
`End`: Go to the last page.\
`F`: Toggle between scaling the image to the window size or only down scaling if the image is too large.\
+`B`: Show/hide the bottom progress bar.\
`I`: Switch to scroll view.
### Manga scroll view controls
`Arrow up`/`K`: Move up.\
@@ -113,6 +98,7 @@ Type text and then wait and QuickMedia will automatically search.\
`Home`: Go to the first page.\
`End`: Go to the last page.\
`F`: Toggle between scaling the image to the window size or only down scaling if the image is too large.\
+`B`: Show/hide the bottom progress bar.\
`I`: Switch to page view.
### Manga chapters controls
`Ctrl+T`: Start tracking the manga after the selected chapter ([AutoMedia](https://git.dec05eba.com/AutoMedia/) needs to be installed).
@@ -144,7 +130,9 @@ Type text and then wait and QuickMedia will automatically search.\
`Alt+End`: Select the last room in the room list.\
`Ctrl+P`: Pin the selected message.\
`Ctrl+R`: Navigate to the replied to message.\
-`Ctrl+B`: Navigate to the bottom (the latest message).
+`Ctrl+B`: Navigate to the bottom (the latest message).\
+`G`: Start typing an `/encrypt` message, `Esc` to cancel.\
+`Ctrl+G`: Start replying to the selected message with `/encrypt`, `Esc` to cancel.
#### Pinned messages page controls
`Ctrl+D`: Unpin the selected message.\
`Ctrl+R`: Navigate to the pinned message in the messages tab.
@@ -184,16 +172,7 @@ Type text and then wait and QuickMedia will automatically search.\
`Ctrl+Enter`/`Click on save`: Save the file.\
`Esc`/`Click on cancel`: Cancel download.
## Matrix text commands
-`/help`: Show a list of valid commands.\
-`/upload`: Bring up the file manager and select a file to upload to the room, `Esc` to cancel.\
-`/join [room]`: Join a room by name or id.\
-`/invite`: Invite a user to the room.\
-`/logout`: Logout.\
-`/leave`: Leave the current room.\
-`/me [text]`: Send a message of type "m.emote".\
-`/react [text]`: React to the selected message (also works if you are replying to a message).\
-`/id`: Show the room id.\
-`/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".
+`/help`: Show a list of valid commands.
## Matrix gpg encryption
Matrix gpg email should use the matrix user id, but in email format. For example: @dec05eba:matrix.org should be created with a gpg with the email dec05eba@matrix.org.
## Config
diff --git a/TODO b/TODO
index 0d7af13..e56219f 100644
--- a/TODO
+++ b/TODO
@@ -298,4 +298,15 @@ Use DPMSInfoNotify.
Use stb_image_resize2.h
Youtube audio only download if audio stream not available, also for youtube-dl fallback.
Make history (local-manga and others) use relative path to the downloads directory for thumbnails. Otherwise the thumbnails wont show when moving the download directory.
-Keep the rooms that we were kicked/banned from so we can still read them and re-read the reason for why we were kicked/banned. Or add a list of historical rooms with leave reason. \ No newline at end of file
+Keep the rooms that we were kicked/banned from so we can still read them and re-read the reason for why we were kicked/banned. Or add a list of historical rooms with leave reason.
+Fix youtube broken without yt-dlp. Unable to download video.
+Use different font for chinese, japanese and korean (or maybe only for korean).
+Include noto font instead of having to install it. Only require installing it for other noto fonts.
+Re-design matrix /encrypt. You should press G to switch to encryption mode in the room (save it as a room variable, maybe even in file?) and when you do that the matrix icon beside the chat
+ will have a padlock in front of it and input text will be orange.
+ Press G again to switch to regular text mode.
+ Also encrypt media and maybe even reactions.
+ Add /gpg command to send your gpg key. Generate it if it doesn't exist in gpg --list-keys for your matrix id.
+ Change matrix gpg config to enable option instead.
+ Allow pressing enter on a message with PGP key to import it.
+ Cache decrypted gpg messages in ram, so they dont have to be decrypted again when closing the room and entering it again.
diff --git a/depends/html-parser b/depends/html-parser
-Subproject ec0e71c259d5aa8be7b8456509b5617d27742b6
+Subproject 3cf44ec7fba308a4b33b0486545b33e334bed74
diff --git a/depends/html-search b/depends/html-search
-Subproject 68989816f43ceda8655c2dbdc0d581971e645fc
+Subproject a77706c37d3b6df3049f658dd220b813fe1834b
diff --git a/depends/jsoncpp b/depends/jsoncpp
-Subproject f23fb32fd9d9c3d01fa67afa0d75f7ff227647e
+Subproject 98e28b38ffbae24c0e69e620bf18f8a0c5c6f20
diff --git a/depends/mglpp b/depends/mglpp
-Subproject 1a2a6ad8ef354f62bdf9adcfef73af1e5d03695
+Subproject 9634e4fef56016f59d45b0cb530087ab36f0171
diff --git a/depends/rapidjson b/depends/rapidjson
-Subproject a42f7cc32be7c1d2100d30098e15ff2661c4a61
+Subproject 49b4e2dc7892f27b56a5a9f058c2c9969609324
diff --git a/example-config.json b/example-config.json
index 83858c8..7fcb75d 100644
--- a/example-config.json
+++ b/example-config.json
@@ -152,6 +152,8 @@
"use_system_fonts": false,
// Use system (~/.config/mpv) mpv.conf instead of the one provided by QuickMedia
"use_system_mpv_config": false,
+ // Which mpv profile to use when |use_system_mpv_config| is set to true
+ "system_mpv_profile": "",
// Enable shaders. Only set to false if you have a very slow computer or dont care about graphical effects
// such as rounded corners. Rounded corners can also be disabled with the theme option "rounded_rectangles"
"enable_shaders": true,
diff --git a/include/Config.hpp b/include/Config.hpp
index e737fee..fe210a6 100644
--- a/include/Config.hpp
+++ b/include/Config.hpp
@@ -132,6 +132,7 @@ namespace QuickMedia {
FileManagerConfig file_manager;
bool use_system_fonts = false;
bool use_system_mpv_config = false;
+ std::string system_mpv_profile;
bool enable_shaders = true;
std::string theme = "default";
float scale = 1.0f;
@@ -140,5 +141,5 @@ namespace QuickMedia {
bool low_latency_mode = false;
};
- const Config& get_config();
+ Config& get_config();
} \ No newline at end of file
diff --git a/include/Entry.hpp b/include/Entry.hpp
index f4cbc5d..54a5713 100644
--- a/include/Entry.hpp
+++ b/include/Entry.hpp
@@ -32,6 +32,7 @@ namespace QuickMedia {
void replace(size_t start_index, size_t length, const std::string &insert_str);
int get_caret_index() const;
+ void set_caret_index(int index);
bool is_editable() const;
float get_height();
diff --git a/include/ImageViewer.hpp b/include/ImageViewer.hpp
index b587b82..b3b18dc 100644
--- a/include/ImageViewer.hpp
+++ b/include/ImageViewer.hpp
@@ -104,5 +104,7 @@ namespace QuickMedia {
mgl::vec2d prev_size_first_page;
mgl::vec2d prev_size_last_page;
+
+ bool show_progress_bar = true;
};
} \ No newline at end of file
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp
index a6de75c..c316f5e 100644
--- a/include/QuickMedia.hpp
+++ b/include/QuickMedia.hpp
@@ -119,7 +119,7 @@ namespace QuickMedia {
void set_clipboard(const std::string &str);
bool youtube_dl_extract_url(const std::string &url, std::string &video_url, std::string &audio_url);
private:
- void init(mgl::WindowHandle parent_window, std::string &program_path, bool no_dialog);
+ void init(mgl::WindowHandle parent_window, std::string &program_path, bool no_dialog, const std::string &theme);
const char* get_youtube_dl_program_name();
void check_youtube_dl_installed(const std::string &plugin_name);
void load_plugin_by_name(std::vector<Tab> &tabs, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler, std::string instance);
@@ -245,5 +245,6 @@ namespace QuickMedia {
std::mutex login_inputs_mutex;
const char *yt_dl_name = nullptr;
bool yt_dl_name_checked = false;
+ bool show_manga_bottom_bar = true;
};
}
diff --git a/include/Text.hpp b/include/Text.hpp
index 89aba99..21b6dc0 100644
--- a/include/Text.hpp
+++ b/include/Text.hpp
@@ -117,6 +117,7 @@ namespace QuickMedia
void replace(size_t start_index, size_t length, const std::string &insert_str);
int getCaretIndex() const;
+ void setCaretIndex(int index);
int getNumLines() const;
void set_color(mgl::Color color, bool force_color = false);
diff --git a/include/Utils.hpp b/include/Utils.hpp
index fe0e583..370311a 100644
--- a/include/Utils.hpp
+++ b/include/Utils.hpp
@@ -8,7 +8,7 @@ namespace QuickMedia {
void show_virtual_keyboard();
void hide_virtual_keyboard();
bool is_touch_enabled();
- bool is_running_wayland();
+ bool is_running_wayland(void *dpy);
time_t iso_utc_to_unix_time(const char *time_str);
std::string unix_time_to_local_time_str(time_t unix_time);
int64_t get_boottime_milliseconds();
diff --git a/include/VideoPlayer.hpp b/include/VideoPlayer.hpp
index bea03fc..ea5996a 100644
--- a/include/VideoPlayer.hpp
+++ b/include/VideoPlayer.hpp
@@ -39,6 +39,7 @@ namespace QuickMedia {
mgl::WindowHandle parent_window;
bool no_video = false;
bool use_system_mpv_config = false;
+ std::string system_mpv_profile;
bool use_system_input_config = false; // |use_system_mpv_config| has to be true if this is set to true
bool keep_open = false;
bool resume = false;
diff --git a/install.sh b/install.sh
index 64ee28f..e8743e4 100755
--- a/install.sh
+++ b/install.sh
@@ -5,35 +5,9 @@ cd "$script_dir"
[ $(id -u) -ne 0 ] && echo "You need root privileges to run the install script" && exit 1
-curl -sfL 'https://dec05eba.com/files/twemoji.tar.gz' -o /tmp/twemoji.tar.gz
-mkdir -p /usr/share/quickmedia/emoji
-tar xf /tmp/twemoji.tar.gz --directory=/usr/share/quickmedia/emoji
-rm -f /tmp/twemoji.tar.gz
+rm -rf build
+meson setup build
+meson configure --prefix=/usr --buildtype=release -Dstrip=true build
+ninja -C build install
-sibs build --release video_player
-sibs build --release
-
-install -Dm755 "video_player/sibs-build/$(sibs platform)/release/quickmedia-video-player" "/usr/bin/quickmedia-video-player"
-install -Dm755 "sibs-build/$(sibs platform)/release/quickmedia" "/usr/bin/quickmedia"
-ln -sf "/usr/bin/quickmedia" "/usr/bin/qm"
-install -Dm644 boards.json "/usr/share/quickmedia/boards.json"
-
-install -Dm644 example-config.json "/usr/share/quickmedia/example-config.json"
-install -Dm644 README.md "/usr/share/quickmedia/README.md"
-
-install -Dm644 mpv/fonts/Material-Design-Iconic-Font.ttf "/usr/share/quickmedia/mpv/fonts/Material-Design-Iconic-Font.ttf"
-install -Dm644 mpv/scripts/mordenx.lua "/usr/share/quickmedia/mpv/scripts/mordenx.lua"
-install -Dm644 mpv/scripts/ytdl_hook.lua "/usr/share/quickmedia/mpv/scripts/ytdl_hook.lua"
-install -Dm644 mpv/input.conf "/usr/share/quickmedia/mpv/input.conf"
-install -Dm644 mpv/mpv.conf "/usr/share/quickmedia/mpv/mpv.conf"
-
-for file in images/* icons/* shaders/* themes/*; do
- install -Dm644 "$file" "/usr/share/quickmedia/$file"
-done
-
-for file in launcher/*; do
- filename=$(basename "$file")
- install -Dm644 "$file" "/usr/share/applications/$filename"
-done
-
-echo "Successfully installed QuickMedia"
+echo "Successfully installed quickmedia" \ No newline at end of file
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..a675ac8
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,138 @@
+project('quickmedia', ['c', 'cpp'], version : '1.0.0', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
+
+if get_option('buildtype') == 'debug'
+ add_project_arguments('-g3', language : ['c', 'cpp'])
+elif get_option('buildtype') == 'release'
+ add_project_arguments('-DNDEBUG', language : ['c', 'cpp'])
+endif
+
+src = [
+ 'external/hash-library/sha256.cpp',
+ 'generated/Tlds.cpp',
+ 'generated/Emoji.cpp',
+ 'src/plugins/utils/aes.c',
+ 'src/plugins/Fourchan.cpp',
+ 'src/plugins/Mangadex.cpp',
+ 'src/plugins/ImageBoard.cpp',
+ 'src/plugins/MangaCombined.cpp',
+ 'src/plugins/Manga.cpp',
+ 'src/plugins/Lbry.cpp',
+ 'src/plugins/NyaaSi.cpp',
+ 'src/plugins/HotExamples.cpp',
+ 'src/plugins/FileManager.cpp',
+ 'src/plugins/MediaGeneric.cpp',
+ 'src/plugins/Matrix.cpp',
+ 'src/plugins/Plugin.cpp',
+ 'src/plugins/MangaGeneric.cpp',
+ 'src/plugins/Pipe.cpp',
+ 'src/plugins/Manganelo.cpp',
+ 'src/plugins/LocalManga.cpp',
+ 'src/plugins/Page.cpp',
+ 'src/plugins/Peertube.cpp',
+ 'src/plugins/LocalAnime.cpp',
+ 'src/plugins/AniList.cpp',
+ 'src/plugins/DramaCool.cpp',
+ 'src/plugins/utils/UniqueProcess.cpp',
+ 'src/plugins/utils/WatchProgress.cpp',
+ 'src/plugins/utils/EpisodeNameParser.cpp',
+ 'src/plugins/Info.cpp',
+ 'src/plugins/Youtube.cpp',
+ 'src/plugins/Saucenao.cpp',
+ 'src/plugins/Soundcloud.cpp',
+ 'src/Theme.cpp',
+ 'src/Storage.cpp',
+ 'src/Text.cpp',
+ 'src/Config.cpp',
+ 'src/DownloadUtils.cpp',
+ 'src/Json.cpp',
+ 'src/gui/Button.cpp',
+ 'src/SearchBar.cpp',
+ 'src/RoundedRectangle.cpp',
+ 'src/Entry.cpp',
+ 'src/Notification.cpp',
+ 'src/AsyncImageLoader.cpp',
+ 'src/ImageViewer.cpp',
+ 'src/Body.cpp',
+ 'src/Program.cpp',
+ 'src/main.cpp',
+ 'src/ImageUtils.cpp',
+ 'src/NetUtils.cpp',
+ 'src/BodyItem.cpp',
+ 'src/Downloader.cpp',
+ 'src/ResourceLoader.cpp',
+ 'src/VideoPlayer.cpp',
+ 'src/Tabs.cpp',
+ 'src/FileAnalyzer.cpp',
+ 'src/QuickMedia.cpp',
+ 'src/M3U8.cpp',
+ 'src/Utils.cpp',
+ 'src/StringUtils.cpp',
+]
+
+mglpp_proj = subproject('mglpp')
+mglpp_dep = mglpp_proj.get_variable('mglpp_dep')
+
+jsoncpp_proj = subproject('jsoncpp')
+jsoncpp_dep = jsoncpp_proj.get_variable('jsoncpp_dep')
+
+rapidjson_proj = subproject('rapidjson')
+rapidjson_dep = rapidjson_proj.get_variable('rapidjson_dep')
+
+html_parser_proj = subproject('html-parser')
+html_parser_dep = html_parser_proj.get_variable('html_parser_dep')
+
+html_search_proj = subproject('html-search')
+html_search_dep = html_search_proj.get_variable('html_search_dep')
+
+prefix = get_option('prefix')
+datadir = get_option('datadir')
+qm_resources_path = join_paths(prefix, datadir, 'quickmedia')
+
+if get_option('native_arch') == true
+ add_project_arguments('-march=native', language: ['c', 'cpp'])
+endif
+
+executable(
+ meson.project_name(),
+ src,
+ install : true,
+ dependencies : [
+ mglpp_dep,
+ jsoncpp_dep,
+ rapidjson_dep,
+ html_parser_dep,
+ html_search_dep,
+ dependency('threads'),
+ ],
+)
+
+executable(
+ 'quickmedia-video-player',
+ [
+ 'video_player/src/main.cpp'
+ ],
+ install : true,
+ dependencies : [
+ jsoncpp_dep,
+ dependency('mpv'),
+ ],
+)
+
+install_subdir('mpv', install_dir : qm_resources_path)
+install_subdir('images', install_dir : qm_resources_path)
+install_subdir('icons', install_dir : qm_resources_path)
+install_subdir('shaders', install_dir : qm_resources_path)
+install_subdir('themes', install_dir : qm_resources_path)
+
+install_data('launcher/QuickMedia.desktop', install_dir : join_paths(prefix, datadir, 'applications'))
+install_data(files('boards.json'), install_dir : qm_resources_path)
+install_data(files('example-config.json'), install_dir : qm_resources_path)
+install_data(files('README.md'), install_dir : qm_resources_path)
+
+if get_option('install_symlink') == true
+ install_symlink('qm', install_dir : join_paths(prefix, 'bin'), pointing_to: join_paths(prefix, 'bin', meson.project_name()))
+endif
+
+if get_option('install_emoji') == true
+ meson.add_install_script('meson_post_install.sh')
+endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..0232def
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,3 @@
+option('native_arch', type : 'boolean', value : true, description : 'Build specifically for your own cpu for best performance')
+option('install_symlink', type : 'boolean', value : true, description : 'Install a symlink for qm to quickmedia')
+option('install_emoji', type : 'boolean', value : true, description : 'Download and install emoji')
diff --git a/meson_post_install.sh b/meson_post_install.sh
new file mode 100755
index 0000000..1c87f9f
--- /dev/null
+++ b/meson_post_install.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+curl -sfL 'https://dec05eba.com/files/twemoji.tar.gz' -o /tmp/twemoji.tar.gz
+mkdir -p "${MESON_INSTALL_DESTDIR_PREFIX}/share/quickmedia/emoji"
+tar xf /tmp/twemoji.tar.gz --directory="${MESON_INSTALL_DESTDIR_PREFIX}/share/quickmedia/emoji"
+rm -f /tmp/twemoji.tar.gz \ No newline at end of file
diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index 23b0a8a..f8b10ae 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -18,7 +18,7 @@ namespace QuickMedia {
struct RoomData;
struct Message;
- static const int AUTHOR_MAX_LENGTH = 48;
+ static const int AUTHOR_MAX_LENGTH = 192;
class Matrix;
@@ -548,8 +548,9 @@ namespace QuickMedia {
class MatrixVideoPage : public VideoPage {
public:
MatrixVideoPage(Program *program, std::string filename) : VideoPage(program, ""), filename(std::move(filename)) {}
- const char* get_title() const override { return ""; }
- std::string get_filename() override { return filename; }
+ const char* get_title() const override;
+ std::string get_filename() override;
+ std::string get_download_url_for_clipboard(int max_height) override;
private:
std::string filename;
};
diff --git a/plugins/Page.hpp b/plugins/Page.hpp
index 044759a..9e844b3 100644
--- a/plugins/Page.hpp
+++ b/plugins/Page.hpp
@@ -165,6 +165,7 @@ namespace QuickMedia {
virtual void set_url(std::string new_url) { url = std::move(new_url); }
std::string get_url() { return url; }
virtual std::string get_download_url(int max_height) { (void)max_height; return url; }
+ virtual std::string get_download_url_for_clipboard(int max_height) { return get_download_url(max_height); }
// Returns empty string for no timestamp or if the video doesn't support timestamps.
// Timestamp is in seconds.
virtual std::string get_url_timestamp() { return ""; }
diff --git a/project.conf b/project.conf
index b737123..5f01694 100644
--- a/project.conf
+++ b/project.conf
@@ -7,7 +7,7 @@ platforms = ["posix"]
[config]
# This needs to be commented out for now because rapidjson depends on undefined behavior according to gcc...
#error_on_warning = "true"
-ignore_dirs = ["video_player"]
+ignore_dirs = ["video_player", "build"]
[lang.cpp]
version = "c++17"
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));
}
}
diff --git a/uninstall.sh b/uninstall.sh
new file mode 100644
index 0000000..a1acbbf
--- /dev/null
+++ b/uninstall.sh
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+script_dir=$(dirname "$0")
+cd "$script_dir"
+
+[ $(id -u) -ne 0 ] && echo "You need root privileges to run the uninstall script" && exit 1
+
+ninja -C build uninstall
+
+echo "Successfully uninstalled quickmedia" \ No newline at end of file
diff --git a/video_player/jsoncpp b/video_player/jsoncpp
-Subproject f23fb32fd9d9c3d01fa67afa0d75f7ff227647e
+Subproject 98e28b38ffbae24c0e69e620bf18f8a0c5c6f20
diff --git a/video_player/src/main.cpp b/video_player/src/main.cpp
index 54b5bb4..15092d9 100644
--- a/video_player/src/main.cpp
+++ b/video_player/src/main.cpp
@@ -307,21 +307,27 @@ static bool string_looks_like_double(const char *str, size_t size) {
return true;
}
-static void mpv_set_before_init_options(mpv_handle *mpv_ctx, const Args &args) {
+static void set_mpv_properties(mpv_handle *mpv_ctx, const MpvProperty &property, bool is_string_property) {
long value_long = 0;
double value_double = 0;
+ if(!is_string_property && string_looks_like_int(property.value.c_str(), property.value.size()) && string_to_long(property.value.c_str(), value_long))
+ check_error(mpv_set_option(mpv_ctx, property.key.c_str(), MPV_FORMAT_INT64, &value_long), property.key.c_str());
+ else if(!is_string_property && string_looks_like_double(property.value.c_str(), property.value.size()) && string_to_double(property.value.c_str(), value_double))
+ check_error(mpv_set_option(mpv_ctx, property.key.c_str(), MPV_FORMAT_DOUBLE, &value_double), property.key.c_str());
+ else
+ check_error(mpv_set_option_string(mpv_ctx, property.key.c_str(), property.value.c_str()), property.key.c_str());
+}
- std::set<std::string> known_string_properties = { "start", "force-media-title" };
+static void mpv_set_before_init_options(mpv_handle *mpv_ctx, const Args &args) {
+ const std::set<std::string> known_string_properties = { "start", "force-media-title" };
+ const std::set<std::string> properties_to_ignore = { "profile" };
for(const MpvProperty &property : args.mpv_properties) {
- const bool is_string_property = known_string_properties.find(property.key) != known_string_properties.end();
+ if(properties_to_ignore.find(property.key) != properties_to_ignore.end())
+ continue;
- if(!is_string_property && string_looks_like_int(property.value.c_str(), property.value.size()) && string_to_long(property.value.c_str(), value_long))
- check_error(mpv_set_option(mpv_ctx, property.key.c_str(), MPV_FORMAT_INT64, &value_long), property.key.c_str());
- else if(!is_string_property && string_looks_like_double(property.value.c_str(), property.value.size()) && string_to_double(property.value.c_str(), value_double))
- check_error(mpv_set_option(mpv_ctx, property.key.c_str(), MPV_FORMAT_DOUBLE, &value_double), property.key.c_str());
- else
- check_error(mpv_set_option_string(mpv_ctx, property.key.c_str(), property.value.c_str()), property.key.c_str());
+ const bool is_string_property = known_string_properties.find(property.key) != known_string_properties.end();
+ set_mpv_properties(mpv_ctx, property, is_string_property);
}
if(args.audio_file) {
@@ -341,6 +347,19 @@ static void mpv_set_before_init_options(mpv_handle *mpv_ctx, const Args &args) {
}
}
+static void mpv_set_after_init_options(mpv_handle *mpv_ctx, const Args &args) {
+ const std::set<std::string> known_string_properties = { "profile" };
+ const std::set<std::string> properties_to_include = { "profile" };
+
+ for(const MpvProperty &property : args.mpv_properties) {
+ if(properties_to_include.find(property.key) == properties_to_include.end())
+ continue;
+
+ const bool is_string_property = known_string_properties.find(property.key) != known_string_properties.end();
+ set_mpv_properties(mpv_ctx, property, is_string_property);
+ }
+}
+
#define READ_END 0
#define WRITE_END 1
@@ -473,8 +492,14 @@ static int64_t size_fn(void *cookie) {
if(res == 0) {
size_t header_start = str_find_case_insensitive(buffer, 0, "200 OK", 6);
if(header_start == std::string::npos) {
- res = MPV_ERROR_UNSUPPORTED;
- goto end;
+ header_start = str_find_case_insensitive(buffer, 0, "HTTP/3 200", 10);
+ if(header_start == std::string::npos)
+ header_start = str_find_case_insensitive(buffer, 0, "HTTP/2 200", 10);
+
+ if(header_start == std::string::npos) {
+ res = MPV_ERROR_UNSUPPORTED;
+ goto end;
+ }
}
header_start += 6;
@@ -624,6 +649,7 @@ int main(int argc, char **argv) {
mpv_set_before_init_options(mpv_ctx, args);
check_error(mpv_initialize(mpv_ctx), "mpv_initialize");
+ mpv_set_after_init_options(mpv_ctx, args);
check_error(mpv_stream_cb_add_ro(mpv_ctx, "qm-yt", nullptr, open_fn), "mpv_stream_cb_add_ro");
check_error(mpv_observe_property(mpv_ctx, 0, "idle-active", MPV_FORMAT_FLAG), "observe idle-active");