diff --git a/MANIFEST.in b/MANIFEST.in index 4115d88..5e709ae 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include plex_mpv_shim/systray.png -recursive-include plex_mpv_shim/default_shader_pack * \ No newline at end of file +recursive-include plex_mpv_shim/default_shader_pack * +include jellyfin_mpv_shim/mouse.lua diff --git a/README.md b/README.md index 71fc939..80b58ea 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,8 @@ Currently on Windows the built-in MPV does not work with SVP. You must download - `stop_idle` - Stop the player when idle. (Requires `idle_when_paused`.) Default: `false` - `skip_intro_always` - Always skip intros, without asking. Default: `false` - `skip_intro_prompt` - Prompt to skip intro via seeking. Default: `true` + - `menu_mouse` - Enable mouse support in the menu. Default: `true` + - This requires MPV to be compiled with lua support. ### MPV Configuration diff --git a/build-debug.bat b/build-debug.bat index 08951ed..1a13463 100644 --- a/build-debug.bat +++ b/build-debug.bat @@ -1,4 +1,4 @@ @echo off git pull rd /s /q __pycache__ dist build -pyinstaller -cF --add-binary "mpv-2.dll;." --add-binary "plex_mpv_shim\systray.png;." --icon media.ico run.py --hidden-import pystray._win32 +pyinstaller -cF --add-binary "mpv-2.dll;." --add-binary "plex_mpv_shim\systray.png;." --add-data "plex_mpv_shim\mouse.lua;plex_mpv_shim" --icon media.ico run.py --hidden-import pystray._win32 diff --git a/build.bat b/build.bat index 7cc7b52..f43d485 100644 --- a/build.bat +++ b/build.bat @@ -1,7 +1,7 @@ @echo off git pull rd /s /q __pycache__ dist build -pyinstaller -w --add-binary "mpv-2.dll;." --add-binary "plex_mpv_shim\systray.png;." --add-data "plex_mpv_shim\default_shader_pack;plex_mpv_shim\default_shader_pack" --hidden-import pystray._win32 --icon media.ico run.py +pyinstaller -w --add-binary "mpv-2.dll;." --add-binary "plex_mpv_shim\systray.png;." --add-data "plex_mpv_shim\mouse.lua;plex_mpv_shim" --add-data "plex_mpv_shim\default_shader_pack;plex_mpv_shim\default_shader_pack" --hidden-import pystray._win32 --icon media.ico run.py if %errorlevel% neq 0 exit /b %errorlevel% del dist\run\run.exe.manifest copy hidpi.manifest dist\run\run.exe.manifest diff --git a/plex_mpv_shim/conf.py b/plex_mpv_shim/conf.py index 73c338a..1beebf7 100644 --- a/plex_mpv_shim/conf.py +++ b/plex_mpv_shim/conf.py @@ -78,6 +78,7 @@ class Settings(object): "svp_url": "http://127.0.0.1:9901/", "svp_socket": None, "shader_pack_subtype": "lq", + "menu_mouse": True, } def __getattr__(self, name): diff --git a/plex_mpv_shim/menu.py b/plex_mpv_shim/menu.py index 1f8a75d..52f9fc7 100644 --- a/plex_mpv_shim/menu.py +++ b/plex_mpv_shim/menu.py @@ -54,6 +54,7 @@ def __init__(self, playerManager): self.menu_list = [] self.menu_selection = 0 self.menu_tmp = None + self.mouse_back = False self.original_osd_color = playerManager._player.osd_back_color self.original_osd_size = playerManager._player.osd_font_size @@ -83,15 +84,28 @@ def refresh_menu(self): items = self.menu_list selected_item = self.menu_selection - menu_text = "{0}".format(self.menu_title) + if self.mouse_back: + menu_text = "(<--) {0}".format(self.menu_title) + else: + menu_text = self.menu_title for i, item in enumerate(items): fmt = "\n {0}" - if i == selected_item: + if i == selected_item and not self.mouse_back: fmt = "\n **{0}**" menu_text += fmt.format(item[0]) self.playerManager._player.show_text(menu_text,2**30,1) + def mouse_select(self, idx): + if idx < 0 or idx > len(self.menu_list): + return + if idx == 0: + self.mouse_back = True + else: + self.mouse_back = False + self.menu_selection = idx - 1 + self.refresh_menu() + def show_menu(self): self.is_menu_shown = True player = self.playerManager._player @@ -101,8 +115,11 @@ def show_menu(self): if hasattr(player, 'osc'): player.osc = False + player.command("script-message", "shim-menu-enable", "True") + self.menu_title = "Main Menu" self.menu_selection = 0 + self.mouse_back = False if self.playerManager._media_item and not player.playback_abort: self.menu_list = [ @@ -154,6 +171,8 @@ def hide_menu(self): if hasattr(player, 'osc'): player.osc = settings.enable_osc + player.command("script-message", "shim-menu-enable", "False") + if player.playback_abort: player.play("") else: @@ -184,9 +203,13 @@ def menu_action(self, action): else: self.menu_title, self.menu_list, self.menu_selection = self.menu_stack.get_nowait() elif action == "ok": - self.menu_list[self.menu_selection][1]() + if self.mouse_back: + self.menu_action("back") + else: + self.menu_list[self.menu_selection][1]() elif action == "home": self.show_menu() + self.mouse_back = False self.refresh_menu() def change_audio_menu_handle(self): diff --git a/plex_mpv_shim/mouse.lua b/plex_mpv_shim/mouse.lua new file mode 100644 index 0000000..2816579 --- /dev/null +++ b/plex_mpv_shim/mouse.lua @@ -0,0 +1,40 @@ +last_idx = -1 +function mouse_handler() + local x, y = mp.get_mouse_pos() + local hy = mp.get_property_native("osd-height") + if hy == nil + then + return + end + idx = math.floor((y * 1000 / hy - 33) / 55) + if idx ~= last_idx + then + last_idx = idx + mp.commandv("script-message", "shim-menu-select", idx) + end +end + +function mouse_click_handler() + last_idx = -1 -- Force refresh. + mouse_handler() + mp.commandv("script-message", "shim-menu-click") +end + +function client_message_handler(event) + if event["args"][1] == "shim-menu-enable" + then + if event["args"][2] == "True" + then + mp.log("info", "Enabled shim menu mouse events.") + mp.add_key_binding("MOUSE_BTN0", "shim_mouse_click_handler", mouse_click_handler) + mp.add_key_binding("MOUSE_MOVE", "shim_mouse_move_handler", mouse_handler) + else + mp.log("info", "Disabled shim menu mouse events.") + mp.remove_key_binding("shim_mouse_click_handler") + mp.remove_key_binding("shim_mouse_move_handler") + end + end +end + +mp.add_key_binding("MOUSE_MOVE", "shim_mouse_move_handler", mouse_handler) +mp.register_event("client-message", client_message_handler) diff --git a/plex_mpv_shim/player.py b/plex_mpv_shim/player.py index cc23c0f..f095ed1 100644 --- a/plex_mpv_shim/player.py +++ b/plex_mpv_shim/player.py @@ -9,7 +9,7 @@ from collections import OrderedDict from . import conffile -from .utils import synchronous, Timer +from .utils import synchronous, Timer, get_resource from .conf import settings from .menu import OSDMenu from .media import MediaType @@ -100,6 +100,12 @@ def __init__(self): } ) + if settings.menu_mouse: + if is_using_ext_mpv: + mpv_options["script"] = get_resource("mouse.lua") + else: + mpv_options["scripts"] = get_resource("mouse.lua") + if not (settings.mpv_ext and settings.mpv_ext_no_ovr): mpv_options["include"] = conffile.get(APP_NAME, "mpv.conf", True) mpv_options["input_conf"] = conffile.get(APP_NAME, "input.conf", True) @@ -242,6 +248,33 @@ def handle_end_idle(_name, value): has_lock = self._finished_lock.acquire(False) self.put_task(self.finished_callback, has_lock) + @self._player.event_callback('client-message') + def handle_client_message(event): + try: + # Python-MPV 1.0 uses a class/struct combination now + if hasattr(event, "as_dict"): + event = event.as_dict() + if 'event' in event: + event['event'] = event['event'].decode('utf-8') + if 'args' in event: + event['args'] = [d.decode('utf-8') for d in event['args']] + + if "event_id" in event: + args = event["event"]["args"] + else: + args = event["args"] + if len(args) == 0: + return + if args[0] == "shim-menu-select": + # Apparently this can happen... + if args[1] == "inf": + return + self.menu.mouse_select(int(args[1])) + elif args[0] == "shim-menu-click": + self.menu.menu_action("ok") + except Exception: + log.warning("Error when processing client-message.", exc_info=True) + # Put a task to the event queue. # This ensures the task executes outside # of an event handler, which causes a crash.