From 92b7c6f06e8bcb29b6f7645481ffae91f3d84488 Mon Sep 17 00:00:00 2001 From: Nose Date: Mon, 15 Jun 2026 11:46:34 -0700 Subject: [PATCH] update README.md and add linux-mpv-handler scripts --- README.md | 4 +- scripts/README.md | 5 +- scripts/linux-mpv-handler-debug.desktop | 7 ++ scripts/linux-mpv-handler.desktop | 7 ++ scripts/linux-mpv-handler.py | 89 +++++++++++++++++++++++++ scripts/setup-mpv-handler.mjs | 38 ++++++++++- 6 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 scripts/linux-mpv-handler-debug.desktop create mode 100644 scripts/linux-mpv-handler.desktop create mode 100644 scripts/linux-mpv-handler.py diff --git a/README.md b/README.md index a202dff..9cbaf31 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Run this on the Linux desktop client that should receive `mpv-handler://` links, https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip ``` -The upstream project currently publishes an official Linux release for `amd64` only. On Raspberry Pi or other ARM Linux systems, `npm run setup:mpv-handler` now tries to build a compatible binary automatically from upstream source if both `git` and Rust `cargo` are installed. If you prefer not to build locally, pass `--skip-source-build` and provide `--root` with a compatible extracted folder instead. +The upstream project currently publishes an official Linux release for `amd64` only. On Raspberry Pi or other ARM Linux systems, `npm run setup:mpv-handler` now tries to build a compatible binary automatically from upstream source if both `git` and Rust `cargo` are installed. If they are not available, it falls back to a bundled Python-based Linux protocol handler that supports this app's `mpv-handler://play/...` URLs without Rust. 3. Run the helper against the extracted folder: @@ -92,7 +92,7 @@ The upstream project currently publishes an official Linux release for `amd64` o npm run setup:mpv-handler -- --root /path/to/extracted/mpv-handler-linux-amd64 ``` -On Linux the helper copies the binary and desktop files into `~/.local`, writes `config.toml` to `~/.config/mpv-handler/config.toml` (or `$XDG_CONFIG_HOME/mpv-handler/config.toml`), and runs `xdg-mime` for both protocol handlers. On non-`x64` Linux it also auto-builds a staged `mpv-handler` root under `~/.cache/api-mediaplayer/` before installing, unless you pass `--skip-source-build`. +On Linux the helper copies the binary and desktop files into `~/.local`, writes `config.toml` to `~/.config/mpv-handler/config.toml` (or `$XDG_CONFIG_HOME/mpv-handler/config.toml`), and runs `xdg-mime` for both protocol handlers. On non-`x64` Linux it first prefers an automatic upstream source build under `~/.cache/api-mediaplayer/` when `git` and `cargo` are available, and otherwise falls back to the bundled Python handler. ### Android diff --git a/scripts/README.md b/scripts/README.md index 4d960b4..91dfffd 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -35,7 +35,7 @@ npm run uninstall:mpv-handler https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip ``` -The official upstream Linux release is currently `amd64` only. On Raspberry Pi or other ARM Linux systems, `npm run setup:mpv-handler` now tries to build a compatible `mpv-handler` binary automatically from upstream source if both `git` and Rust `cargo` are installed. If you do not want that behavior, pass `--skip-source-build` and provide `--root` yourself. +The official upstream Linux release is currently `amd64` only. On Raspberry Pi or other ARM Linux systems, `npm run setup:mpv-handler` now tries to build a compatible `mpv-handler` binary automatically from upstream source if both `git` and Rust `cargo` are installed. If they are not available, the helper falls back to a bundled Python-based handler that supports this app's `mpv-handler://play/...` URLs without Rust. 2. Point the helper at that extracted folder: @@ -45,7 +45,7 @@ npm run setup:mpv-handler -- --root /path/to/extracted/mpv-handler-linux-amd64 Run this on the Linux desktop client that should handle `mpv-handler://`, not on the machine that is only serving the web app. -The helper copies files into `~/.local/bin` and `~/.local/share/applications`, writes config to `~/.config/mpv-handler/config.toml` (or `$XDG_CONFIG_HOME/mpv-handler/config.toml`), and runs `xdg-mime` for both protocol handlers. On non-`x64` Linux it will first build and stage upstream source into `~/.cache/api-mediaplayer/` unless you pass `--skip-source-build`. +The helper copies files into `~/.local/bin` and `~/.local/share/applications`, writes config to `~/.config/mpv-handler/config.toml` (or `$XDG_CONFIG_HOME/mpv-handler/config.toml`), and runs `xdg-mime` for both protocol handlers. On non-`x64` Linux it first tries to build and stage upstream source into `~/.cache/api-mediaplayer/`, and if `git` or `cargo` are unavailable it stages the bundled Python handler there instead. ## Useful flags @@ -74,6 +74,7 @@ npm run setup:mpv-handler -- --dry-run - validates the extracted upstream release layout - auto-builds upstream source on non-`x64` Linux when no compatible local root is available and `git` plus `cargo` are installed +- falls back to a bundled Python `mpv-handler` implementation on non-`x64` Linux when source-build tooling is unavailable - creates or updates `config.toml` - copies the handler binary to `~/.local/bin/mpv-handler` - copies the desktop entries to `~/.local/share/applications` diff --git a/scripts/linux-mpv-handler-debug.desktop b/scripts/linux-mpv-handler-debug.desktop new file mode 100644 index 0000000..e3c6e59 --- /dev/null +++ b/scripts/linux-mpv-handler-debug.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=mpv-handler-debug +Exec=__MPV_HANDLER_EXEC__ %u +Type=Application +Terminal=true +MimeType=x-scheme-handler/mpv-handler-debug; +NoDisplay=true \ No newline at end of file diff --git a/scripts/linux-mpv-handler.desktop b/scripts/linux-mpv-handler.desktop new file mode 100644 index 0000000..09e027b --- /dev/null +++ b/scripts/linux-mpv-handler.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=mpv-handler +Exec=__MPV_HANDLER_EXEC__ %u +Type=Application +Terminal=false +MimeType=x-scheme-handler/mpv-handler; +NoDisplay=true \ No newline at end of file diff --git a/scripts/linux-mpv-handler.py b/scripts/linux-mpv-handler.py new file mode 100644 index 0000000..9fcdf22 --- /dev/null +++ b/scripts/linux-mpv-handler.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import base64 +import os +import re +import subprocess +import sys +from pathlib import Path +from urllib.parse import parse_qs, urlparse + + +def decode_urlsafe_base64(value: str) -> str: + padding = '=' * (-len(value) % 4) + return base64.urlsafe_b64decode(f'{value}{padding}').decode('utf-8') + + +def read_config_value(config_path: Path, key: str) -> str: + if not config_path.is_file(): + return '' + + pattern = re.compile(rf'^\s*#?\s*{re.escape(key)}\s*=\s*"([^"]*)"\s*$') + for line in config_path.read_text(encoding='utf-8').splitlines(): + match = pattern.match(line) + if match: + return match.group(1).strip() + return '' + + +def resolve_mpv_command(config_path: Path) -> list[str]: + configured_mpv = read_config_value(config_path, 'mpv') + if configured_mpv: + return [configured_mpv] + return ['mpv'] + + +def build_mpv_command(target_url: str, title: str, config_path: Path) -> list[str]: + command = resolve_mpv_command(config_path) + 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]: + parsed = urlparse(raw_url) + if parsed.scheme not in {'mpv-handler', 'mpv-handler-debug'}: + raise ValueError(f'Unsupported scheme: {parsed.scheme}') + + path_segments = [segment for segment in parsed.path.split('/') if segment] + if not path_segments or path_segments[0] != 'play': + raise ValueError('Unsupported mpv-handler plugin') + if len(path_segments) < 2: + raise ValueError('Missing encoded media URL') + + target_url = decode_urlsafe_base64(path_segments[1]) + query = parse_qs(parsed.query) + title = '' + if query.get('v_title'): + title = decode_urlsafe_base64(query['v_title'][0]) + return target_url, title + + +def main() -> int: + if len(sys.argv) < 2: + print('Expected protocol URL argument', file=sys.stderr) + return 1 + + try: + target_url, title = parse_protocol_url(sys.argv[1]) + except Exception as error: + print(f'Failed to parse mpv-handler URL: {error}', file=sys.stderr) + return 1 + + 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' + command = build_mpv_command(target_url, title, config_path) + + try: + subprocess.Popen(command) + except Exception as error: + print(f'Failed to launch mpv: {error}', file=sys.stderr) + return 1 + + return 0 + + +if __name__ == '__main__': + raise SystemExit(main()) \ No newline at end of file diff --git a/scripts/setup-mpv-handler.mjs b/scripts/setup-mpv-handler.mjs index 297f918..9756cf7 100644 --- a/scripts/setup-mpv-handler.mjs +++ b/scripts/setup-mpv-handler.mjs @@ -9,6 +9,9 @@ import { fileURLToPath } from 'node:url' const scriptPath = fileURLToPath(import.meta.url) const scriptDir = path.dirname(scriptPath) const templateConfigPath = path.join(scriptDir, 'config.toml') +const bundledLinuxHandlerPath = path.join(scriptDir, 'linux-mpv-handler.py') +const bundledLinuxDesktopPath = path.join(scriptDir, 'linux-mpv-handler.desktop') +const bundledLinuxDebugDesktopPath = path.join(scriptDir, 'linux-mpv-handler-debug.desktop') const UPSTREAM_MPV_HANDLER_REPO = 'https://github.com/akiirui/mpv-handler.git' const UPSTREAM_MPV_HANDLER_REF = 'v0.4.2' @@ -216,6 +219,27 @@ function prepareLinuxSourceBuildRoot(options) { return stageDir } +function prepareBundledLinuxHandlerRoot(options) { + const cacheDir = getLinuxCacheDir() + const stageDir = path.join(cacheDir, 'linux-bundled-handler') + + if (options.dryRun) { + console.log(`Dry run: would stage bundled Linux handler into ${stageDir}`) + return stageDir + } + + mkdirSync(cacheDir, { recursive: true }) + mkdirSync(stageDir, { recursive: true }) + + copyFileSync(bundledLinuxHandlerPath, path.join(stageDir, 'mpv-handler')) + copyFileSync(bundledLinuxDesktopPath, path.join(stageDir, 'mpv-handler.desktop')) + copyFileSync(bundledLinuxDebugDesktopPath, path.join(stageDir, 'mpv-handler-debug.desktop')) + copyFileSync(templateConfigPath, path.join(stageDir, 'config.toml')) + chmodSync(path.join(stageDir, 'mpv-handler'), 0o755) + + return stageDir +} + function resolveRoot(options) { const resolvedRoot = findRoot(options) if (resolvedRoot) return resolvedRoot @@ -225,13 +249,21 @@ function resolveRoot(options) { } if (process.platform === 'linux') { - if (process.arch !== 'x64' && !options.skipSourceBuild) { - return prepareLinuxSourceBuildRoot(options) + if (process.arch !== 'x64') { + if (!options.skipSourceBuild) { + const gitPath = resolveOnPath(['git']) + const cargoPath = resolveOnPath(['cargo']) + if (gitPath && cargoPath) { + return prepareLinuxSourceBuildRoot(options) + } + } + + return prepareBundledLinuxHandlerRoot(options) } const archHint = process.arch === 'x64' ? 'Download and extract the upstream archive, then pass --root /path/to/extracted/mpv-handler-linux-amd64.' - : `The upstream project only publishes an official linux-amd64 archive. Install git and cargo to allow automatic source builds, or build/obtain a compatible mpv-handler binary and pass --root /path/to/extracted/mpv-handler.` + : `The upstream project only publishes an official linux-amd64 archive. Install git and cargo to allow automatic source builds, use the bundled fallback handler, or build/obtain a compatible mpv-handler binary and pass --root /path/to/extracted/mpv-handler.` fail(`Could not find a Linux mpv-handler release. ${archHint}`) }