update
This commit is contained in:
@@ -59,6 +59,16 @@ npm run setup:mpv-handler -- --skip-config
|
||||
npm run setup:mpv-handler -- --dry-run
|
||||
```
|
||||
|
||||
## Reuse existing mpv window (playlist append)
|
||||
|
||||
Desktop links now include an `enqueue=append` hint. For the bundled Linux Python fallback handler, this can append into an already-running mpv instance when you set an IPC socket in `config.toml`:
|
||||
|
||||
```toml
|
||||
ipc_socket = "/tmp/mpv-handler.sock"
|
||||
```
|
||||
|
||||
With this set, the first launch starts mpv with that IPC socket and later launches can add new URLs to the current playlist instead of opening a new window.
|
||||
|
||||
`--root` points at the extracted upstream `mpv-handler` folder. On Windows it is optional because this repo already ships the handler files under `scripts/`. On Linux it is normally required.
|
||||
|
||||
## What the helper does
|
||||
|
||||
Binary file not shown.
@@ -13,6 +13,11 @@
|
||||
# Optional, Type: String
|
||||
# HTTP(S) proxy server address
|
||||
|
||||
#ipc_socket = "/tmp/mpv-handler.sock"
|
||||
# Optional, Type: String
|
||||
# mpv IPC socket path used for appending new URLs to an already-running mpv instance.
|
||||
# If set, API Media Player desktop links can enqueue into the existing player instead of opening a new window.
|
||||
|
||||
# For Windows users:
|
||||
# - The path can be "C:\\folder\\some.exe" or "C:/folder/some.exe"
|
||||
# - The path is an executable binary file, not a directory
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -33,15 +35,57 @@ def resolve_mpv_command(config_path: Path) -> list[str]:
|
||||
return ['mpv']
|
||||
|
||||
|
||||
def get_ipc_socket_path(config_path: Path) -> str:
|
||||
configured_socket = read_config_value(config_path, 'ipc_socket')
|
||||
if configured_socket:
|
||||
return configured_socket
|
||||
|
||||
return os.environ.get('MPV_HANDLER_IPC_SOCKET', '').strip()
|
||||
|
||||
|
||||
def try_enqueue_via_ipc(ipc_socket_path: str, target_url: str, title: str) -> bool:
|
||||
if not ipc_socket_path:
|
||||
return False
|
||||
|
||||
socket_path = Path(ipc_socket_path).expanduser()
|
||||
if not socket_path.exists():
|
||||
return False
|
||||
|
||||
# Try with per-entry title first, then fallback to plain append if unsupported.
|
||||
commands = [
|
||||
{'command': ['loadfile', target_url, 'append-play', f'force-media-title={title}']} if title else {'command': ['loadfile', target_url, 'append-play']},
|
||||
{'command': ['loadfile', target_url, 'append-play']},
|
||||
]
|
||||
|
||||
for command in commands:
|
||||
try:
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
|
||||
client.settimeout(0.4)
|
||||
client.connect(str(socket_path))
|
||||
client.sendall((json.dumps(command) + '\n').encode('utf-8'))
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def build_mpv_command(target_url: str, title: str, config_path: Path) -> list[str]:
|
||||
command = resolve_mpv_command(config_path)
|
||||
ipc_socket_path = get_ipc_socket_path(config_path)
|
||||
|
||||
if ipc_socket_path:
|
||||
expanded_socket = str(Path(ipc_socket_path).expanduser())
|
||||
# Ensure new mpv instances expose the same IPC socket for future append requests.
|
||||
command.append(f'--input-ipc-server={expanded_socket}')
|
||||
|
||||
if title:
|
||||
command.append(f'--force-media-title={title}')
|
||||
command.append(target_url)
|
||||
return command
|
||||
|
||||
|
||||
def parse_protocol_url(raw_url: str) -> tuple[str, str]:
|
||||
def parse_protocol_url(raw_url: str) -> tuple[str, str, str]:
|
||||
parsed = urlparse(raw_url)
|
||||
if parsed.scheme not in {'mpv-handler', 'mpv-handler-debug'}:
|
||||
raise ValueError(f'Unsupported scheme: {parsed.scheme}')
|
||||
@@ -57,7 +101,8 @@ def parse_protocol_url(raw_url: str) -> tuple[str, str]:
|
||||
title = ''
|
||||
if query.get('v_title'):
|
||||
title = decode_urlsafe_base64(query['v_title'][0])
|
||||
return target_url, title
|
||||
enqueue_mode = (query.get('enqueue') or [''])[0].strip().lower()
|
||||
return target_url, title, enqueue_mode
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -66,7 +111,7 @@ def main() -> int:
|
||||
return 1
|
||||
|
||||
try:
|
||||
target_url, title = parse_protocol_url(sys.argv[1])
|
||||
target_url, title, enqueue_mode = parse_protocol_url(sys.argv[1])
|
||||
except Exception as error:
|
||||
print(f'Failed to parse mpv-handler URL: {error}', file=sys.stderr)
|
||||
return 1
|
||||
@@ -74,6 +119,12 @@ def main() -> int:
|
||||
config_home = os.environ.get('XDG_CONFIG_HOME', '').strip()
|
||||
config_dir = Path(config_home) / 'mpv-handler' if config_home else Path.home() / '.config' / 'mpv-handler'
|
||||
config_path = config_dir / 'config.toml'
|
||||
|
||||
if enqueue_mode == 'append':
|
||||
ipc_socket_path = get_ipc_socket_path(config_path)
|
||||
if try_enqueue_via_ipc(ipc_socket_path, target_url, title):
|
||||
return 0
|
||||
|
||||
command = build_mpv_command(target_url, title, config_path)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user