diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | include/VideoPlayer.hpp | 3 | ||||
-rw-r--r-- | mpv/input.conf | 1 | ||||
-rw-r--r-- | mpv/scripts/mordenx.lua | 10 | ||||
-rwxr-xr-x | run.sh | 4 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 16 | ||||
-rw-r--r-- | src/VideoPlayer.cpp | 49 | ||||
-rw-r--r-- | video_player/README.md | 27 | ||||
-rw-r--r-- | video_player/src/main.cpp | 34 |
9 files changed, 113 insertions, 33 deletions
@@ -68,7 +68,7 @@ Type text and then wait and QuickMedia will automatically search.\ ### Video controls `mpv` controls apply in general, see https://mpv.io/manual/master/#interactive-control.\ `Esc`/`Backspace`/`Q`: Close the video.\ -`Ctrl+F`: Toggle between fullscreen mode and window mode. `Esc` can also be used to switch from fullscreen mode to window mode.\ +`Ctrl+F`: Toggle between fullscreen mode and window mode.\ `Ctrl+R`: Show pages related to the video, such as comments, related videos or channel videos (if supported).\ `Ctrl+S`: Save the video/music.\ `Ctrl+C`: Copy the url of the currently playing video to the clipboard (with timestamp, if supported).\ diff --git a/include/VideoPlayer.hpp b/include/VideoPlayer.hpp index feede37..981c114 100644 --- a/include/VideoPlayer.hpp +++ b/include/VideoPlayer.hpp @@ -8,7 +8,7 @@ #include <X11/Xlib.h> namespace QuickMedia { - using EventCallbackFunc = std::function<void(const char *event_name)>; + using EventCallbackFunc = std::function<void(const char *event_name, const std::vector<std::string> &args)>; using VideoPlayerWindowCreateCallback = std::function<void(mgl::WindowHandle window)>; // Currently this video player launches mpv and embeds it into the QuickMedia window @@ -65,6 +65,7 @@ namespace QuickMedia { // Returns time in seconds Error get_time_in_file(double *result); Error add_subtitle(const std::string &url, const std::string &title, const std::string &lang); + Error cycle_fullscreen(); int exit_status; private: diff --git a/mpv/input.conf b/mpv/input.conf index 635a82c..26b7ef6 100644 --- a/mpv/input.conf +++ b/mpv/input.conf @@ -1,5 +1,6 @@ Ctrl+c ignore Ctrl+s ignore +f ignore BS ignore q ignore WHEEL_UP ignore diff --git a/mpv/scripts/mordenx.lua b/mpv/scripts/mordenx.lua index 1b6bd5a..2df72c3 100644 --- a/mpv/scripts/mordenx.lua +++ b/mpv/scripts/mordenx.lua @@ -114,9 +114,9 @@ local osc_styles = { SeekbarBg = '{\\blur0\\bord0\\1c&HFFFFFF&}',
SeekbarFg = '{\\blur1\\bord1\\1c&HE39C42&}',
Ctrl1 = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs36\\fnmaterial-design-iconic-font}',
- Ctrl2 = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs24\\fnmaterial-design-iconic-font}',
- Ctrl2Flip = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs24\\fnmaterial-design-iconic-font\\fry180',
- Ctrl3 = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs24\\fnmaterial-design-iconic-font}',
+ Ctrl2 = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs30\\fnmaterial-design-iconic-font}',
+ Ctrl2Flip = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs30\\fnmaterial-design-iconic-font\\fry180',
+ Ctrl3 = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&HFFFFFF&\\fs30\\fnmaterial-design-iconic-font}',
Time = '{\\blur0\\bord0\\1c&HFFFFFF&\\3c&H000000&\\fs20\\fn' .. user_opts.font .. '}',
Tooltip = '{\\blur1\\bord0.5\\1c&HFFFFFF&\\3c&H000000&\\fs18\\fn' .. user_opts.font .. '}',
Title = '{\\blur1\\bord0.5\\1c&HFFFFFF&\\3c&H0\\fs32\\q2\\fn' .. user_opts.font .. '}',
@@ -1098,7 +1098,7 @@ layouts = function () osc_param.areas = {} -- delete areas
-- area for active mouse input
- add_area('input', get_hitbox_coords(posX, posY, 1, osc_geo.w, 104))
+ add_area('input', get_hitbox_coords(posX, posY, 1, osc_geo.w, 113))
-- area for show/hide
add_area('showhide', 0, 0, osc_param.playresx, osc_param.playresy)
@@ -1489,7 +1489,7 @@ function osc_init() end
ne.visible = (osc_param.playresx >= 540)
ne.eventresponder['mbtn_left_up'] =
- function () mp.commandv('cycle', 'fullscreen') end
+ function () mp.commandv("cycle", "fullscreen") end
--tog_info
ne = new_element('tog_info', 'button')
@@ -1,6 +1,6 @@ #!/bin/sh -e -[ $# -ne 1 ] && echo "usage: run.sh debug|release" && exit 1 +[ $# -lt 1 ] && echo "usage: run.sh debug|release <args...>" && exit 1 script_dir=$(dirname "$0") cd "$script_dir" @@ -9,4 +9,4 @@ sibs build --"$1" video_player sibs build --"$1" echo "Successfully built the video player and QuickMedia" -"./sibs-build/$(sibs platform)/$1/quickmedia" +"./sibs-build/$(sibs platform)/$1/quickmedia" "${@:2}" diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 8d6e4a2..ddb81a9 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -3076,7 +3076,7 @@ namespace QuickMedia { std::string channel_url; AsyncTask<void> related_videos_task; - std::function<void(const char*)> video_event_callback; + EventCallbackFunc video_event_callback; bool go_to_previous_page = false; std::string video_url; @@ -3317,7 +3317,7 @@ namespace QuickMedia { } }; - video_event_callback = [&](const char *event_name) mutable { + video_event_callback = [&](const char *event_name, const std::vector<std::string> &args) mutable { if(strcmp(event_name, "pause") == 0) { //double time_remaining = 9999.0; //if(video_player->get_time_remaining(&time_remaining) == VideoPlayer::Error::OK && time_remaining <= 1.0) @@ -3337,6 +3337,8 @@ namespace QuickMedia { } else if(strcmp(event_name, "seek") == 0) { if(video_page->is_local()) update_time_pos = true; + } else if(strcmp(event_name, "fullscreen") == 0 && args.size() == 1) { + window_set_fullscreen(disp, window.get_system_handle(), args[0] == "yes" ? WindowFullscreenState::SET : WindowFullscreenState::UNSET); } //fprintf(stderr, "event name: %s\n", event_name); @@ -3376,13 +3378,12 @@ namespace QuickMedia { } else if(event.type == mgl::Event::KeyPressed && (event.key.code == mgl::Keyboard::Escape || event.key.code == mgl::Keyboard::Q || event.key.code == mgl::Keyboard::Backspace)) { // To be able to close the video player while the video is loading if(window_is_fullscreen(disp, window.get_system_handle())) { - window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::UNSET); + if(video_player && video_player_window && event.key.code != mgl::Keyboard::Escape) + video_player->cycle_fullscreen(); } else { current_page = previous_page; go_to_previous_page = true; } - } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::F && event.key.control) { - window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::TOGGLE); } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::C && event.key.control && !video_page->is_local()) { save_video_url_to_clipboard(); } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::F5 && !video_page->is_local()) { @@ -3401,14 +3402,15 @@ namespace QuickMedia { window.close(); } else if(pressed_keysym == XK_Escape || pressed_keysym == XK_q || pressed_keysym == XK_BackSpace) { if(window_is_fullscreen(disp, window.get_system_handle())) { - window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::UNSET); + if(pressed_keysym != XK_Escape) + video_player->cycle_fullscreen(); } else { current_page = previous_page; go_to_previous_page = true; break; } } else if(pressed_keysym == XK_f && pressing_ctrl) { - window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::TOGGLE); + video_player->cycle_fullscreen(); } else if(pressed_keysym == XK_s && pressing_ctrl && !video_page->is_local()) { video_page_download_video(video_page->get_download_url(video_get_max_height()), video_player_window); } else if(pressed_keysym == XK_F5 && !video_page->is_local()) { diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp index 2c0fe44..26e2013 100644 --- a/src/VideoPlayer.cpp +++ b/src/VideoPlayer.cpp @@ -96,8 +96,8 @@ namespace QuickMedia { video_process_id(-1), connect_tries(0), find_window_tries(0), - event_callback(_event_callback), - window_create_callback(_window_create_callback), + event_callback(std::move(_event_callback)), + window_create_callback(std::move(_window_create_callback)), window_handle(0), display(nullptr), request_id_counter(1), @@ -364,6 +364,21 @@ namespace QuickMedia { return Error::OK; } + static std::vector<std::string> json_array_to_string_list(const Json::Value &json) { + std::vector<std::string> result; + if(!json.isArray()) + return result; + + for(const Json::Value &item : json) { + if(!item.isString()) + continue; + + result.push_back(item.asString()); + } + + return result; + } + VideoPlayer::Error VideoPlayer::read_ipc_func() { Json::Value json_root; Json::CharReaderBuilder json_builder; @@ -386,18 +401,26 @@ namespace QuickMedia { if(json_reader->parse(buffer + start, buffer + i, &json_root, &json_errors) && json_root.isObject()) { const Json::Value &event = json_root["event"]; - const Json::Value &request_id_json = json_root["request_id"]; + const Json::Value &args = json_root["args"]; if(event.isString()) { if(event_callback) - event_callback(event.asCString()); + event_callback(event.asCString(), json_array_to_string_list(args)); } + const Json::Value &request_id_json = json_root["request_id"]; if(expected_request_id != 0 && request_id_json.isNumeric() && request_id_json.asUInt() == expected_request_id) { const Json::Value &status_json = json_root["status"]; - if(!status_json.isString() || strcmp(status_json.asCString(), "error") == 0) + if(!status_json.isString() || strcmp(status_json.asCString(), "error") == 0) { response_data_status = ResponseDataStatus::ERROR; - else + const char *err_msg = "Unknown"; + const Json::Value &message_json = json_root["message"]; + if(message_json.isString()) + err_msg = message_json.asCString(); + + fprintf(stderr, "VideoPlayer::send_command failed, error from video player: %s\n", err_msg); + } else { response_data_status = ResponseDataStatus::OK; + } request_response_data = json_root["data"]; } } else { @@ -440,6 +463,14 @@ namespace QuickMedia { return send_command(json_root, &result, Json::ValueType::nullValue); } + VideoPlayer::Error VideoPlayer::cycle_fullscreen() { + Json::Value json_root(Json::objectValue); + json_root["command"] = "cycle-fullscreen"; + + Json::Value result; + return send_command(json_root, &result, Json::ValueType::nullValue); + } + uint32_t VideoPlayer::get_next_request_id() { unsigned int cmd_request_id = request_id_counter; ++request_id_counter; @@ -483,12 +514,6 @@ namespace QuickMedia { else err = Error::READ_INCORRECT_TYPE; } else if(response_data_status == ResponseDataStatus::ERROR) { - const char *err_msg = "Unknown"; - const Json::Value &message_json = request_response_data["message"]; - if(message_json.isString()) - err_msg = message_json.asCString(); - - fprintf(stderr, "VideoPlayer::send_command failed, error from video player: %s\n", err_msg); err = Error::READ_RESPONSE_ERROR; goto cleanup; } else { diff --git a/video_player/README.md b/video_player/README.md index 9a74c9c..0d0adeb 100644 --- a/video_player/README.md +++ b/video_player/README.md @@ -56,9 +56,34 @@ Add a subtitle file/url that is loaded asynchronously "request_id": 233 // Optional. Its provided if request_id was provided in the request } ``` +## cycle-fullscreen +Return seeking position in file in seconds +### request +``` +{ + "command": "cycle-fullscreen", + "request_id": 232 // Optional +} +``` +### response on success +``` +{ + "status": "success", + "request_id": 232, // Optional. Its provided if request_id was provided in the request +} +``` +### response on error +``` +{ + "status": "error", + "message": "error message", + "request_id": 233 // Optional. Its provided if request_id was provided in the request +} +``` # IPC event ``` { - "event": "file-loaded" + "event": "file-loaded", + "args": [] // A list of strings } ```
\ No newline at end of file diff --git a/video_player/src/main.cpp b/video_player/src/main.cpp index 03acd93..33c4d96 100644 --- a/video_player/src/main.cpp +++ b/video_player/src/main.cpp @@ -101,6 +101,20 @@ static Json::Value handle_json_command_sub_add(mpv_handle *mpv_ctx, const Json:: return response_json; } +static Json::Value handle_json_command_cycle_fullscreen(mpv_handle *mpv_ctx) { + const char *args[] = { "cycle", "fullscreen", nullptr }; + const int res = mpv_command_async(mpv_ctx, 0, args); + + Json::Value response_json(Json::objectValue); + if(res < 0) { + response_json["status"] = "error"; + response_json["message"] = mpv_error_string(res); + } else { + response_json["status"] = "success"; + } + return response_json; +} + static void send_error(const std::string &err_msg, std::optional<int64_t> request_id, int fd) { fprintf(stderr, "Error: %s\n", err_msg.c_str()); @@ -151,10 +165,12 @@ static void handle_json_command(mpv_handle *mpv_ctx, const Json::Value &json_roo response_json = handle_json_command_time_pos(mpv_ctx); } else if(strcmp(command_json.asCString(), "sub-add") == 0) { response_json = handle_json_command_sub_add(mpv_ctx, json_root); + } else if(strcmp(command_json.asCString(), "cycle-fullscreen") == 0) { + response_json = handle_json_command_cycle_fullscreen(mpv_ctx); } else { response_json = Json::Value(Json::objectValue); response_json["status"] = "error"; - response_json["message"] = "invalid command " + command_json.asString() + ", expected time-pos or sub-add"; + response_json["message"] = "invalid command " + command_json.asString() + ", expected time-pos, sub-add or cycle-fullscreen"; } if(request_id) @@ -193,9 +209,16 @@ static void handle_request_commands_line_by_line(mpv_handle *mpv_ctx, int fd, ch command_buffer_size = 0; } -static void send_event(const char *event_name, int fd) { +static void send_event(const char *event_name, int fd, const std::vector<std::string> &args = {}) { Json::Value json_root(Json::objectValue); json_root["event"] = event_name; + if(!args.empty()) { + Json::Value args_json(Json::arrayValue); + for(const std::string &arg : args) { + args_json.append(arg); + } + json_root["args"] = std::move(args_json); + } Json::StreamWriterBuilder builder; builder["commentStyle"] = "None"; @@ -292,6 +315,7 @@ int main(int argc, char **argv) { check_error(mpv_command(mpv_ctx, cmd), "loadfile"); check_error(mpv_observe_property(mpv_ctx, 0, "idle-active", MPV_FORMAT_FLAG), "observe idle-active"); + check_error(mpv_observe_property(mpv_ctx, 0, "fullscreen", MPV_FORMAT_FLAG), "observe fullscreen"); char command_buffer[COMMAND_BUFFER_MAX_SIZE]; size_t command_buffer_size = 0; @@ -325,12 +349,14 @@ int main(int argc, char **argv) { } else if(event->event_id == MPV_EVENT_SHUTDOWN) { running = false; break; - } else if(event->event_id == MPV_EVENT_PROPERTY_CHANGE && file_started) { + } else if(event->event_id == MPV_EVENT_PROPERTY_CHANGE) { // End of file (idle) mpv_event_property *property = (mpv_event_property*)event->data; - if(strcmp(property->name, "idle-active") == 0 && *(int*)property->data == 1) { + if(file_started && strcmp(property->name, "idle-active") == 0 && *(int*)property->data == 1) { running = false; break; + } else if(args.ipc_fd && strcmp(property->name, "fullscreen") == 0) { + send_event("fullscreen", args.ipc_fd_num, { *(bool*)property->data ? "yes" : "no" }); } } |