update README.md and add linux-mpv-handler scripts

This commit is contained in:
2026-06-15 11:46:34 -07:00
parent 74ef995da0
commit 92b7c6f06e
6 changed files with 143 additions and 7 deletions
+2 -2
View File
@@ -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 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: 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 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 ### Android
+3 -2
View File
@@ -35,7 +35,7 @@ npm run uninstall:mpv-handler
https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip 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: 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. 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 ## Useful flags
@@ -74,6 +74,7 @@ npm run setup:mpv-handler -- --dry-run
- validates the extracted upstream release layout - 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 - 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` - creates or updates `config.toml`
- copies the handler binary to `~/.local/bin/mpv-handler` - copies the handler binary to `~/.local/bin/mpv-handler`
- copies the desktop entries to `~/.local/share/applications` - copies the desktop entries to `~/.local/share/applications`
+7
View File
@@ -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
+7
View File
@@ -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
+89
View File
@@ -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())
+35 -3
View File
@@ -9,6 +9,9 @@ import { fileURLToPath } from 'node:url'
const scriptPath = fileURLToPath(import.meta.url) const scriptPath = fileURLToPath(import.meta.url)
const scriptDir = path.dirname(scriptPath) const scriptDir = path.dirname(scriptPath)
const templateConfigPath = path.join(scriptDir, 'config.toml') 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_REPO = 'https://github.com/akiirui/mpv-handler.git'
const UPSTREAM_MPV_HANDLER_REF = 'v0.4.2' const UPSTREAM_MPV_HANDLER_REF = 'v0.4.2'
@@ -216,6 +219,27 @@ function prepareLinuxSourceBuildRoot(options) {
return stageDir 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) { function resolveRoot(options) {
const resolvedRoot = findRoot(options) const resolvedRoot = findRoot(options)
if (resolvedRoot) return resolvedRoot if (resolvedRoot) return resolvedRoot
@@ -225,13 +249,21 @@ function resolveRoot(options) {
} }
if (process.platform === 'linux') { if (process.platform === 'linux') {
if (process.arch !== 'x64' && !options.skipSourceBuild) { if (process.arch !== 'x64') {
return prepareLinuxSourceBuildRoot(options) 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' const archHint = process.arch === 'x64'
? 'Download and extract the upstream archive, then pass --root /path/to/extracted/mpv-handler-linux-amd64.' ? '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}`) fail(`Could not find a Linux mpv-handler release. ${archHint}`)
} }