From 3bd335cfb538135dafd67be8abded4900789989e Mon Sep 17 00:00:00 2001 From: Nose Date: Sat, 18 Apr 2026 18:12:17 -0700 Subject: [PATCH] update installation instructions and add mpv handler setup script --- .env.example | 2 +- README.md | 219 +++++------ package.json | 5 + scripts/README.md | 607 +++++++++++++++++++++++------- scripts/handler-install.bat | 93 +---- scripts/handler-uninstall.bat | 75 +--- scripts/install-mpv-handler.ps1 | 30 +- scripts/setup-mpv-handler.mjs | 332 ++++++++++++++++ scripts/uninstall-mpv-handler.ps1 | 32 ++ 9 files changed, 982 insertions(+), 413 deletions(-) create mode 100644 scripts/setup-mpv-handler.mjs create mode 100644 scripts/uninstall-mpv-handler.ps1 diff --git a/.env.example b/.env.example index 13aadf1..4fbeb24 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ VITE_HYDRUS_HOST=http://localhost VITE_HYDRUS_PORT=45869 -VITE_HYDRUS_API_KEY=95178b08e6ba3c57991e7b4e162c6efff1ce90c500005c6ebf8524122ed2486e +VITE_HYDRUS_API_KEY= VITE_HYDRUS_SSL=false diff --git a/README.md b/README.md index 66feee4..f829114 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,107 @@ -# API Media Player (PWA) +# API Media Player -This is a small web-first PWA prototype for browsing media from a Hydrus client via its Client API. The app ships with a demo library and routes playback to external players such as mpv or VLC instead of using an in-browser player. +API Media Player is a web-first PWA for browsing Hydrus media and handing playback off to native apps instead of an in-browser player. -## Quick start (PowerShell) +## What works today -1. Install dependencies: +- Browse the seeded demo library right after `npm install` and `npm run dev` +- Connect to a real Hydrus client from the Settings page or a local `.env` file +- Launch playback in native apps: + - Windows and Linux desktop: `mpv` through `mpv-handler://` + - Android: `mpv-android` through `intent://` + - iPhone and iPad: VLC through `vlc-x-callback://` -```powershell -pwsh -c "npm install" +## Fast start + +1. Install Node.js 20 or newer. +2. Install dependencies: + +```bash +npm install ``` -2. Run the dev server: +3. Start the dev server: -```powershell -pwsh -c "npm run dev" +```bash +npm run dev ``` -Open `http://localhost:5173` in your browser to test the PWA and external-player launch flow. +4. Open `http://localhost:5173`. -## Configuring Hydrus +The app includes sample data, so you can verify the UI immediately. Playback itself is always external, so browsing works before player setup, but actual media launch depends on the platform-specific player flow below. -Create a `.env` file in the project root (not committed) with these variables if you want to connect to a real Hydrus instance: +## Connect Hydrus (optional) -``` +If you want real library data, duplicate `.env.example` as `.env` and fill in your Hydrus values: + +```dotenv VITE_HYDRUS_HOST=http://localhost VITE_HYDRUS_PORT=45869 VITE_HYDRUS_API_KEY= VITE_HYDRUS_SSL=false ``` -> Note: browsers cannot attach custom Hydrus API headers to direct media URLs. If your Hydrus server requires header-based authentication for file access, use a reverse proxy or another trusted layer that can mint playable URLs for your external player. +You can also add or edit servers from Settings inside the app. The first run seeds a sample server entry so you only need to supply your `Hydrus-Client-API-Access-Key` and test the connection. -### Settings UI & quick test +Browsers cannot attach custom Hydrus API headers to direct media URLs. If your Hydrus setup requires header-based authentication for file access, put a trusted reverse proxy in front of it or provide playable URLs another way. -On first run the app will seed a sample server entry for `192.168.1.128:45869` so you can quickly add your API key and test connectivity via the app's Settings (top-right gear). Open Settings, choose the server, paste your `Hydrus-Client-API-Access-Key` (if needed), and click **Test connection**. The test reports whether the server is reachable, whether authentication is required (HTTP 401/403), and whether byte-range requests are supported (needed for seeking). +## Playback setup by device -If you get a CORS or network error in the browser, consider running a reverse proxy that adds proper CORS headers or packaging the app with Capacitor to avoid browser CORS limitations. +### Windows desktop -## Violentmonkey mpv userscript +1. Install `mpv` and optionally `yt-dlp`. +2. Open an elevated PowerShell or Windows Terminal. +3. From the repo root, run: -If you want desktop or Android browsers to hand media straight to mpv instead of the in-browser player, install the userscript at: +```bash +npm run setup:mpv-handler +``` + +The repo already includes the Windows `mpv-handler` binaries under `scripts/`, so the helper script can: + +- reuse the bundled handler files +- detect `mpv` and `yt-dlp` from `PATH` when possible +- update `scripts/config.toml` +- register `mpv-handler://` and `mpv-handler-debug://` + +To remove the registration later: + +```bash +npm run uninstall:mpv-handler +``` + +### Linux desktop + +1. Install `mpv` and optionally `yt-dlp`. +2. Download and extract the latest upstream Linux release: + +```text +https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip +``` + +3. Run the helper against the extracted folder: + +```bash +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`, and runs `xdg-mime` for both protocol handlers. + +### Android + +Install `mpv-android` (`is.xyz.mpv`). No extra handler setup is needed. + +### iPhone / iPad + +Install VLC for iOS. The app sends playback to `vlc-x-callback://` automatically. + +### macOS desktop + +The app can still browse Hydrus and demo content on macOS, but this repo does not currently automate `mpv-handler://` registration for desktop macOS. If you already have a compatible custom protocol handler installed, the desktop playback flow will use it. Otherwise, use another supported playback platform for now. + +## Optional userscript for direct media URLs + +If you want direct file loads in the browser to jump into `mpv` before the page player starts, install the userscript served by this app: ```text /userscripts/api-media-player-open-in-mpv.user.js @@ -52,117 +114,18 @@ http://localhost:5173/userscripts/api-media-player-open-in-mpv.user.js http://127.0.0.1:4173/userscripts/api-media-player-open-in-mpv.user.js ``` -What it does: +It only activates on `localhost`, loopback, RFC1918 LAN IPs, and `.local` or `.lan` hosts. On desktop it redirects to `mpv-handler://...`; on Android it redirects to the `mpv` app through `intent://...`. -- On desktop, direct media playback is redirected to `mpv-handler://...` -- On Android, direct media playback is redirected to the mpv app via `intent://...` -- It only activates by default on `localhost`, loopback, RFC1918 LAN IPs, and `.local` / `.lan` hosts so it does not hijack unrelated public websites +## Useful commands -Notes: - -- Desktop requires `mpv-handler` to be installed and registered. -- Android requires `mpv-android` (`is.xyz.mpv`) to be installed. -- The script intercepts direct file URLs and Hydrus `/get_files/file` playback requests before the browser player starts. - -### Desktop setup for mpv-handler - -Official project: - -```text -https://github.com/akiirui/mpv-handler +```bash +npm run dev +npm run build +npm run preview +npm run typecheck +npm run setup:mpv-handler -- --help ``` -Latest releases: +## More detail -```text -https://github.com/akiirui/mpv-handler/releases -``` - -Windows: - -1. Install `mpv` itself first if you do not already have it. -2. Download the latest Windows archive: - -```text -https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-windows-amd64.zip -``` - -3. Extract it somewhere permanent. -4. Edit `config.toml` in that folder and set the path to your `mpv` executable. If you use `yt-dlp`, set that path too. -5. Register the protocol with either the upstream batch file or the PowerShell installer in this repo. - -Upstream batch option: - -```powershell -Set-Location C:\path\to\mpv-handler -.\handler-install.bat -``` - -PowerShell alternative from this repo: - -```powershell -Set-Location C:\Forgejo\API-MediaPlayer -powershell -ExecutionPolicy Bypass -File .\scripts\install-mpv-handler.ps1 -InstallRoot 'C:\path\to\mpv-handler' -``` - -Or from an already elevated PowerShell window: - -```powershell -& 'C:\Forgejo\API-MediaPlayer\scripts\install-mpv-handler.ps1' -InstallRoot 'C:\path\to\mpv-handler' -``` - -If you copied `config.toml`, `mpv-handler.exe`, and `mpv-handler-debug.exe` into the same folder as [scripts/install-mpv-handler.ps1](scripts/install-mpv-handler.ps1), you can also run it without `-InstallRoot`: - -```powershell -& 'C:\Forgejo\API-MediaPlayer\scripts\install-mpv-handler.ps1' -``` - -What the PowerShell installer does: - -- validates that `config.toml`, `mpv-handler.exe`, and `mpv-handler-debug.exe` exist -- removes old `mpv://` and existing `mpv-handler://` protocol keys unless you tell it not to -- registers `mpv-handler://` and `mpv-handler-debug://` in the Windows registry -- uses the `mpv` path from `config.toml` as the icon when possible, otherwise falls back to `mpv-handler.exe` - -Requirements for the PowerShell installer: - -- run it from an elevated PowerShell window -- point `-InstallRoot` at the extracted `mpv-handler` folder -- do not dot-source it from the repo root without `-InstallRoot`, because this repo does not contain the extracted `mpv-handler` binaries - -Linux: - -1. Download the latest Linux archive: - -```text -https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip -``` - -2. Extract it. -3. Copy `mpv-handler` to `$HOME/.local/bin`. -4. Copy `mpv-handler.desktop` and `mpv-handler-debug.desktop` to `$HOME/.local/share/applications/`. -5. Make the binary executable: - -```text -chmod +x $HOME/.local/bin/mpv-handler -``` - -6. Register the protocol handlers: - -```text -xdg-mime default mpv-handler.desktop x-scheme-handler/mpv-handler -xdg-mime default mpv-handler-debug.desktop x-scheme-handler/mpv-handler-debug -``` - -7. Add `$HOME/.local/bin` to `PATH` if needed. -8. Optionally copy and edit `config.toml` for your `mpv` and `yt-dlp` paths. - -After setup, clicking an `mpv-handler://...` link in the browser should launch mpv instead of showing an unknown protocol error. - -## Next steps - -- Wire the UI to a real Hydrus instance (update `src/api/hydrusClient.ts`). -- Add Capacitor for native packaging and secure storage for API keys. -- Improve Hydrus browsing and filtering UX for large libraries. - -If you want, I can run `npm install` and start the dev server now and confirm the app is reachable locally. Let me know and I'll proceed. +See `scripts/README.md` for the helper script behavior, flags, and the Windows/Linux manual fallbacks. diff --git a/package.json b/package.json index f915f13..6404012 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,13 @@ "description": "Web-first PWA media player that integrates with Hydrus", "scripts": { "dev": "vite", + "start": "vite", "build": "vite build", "preview": "vite preview", + "typecheck": "tsc --noEmit", + "setup:mpv-handler": "node ./scripts/setup-mpv-handler.mjs", + "install:mpv-handler": "npm run setup:mpv-handler", + "uninstall:mpv-handler": "powershell -NoProfile -ExecutionPolicy Bypass -File .\\scripts\\uninstall-mpv-handler.ps1", "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"" }, "dependencies": { diff --git a/scripts/README.md b/scripts/README.md index 829871e..074d8b4 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,155 +1,472 @@ -[English][readme-en] | [简体中文][readme-zh-hans] | [繁体中文][readme-zh-hant] +# mpv-handler setup -[readme-en]: https://github.com/akiirui/mpv-handler/blob/main/README.md -[readme-zh-hans]: https://github.com/akiirui/mpv-handler/blob/main/README.zh-Hans.md -[readme-zh-hant]: https://github.com/akiirui/mpv-handler/blob/main/README.zh-Hant.md +This folder contains the desktop playback helper assets used by API Media Player. -# mpv handler +## What is in this folder -A protocol handler for **mpv**, written by Rust. +- `setup-mpv-handler.mjs`: cross-platform helper used by `npm run setup:mpv-handler` +- `install-mpv-handler.ps1`: Windows protocol registration script +- `uninstall-mpv-handler.ps1`: Windows protocol cleanup script +- `handler-install.bat` and `handler-uninstall.bat`: thin Windows wrappers for the PowerShell scripts +- `config.toml`: template used when the helper needs to create or refresh an `mpv-handler` config +- `mpv-handler.exe` and `mpv-handler-debug.exe`: bundled Windows handler binaries -Use **mpv** and **yt-dlp** to play video and music from the websites. - -Please use it with userscript: - -[![play-with-mpv][badges-play-with-mpv]][greasyfork-play-with-mpv] - -## Breaking changes - -### [v0.4.0][v0.4.0] - -To avoid conflicts with the `mpv://` protocol provided by mpv. - -> mpv://... -> -> mpv protocol. This is used for starting mpv from URL handler. The protocol is stripped and the rest is passed to the player as a normal open argument. Only safe network protocols are allowed to be opened this way. - -Scheme `mpv://` and `mpv-debug://` are deprecated, use `mpv-handler://` and `mpv-handler-debug://`. - -**Require manual intervention** - -#### Windows - -Run `handler-uninstall.bat` to uninstall deprecated protocol, and run `handler-install.bat` to install new procotol. - -#### Linux - -If you installed manually, please repeat the manual installation process. - -## Protocol - -![](share/proto.png) - -### Scheme - -- `mpv-handler`: Run mpv-handler without console window -- `mpv-handler-debug`: Run mpv-handler with console window to view outputs and errors - -### Plugins - -- `play`: Use mpv player to play video - -### Encoded Data - -Use [URL-safe base64][rfc-base64-url] to encode the URL or TITLE. - -Replace `/` to `_`, `+` to `-` and remove padding `=`. - -Example (JavaScript): - -```javascript -let data = btoa("https://www.youtube.com/watch?v=Ggkn2f5e-IU"); -let safe = data.replace(/\//g, "_").replace(/\+/g, "-").replace(/\=/g, ""); -``` - -### Parameters (Optional) - -``` -cookies = [ www.domain.com.txt ] -profile = [ default, low-latency, etc... ] -quality = [ 2160p, 1440p, 1080p, 720p, 480p, 360p ] -v_codec = [ av01, vp9, h265, h264 ] -v_title = [ Encoded Title ] -subfile = [ Encoded URL ] -startat = [ Seconds (float) ] -referrer = [ Encoded URL ] -``` - -## Installation - -### Linux - -#### Arch Linux - -[![mpv-handler][badges-aur]][download-aur] -[![mpv-handler-git][badges-aur-git]][download-aur-git] - -#### Manual installation - -1. Download [latest Linux release][download-linux] -2. Unzip the archive -3. Copy `mpv-handler` to `$HOME/.local/bin` -4. Copy `mpv-handler.desktop` to `$HOME/.local/share/applications/` -5. Copy `mpv-handler-debug.desktop` to `$HOME/.local/share/applications/` -6. Set executable permission for binary - - - ``` - $ chmod +x $HOME/.local/bin/mpv-handler - ``` - -7. Register xdg-mime (thanks for the [linuxuprising][linuxuprising] reminder) - - - ``` - $ xdg-mime default mpv-handler.desktop x-scheme-handler/mpv-handler - $ xdg-mime default mpv-handler-debug.desktop x-scheme-handler/mpv-handler-debug - ``` - -8. Add `$HOME/.local/bin` to your environment variable `PATH` -9. **Optional**: _Copy `config.toml` to `$HOME/.config/mpv-handler/config.toml` and configure_ +## Recommended commands ### Windows -Windows users need to install manually. +Run from an elevated PowerShell or Windows Terminal: -#### Manual installation - -1. Download [latest Windows release][download-windows] -2. Unzip the archive to the directory you want -3. Run `handler-install.bat` to register protocol handler -4. Edit `config.toml` and set `mpv` and `ytdl` path - -## Configuration - -```toml -mpv = "/usr/bin/mpv" -# Optional, Type: String -# The path of mpv executable binary -# Default value: -# - Linux: mpv -# - Windows: mpv.com - -ytdl = "/usr/bin/yt-dlp" -# Optional, Type: String -# The path of yt-dlp executable binary - -proxy = "http://example.com:8080" -# Optional, Type: String -# HTTP(S) proxy server address - -# For Windows users: -# - The path can be "C:\\folder\\some.exe" or "C:/folder/some.exe" -# - The path target is an executable binary file, not a directory +```bash +npm run setup:mpv-handler ``` -[v0.4.0]: https://github.com/akiirui/mpv-handler/releases/tag/v0.4.0 -[rfc-base64-url]: https://datatracker.ietf.org/doc/html/rfc4648#section-5 -[badges-aur-git]: https://img.shields.io/aur/version/mpv-handler-git?style=for-the-badge&logo=archlinux&label=mpv-handler-git -[badges-aur]: https://img.shields.io/aur/version/mpv-handler?style=for-the-badge&logo=archlinux&label=mpv-handler -[badges-play-with-mpv]: https://img.shields.io/greasyfork/v/416271?style=for-the-badge&logo=greasyfork&label=play-with-mpv -[download-aur-git]: https://aur.archlinux.org/packages/mpv-handler-git/ -[download-aur]: https://aur.archlinux.org/packages/mpv-handler/ -[download-linux]: https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip -[download-macos]: https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-macos-amd64.zip -[download-windows]: https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-windows-amd64.zip -[greasyfork-play-with-mpv]: https://greasyfork.org/scripts/416271-play-with-mpv -[linuxuprising]: https://www.linuxuprising.com/2021/07/open-youtube-and-more-videos-from-your.html +Remove the registration later with: + +```bash +npm run uninstall:mpv-handler +``` + +### Linux + +1. Download and extract the upstream Linux release: + +```text +https://github.com/akiirui/mpv-handler/releases/latest/download/mpv-handler-linux-amd64.zip +``` + +2. Point the helper at that extracted folder: + +```bash +npm run setup:mpv-handler -- --root /path/to/extracted/mpv-handler-linux-amd64 +``` + +The helper copies files into `~/.local/bin` and `~/.local/share/applications`, updates `config.toml`, and runs `xdg-mime` for both protocol handlers. + +## Useful flags + +```bash +npm run setup:mpv-handler -- --help +npm run setup:mpv-handler -- --root /path/to/mpv-handler +npm run setup:mpv-handler -- --mpv /path/to/mpv +npm run setup:mpv-handler -- --ytdl /path/to/yt-dlp +npm run setup:mpv-handler -- --skip-config +npm run setup:mpv-handler -- --dry-run +``` + +`--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 + +### Windows + +- finds the bundled handler files in this folder unless you override `--root` +- creates or updates `config.toml` +- tries to detect `mpv` and `yt-dlp` from `PATH` +- registers `mpv-handler://` and `mpv-handler-debug://` through the PowerShell installer + +### Linux + +- validates the extracted upstream release layout +- creates or updates `config.toml` +- copies the handler binary to `~/.local/bin/mpv-handler` +- copies the desktop entries to `~/.local/share/applications` +- rewrites `Exec=` entries to the installed absolute binary path +- runs `xdg-mime default ...` for both schemes + +## Manual fallback + +If you would rather install without the helper: + +### Windows + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\install-mpv-handler.ps1 -InstallRoot .\scripts +``` + +### Linux + +```bash +cp /path/to/mpv-handler/mpv-handler ~/.local/bin/mpv-handler +cp /path/to/mpv-handler/mpv-handler.desktop ~/.local/share/applications/ +cp /path/to/mpv-handler/mpv-handler-debug.desktop ~/.local/share/applications/ +chmod +x ~/.local/bin/mpv-handler +xdg-mime default mpv-handler.desktop x-scheme-handler/mpv-handler +xdg-mime default mpv-handler-debug.desktop x-scheme-handler/mpv-handler-debug +``` + +## Upstream project + +Upstream `mpv-handler` releases and source: + +```text +https://github.com/akiirui/mpv-handler +https://github.com/akiirui/mpv-handler/releases +``` + +This repo uses the upstream protocol scheme and binaries; the docs here only describe the setup flow for API Media Player. +*** Add File: c:\Forgejo\API-MediaPlayer\scripts\uninstall-mpv-handler.ps1 +#Requires -Version 5.1 +#Requires -RunAsAdministrator + +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Remove-ProtocolKey { + param( + [Parameter(Mandatory = $true)] + [string]$SchemeName + ) + + $classesRoot = [Microsoft.Win32.Registry]::ClassesRoot + try { + $classesRoot.DeleteSubKeyTree($SchemeName, $false) + } catch { + } +} + +if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) { + throw 'This uninstaller is only for Windows.' +} + +Remove-ProtocolKey -SchemeName 'mpv' +Remove-ProtocolKey -SchemeName 'mpv-debug' +Remove-ProtocolKey -SchemeName 'mpv-handler' +Remove-ProtocolKey -SchemeName 'mpv-handler-debug' + +Write-Host 'Successfully removed mpv-handler protocol registration.' -ForegroundColor Green +*** Add File: c:\Forgejo\API-MediaPlayer\scripts\setup-mpv-handler.mjs +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process' +import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, chmodSync, writeFileSync } from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const scriptPath = fileURLToPath(import.meta.url) +const scriptDir = path.dirname(scriptPath) +const templateConfigPath = path.join(scriptDir, 'config.toml') + +const usage = `Usage: + npm run setup:mpv-handler + npm run setup:mpv-handler -- --root /path/to/mpv-handler + npm run setup:mpv-handler -- --mpv /path/to/mpv --ytdl /path/to/yt-dlp + +Options: + --root Path to the extracted mpv-handler folder. + --mpv Override the mpv executable path written to config.toml. + --ytdl Override the yt-dlp executable path written to config.toml. + --skip-config Do not create or update config.toml. + --keep-existing Windows only. Keep existing protocol keys instead of replacing them. + --dry-run Print the actions without changing files or running installers. + --help Show this help text. +` + +function fail(message) { + console.error(`Error: ${message}`) + process.exit(1) +} + +function parseArgs(argv) { + const options = { + root: '', + mpv: '', + ytdl: '', + skipConfig: false, + keepExisting: false, + dryRun: false, + help: false, + } + + for (let index = 0; index < argv.length; index += 1) { + const value = argv[index] + + if (value === '--help' || value === '-h') { + options.help = true + continue + } + + if (value === '--skip-config') { + options.skipConfig = true + continue + } + + if (value === '--keep-existing') { + options.keepExisting = true + continue + } + + if (value === '--dry-run') { + options.dryRun = true + continue + } + + if (value === '--root' || value === '--mpv' || value === '--ytdl') { + const nextValue = argv[index + 1] + if (!nextValue || nextValue.startsWith('--')) { + fail(`Missing value for ${value}`) + } + + if (value === '--root') options.root = nextValue + if (value === '--mpv') options.mpv = nextValue + if (value === '--ytdl') options.ytdl = nextValue + index += 1 + continue + } + + fail(`Unknown argument: ${value}`) + } + + return options +} + +function escapeTomlString(value) { + return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"') +} + +function upsertTomlValue(content, key, value) { + const line = `${key} = "${escapeTomlString(value)}"` + const pattern = new RegExp(`^\\s*#?\\s*${key}\\s*=.*$`, 'm') + if (pattern.test(content)) { + return content.replace(pattern, line) + } + + const trimmed = content.trimEnd() + return trimmed ? `${trimmed}\n${line}\n` : `${line}\n` +} + +function resolveOnPath(commandNames) { + const locator = process.platform === 'win32' ? 'where.exe' : 'which' + + for (const commandName of commandNames) { + const result = spawnSync(locator, [commandName], { encoding: 'utf8' }) + if (result.status !== 0) continue + + const match = result.stdout + .split(/\r?\n/) + .map((entry) => entry.trim()) + .find((entry) => entry) + + if (match && existsSync(match)) { + return match + } + } + + return '' +} + +function isFile(candidatePath) { + try { + return statSync(candidatePath).isFile() + } catch { + return false + } +} + +function looksLikeRoot(rootPath) { + const requiredFiles = process.platform === 'win32' + ? ['mpv-handler.exe', 'mpv-handler-debug.exe'] + : process.platform === 'linux' + ? ['mpv-handler', 'mpv-handler.desktop', 'mpv-handler-debug.desktop'] + : ['mpv-handler'] + + return requiredFiles.every((fileName) => isFile(path.join(rootPath, fileName))) +} + +function resolveRoot(options) { + const candidates = [] + if (options.root) candidates.push(path.resolve(options.root)) + candidates.push(scriptDir) + candidates.push(process.cwd()) + + const seen = new Set() + for (const candidate of candidates) { + if (!candidate || seen.has(candidate)) continue + seen.add(candidate) + if (looksLikeRoot(candidate)) { + return candidate + } + } + + if (process.platform === 'win32') { + fail('Could not find mpv-handler files. Re-run with --root pointing at the extracted mpv-handler folder.') + } + + if (process.platform === 'linux') { + fail('Could not find a Linux mpv-handler release. Download and extract the upstream archive, then pass --root /path/to/extracted/mpv-handler-linux-amd64.') + } + + fail('Automatic setup is not available for this platform yet.') +} + +function ensureConfig(rootPath, options) { + const configPath = path.join(rootPath, 'config.toml') + if (options.skipConfig) { + return { configPath, changed: false, mpvPath: '', ytdlPath: '' } + } + + let content = existsSync(configPath) + ? readFileSync(configPath, 'utf8') + : readFileSync(templateConfigPath, 'utf8') + + const detectedMpv = options.mpv || resolveOnPath(process.platform === 'win32' ? ['mpv.com', 'mpv.exe', 'mpv'] : ['mpv']) + const detectedYtdl = options.ytdl || resolveOnPath(process.platform === 'win32' ? ['yt-dlp.exe', 'yt-dlp'] : ['yt-dlp']) + + let changed = !existsSync(configPath) + let nextContent = content + + if (detectedMpv) { + const updated = upsertTomlValue(nextContent, 'mpv', detectedMpv) + changed ||= updated !== nextContent + nextContent = updated + } + + if (detectedYtdl) { + const updated = upsertTomlValue(nextContent, 'ytdl', detectedYtdl) + changed ||= updated !== nextContent + nextContent = updated + } + + if (changed && !options.dryRun) { + writeFileSync(configPath, nextContent, 'utf8') + } + + return { + configPath, + changed, + mpvPath: detectedMpv, + ytdlPath: detectedYtdl, + } +} + +function runWindowsSetup(rootPath, options) { + const powershell = resolveOnPath(['powershell.exe', 'pwsh.exe']) || 'powershell.exe' + const installScript = path.join(scriptDir, 'install-mpv-handler.ps1') + const args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', installScript, '-InstallRoot', rootPath] + + if (options.keepExisting) { + args.push('-KeepExistingProtocolKeys') + } + + if (options.dryRun) { + console.log('Dry run: would execute Windows installer') + console.log(`${powershell} ${args.map((value) => JSON.stringify(value)).join(' ')}`) + return + } + + const result = spawnSync(powershell, args, { stdio: 'inherit' }) + if (result.status !== 0) { + fail('Windows protocol registration failed. Re-run the command from an elevated PowerShell or Windows Terminal.') + } +} + +function rewriteDesktopExec(content, targetBinary) { + return content.replace(/^Exec=.*$/m, (line) => { + const prefix = 'Exec=' + const rest = line.slice(prefix.length).trim() + const firstSpaceIndex = rest.indexOf(' ') + const suffix = firstSpaceIndex === -1 ? '' : rest.slice(firstSpaceIndex) + return `${prefix}${targetBinary}${suffix}` + }) +} + +function runLinuxSetup(rootPath, options) { + const localBin = path.join(os.homedir(), '.local', 'bin') + const applicationsDir = path.join(os.homedir(), '.local', 'share', 'applications') + const targetBinary = path.join(localBin, 'mpv-handler') + const copies = [ + { + source: path.join(rootPath, 'mpv-handler'), + target: targetBinary, + executable: true, + }, + { + source: path.join(rootPath, 'mpv-handler.desktop'), + target: path.join(applicationsDir, 'mpv-handler.desktop'), + patchExec: true, + }, + { + source: path.join(rootPath, 'mpv-handler-debug.desktop'), + target: path.join(applicationsDir, 'mpv-handler-debug.desktop'), + patchExec: true, + }, + ] + + if (options.dryRun) { + console.log('Dry run: would install Linux desktop files to ~/.local') + for (const item of copies) { + console.log(`copy ${item.source} -> ${item.target}`) + } + console.log('xdg-mime default mpv-handler.desktop x-scheme-handler/mpv-handler') + console.log('xdg-mime default mpv-handler-debug.desktop x-scheme-handler/mpv-handler-debug') + return + } + + mkdirSync(localBin, { recursive: true }) + mkdirSync(applicationsDir, { recursive: true }) + + for (const item of copies) { + if (item.patchExec) { + const content = readFileSync(item.source, 'utf8') + writeFileSync(item.target, rewriteDesktopExec(content, targetBinary), 'utf8') + continue + } + + copyFileSync(item.source, item.target) + if (item.executable) { + chmodSync(item.target, 0o755) + } + } + + for (const args of [ + ['default', 'mpv-handler.desktop', 'x-scheme-handler/mpv-handler'], + ['default', 'mpv-handler-debug.desktop', 'x-scheme-handler/mpv-handler-debug'], + ]) { + const result = spawnSync('xdg-mime', args, { stdio: 'inherit' }) + if (result.status !== 0) { + fail(`xdg-mime failed for ${args[2]}. Run the command manually after fixing your desktop environment registration.`) + } + } +} + +function main() { + const options = parseArgs(process.argv.slice(2)) + + if (options.help) { + console.log(usage) + return + } + + if (!['win32', 'linux', 'darwin'].includes(process.platform)) { + fail(`Unsupported platform: ${process.platform}`) + } + + if (process.platform === 'darwin') { + console.log('Automatic macOS protocol registration is not available in this repo yet.') + console.log('The app can still browse media on macOS, but desktop playback setup must be handled manually.') + return + } + + const rootPath = resolveRoot(options) + const configResult = ensureConfig(rootPath, options) + + console.log(`Platform: ${process.platform}`) + console.log(`mpv-handler root: ${rootPath}`) + console.log(`config.toml: ${configResult.configPath}${configResult.changed ? ' (updated)' : ' (unchanged)'}`) + console.log(`mpv: ${configResult.mpvPath || 'not detected'}`) + console.log(`yt-dlp: ${configResult.ytdlPath || 'not detected'}`) + + if (process.platform === 'win32') { + runWindowsSetup(rootPath, options) + } else if (process.platform === 'linux') { + runLinuxSetup(rootPath, options) + } + + console.log('mpv-handler setup complete.') +} + +main() diff --git a/scripts/handler-install.bat b/scripts/handler-install.bat index 844b580..85a4120 100644 --- a/scripts/handler-install.bat +++ b/scripts/handler-install.bat @@ -1,82 +1,19 @@ @echo OFF +setlocal -:: Unattended install flag. When set, the script will not require user input. -set unattended=no -if "%1"=="/u" set unattended=yes +set interactive=yes +if /I "%1"=="/u" ( + set interactive=no + shift +) -:: Make sure this is Windows Vista or later -call :ensure_vista +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0install-mpv-handler.ps1" -InstallRoot "%~dp0" %* +if errorlevel 1 ( + echo. + echo Install failed. Re-run from an elevated PowerShell or Command Prompt. + if /I "%interactive%"=="yes" pause + exit /b 1 +) -:: Make sure the script is running as admin -call :ensure_admin - -:: Get mpv.exe location -call :check_binary - -:: Add registry -call :add_verbs - -:die - if not [%1] == [] echo %~1 - if [%unattended%] == [yes] exit 1 - pause - exit 1 - -:ensure_admin - :: 'openfiles' is just a commmand that is present on all supported Windows - :: versions, requires admin privileges and has no side effects, see: - :: https://stackoverflow.com/questions/4051883/batch-script-how-to-check-for-admin-rights - openfiles >nul 2>&1 - if errorlevel 1 ( - echo This batch script requires administrator privileges. - echo Right-click on handler-install.bat and select "Run as administrator". - call :die - ) - goto :EOF - -:ensure_vista - ver | find "XP" >nul - if not errorlevel 1 ( - echo This batch script only works on Windows Vista and later. To create file - echo associations on Windows XP, right click on a video file and use "Open with...". - call :die - ) - goto :EOF - -:check_binary - cd /D %~dp0 - set mpv_handler_conf=%cd%\config.toml - set mpv_handler_path=%cd%\mpv-handler.exe - set mpv_handler_debug_path=%cd%\mpv-handler-debug.exe - if not exist "%mpv_handler_conf%" call :die "Not found config.toml" - if not exist "%mpv_handler_path%" call :die "Not found mpv-handler.exe" - if not exist "%mpv_handler_debug_path%" call :die "Not found mpv-handler-debug.exe" - goto :EOF - -:reg - :: Wrap the reg command to check for errors - >nul reg %* - if errorlevel 1 set error=yes - if [%error%] == [yes] echo Error in command: reg %* - if [%error%] == [yes] call :die - goto :EOF - -:add_verbs - :: Add the mpv protocol to the registry - call :reg add "HKCR\mpv-handler" /d "URL:MPV Handler" /f - call :reg add "HKCR\mpv-handler" /v "Content Type" /d "application/x-mpv-handler" /f - call :reg add "HKCR\mpv-handler" /v "URL Protocol" /f - call :reg add "HKCR\mpv-handler\DefaultIcon" /d "\"%mpv_exe_path%\",1" /f - call :reg add "HKCR\mpv-handler\shell\open\command" /d "\"%mpv_handler_path%\" \"%%%%1\"" /f - - :: Add the mpv protocol to the registry - call :reg add "HKCR\mpv-handler-debug" /d "URL:MPV Handler Debug" /f - call :reg add "HKCR\mpv-handler-debug" /v "Content Type" /d "application/x-mpv-handler-debug" /f - call :reg add "HKCR\mpv-handler-debug" /v "URL Protocol" /f - call :reg add "HKCR\mpv-handler-debug\DefaultIcon" /d "\"%mpv_exe_path%\",1" /f - call :reg add "HKCR\mpv-handler-debug\shell\open\command" /d "\"%mpv_handler_debug_path%\" \"%%%%1\"" /f - - echo Successfully installed mpv-handler - echo Enjoy! - - goto :EOF +if /I "%interactive%"=="yes" pause +exit /b 0 diff --git a/scripts/handler-uninstall.bat b/scripts/handler-uninstall.bat index 963bab9..2cb0ab6 100644 --- a/scripts/handler-uninstall.bat +++ b/scripts/handler-uninstall.bat @@ -1,64 +1,19 @@ @echo OFF +setlocal -:: Unattended install flag. When set, the script will not require user input. -set unattended=no -if "%1"=="/u" set unattended=yes +set interactive=yes +if /I "%1"=="/u" ( + set interactive=no + shift +) -:: Make sure this is Windows Vista or later -call :ensure_vista +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0uninstall-mpv-handler.ps1" %* +if errorlevel 1 ( + echo. + echo Uninstall failed. Re-run from an elevated PowerShell or Command Prompt. + if /I "%interactive%"=="yes" pause + exit /b 1 +) -:: Make sure the script is running as admin -call :ensure_admin - -:: Delete registry -call :del_verbs - - - -:die - if not [%1] == [] echo %~1 - if [%unattended%] == [yes] exit 1 - pause - exit 1 - -:ensure_admin - :: 'openfiles' is just a commmand that is present on all supported Windows - :: versions, requires admin privileges and has no side effects, see: - :: https://stackoverflow.com/questions/4051883/batch-script-how-to-check-for-admin-rights - openfiles >nul 2>&1 - if errorlevel 1 ( - echo This batch script requires administrator privileges. - echo Right-click on handler-uninstall.bat and select "Run as administrator". - call :die - ) - goto :EOF - -:ensure_vista - ver | find "XP" >nul - if not errorlevel 1 ( - echo This batch script only works on Windows Vista and later. To create file - echo associations on Windows XP, right click on a video file and use "Open with...". - call :die - ) - goto :EOF - -:reg - :: Wrap the reg command to check for errors - >nul reg %* - if errorlevel 1 set error=yes - if [%error%] == [yes] echo Error in command: reg %* - if [%error%] == [yes] call :die - goto :EOF - -:del_verbs - :: Delete deprecated mpv and mpv-debug protocol - call :reg delete "HKCR\mpv" /f - call :reg delete "HKCR\mpv-debug" /f - - :: Delete protocol - call :reg delete "HKCR\mpv-handler" /f - call :reg delete "HKCR\mpv-handler-debug" /f - - echo Successfully uninstalled mpv-handler - - goto :EOF +if /I "%interactive%"=="yes" pause +exit /b 0 diff --git a/scripts/install-mpv-handler.ps1 b/scripts/install-mpv-handler.ps1 index 3c0989b..4ac151b 100644 --- a/scripts/install-mpv-handler.ps1 +++ b/scripts/install-mpv-handler.ps1 @@ -129,6 +129,29 @@ function Get-MpvPathFromConfig { } } +function Resolve-CommandPath { + param( + [Parameter(Mandatory = $true)] + [string[]]$Names + ) + + foreach ($name in $Names) { + try { + $command = Get-Command -Name $name -ErrorAction Stop + if ($command.Path) { + return $command.Path + } + + if ($command.Source) { + return $command.Source + } + } catch { + } + } + + return $null +} + function Remove-ProtocolKey { param( [Parameter(Mandatory = $true)] @@ -204,7 +227,12 @@ $effectiveIconPath = if ($IconPath) { } (Resolve-Path -LiteralPath $IconPath).Path } else { - Get-MpvPathFromConfig -ConfigPath $configPath + $configuredPath = Get-MpvPathFromConfig -ConfigPath $configPath + if ($configuredPath) { + $configuredPath + } else { + Resolve-CommandPath -Names @('mpv.com', 'mpv.exe', 'mpv') + } } if (-not $effectiveIconPath) { diff --git a/scripts/setup-mpv-handler.mjs b/scripts/setup-mpv-handler.mjs new file mode 100644 index 0000000..e272900 --- /dev/null +++ b/scripts/setup-mpv-handler.mjs @@ -0,0 +1,332 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process' +import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, chmodSync, writeFileSync } from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const scriptPath = fileURLToPath(import.meta.url) +const scriptDir = path.dirname(scriptPath) +const templateConfigPath = path.join(scriptDir, 'config.toml') + +const usage = `Usage: + npm run setup:mpv-handler + npm run setup:mpv-handler -- --root /path/to/mpv-handler + npm run setup:mpv-handler -- --mpv /path/to/mpv --ytdl /path/to/yt-dlp + +Options: + --root Path to the extracted mpv-handler folder. + --mpv Override the mpv executable path written to config.toml. + --ytdl Override the yt-dlp executable path written to config.toml. + --skip-config Do not create or update config.toml. + --keep-existing Windows only. Keep existing protocol keys instead of replacing them. + --dry-run Print the actions without changing files or running installers. + --help Show this help text. +` + +function fail(message) { + console.error(`Error: ${message}`) + process.exit(1) +} + +function parseArgs(argv) { + const options = { + root: '', + mpv: '', + ytdl: '', + skipConfig: false, + keepExisting: false, + dryRun: false, + help: false, + } + + for (let index = 0; index < argv.length; index += 1) { + const value = argv[index] + + if (value === '--help' || value === '-h') { + options.help = true + continue + } + + if (value === '--skip-config') { + options.skipConfig = true + continue + } + + if (value === '--keep-existing') { + options.keepExisting = true + continue + } + + if (value === '--dry-run') { + options.dryRun = true + continue + } + + if (value === '--root' || value === '--mpv' || value === '--ytdl') { + const nextValue = argv[index + 1] + if (!nextValue || nextValue.startsWith('--')) { + fail(`Missing value for ${value}`) + } + + if (value === '--root') options.root = nextValue + if (value === '--mpv') options.mpv = nextValue + if (value === '--ytdl') options.ytdl = nextValue + index += 1 + continue + } + + fail(`Unknown argument: ${value}`) + } + + return options +} + +function escapeTomlString(value) { + return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"') +} + +function upsertTomlValue(content, key, value) { + const line = `${key} = "${escapeTomlString(value)}"` + const pattern = new RegExp(`^\\s*#?\\s*${key}\\s*=.*$`, 'm') + if (pattern.test(content)) { + return content.replace(pattern, line) + } + + const trimmed = content.trimEnd() + return trimmed ? `${trimmed}\n${line}\n` : `${line}\n` +} + +function resolveOnPath(commandNames) { + const locator = process.platform === 'win32' ? 'where.exe' : 'which' + + for (const commandName of commandNames) { + const result = spawnSync(locator, [commandName], { encoding: 'utf8' }) + if (result.status !== 0) continue + + const match = result.stdout + .split(/\r?\n/) + .map((entry) => entry.trim()) + .find((entry) => entry) + + if (match && existsSync(match)) { + return match + } + } + + return '' +} + +function isFile(candidatePath) { + try { + return statSync(candidatePath).isFile() + } catch { + return false + } +} + +function looksLikeRoot(rootPath) { + const requiredFiles = process.platform === 'win32' + ? ['mpv-handler.exe', 'mpv-handler-debug.exe'] + : process.platform === 'linux' + ? ['mpv-handler', 'mpv-handler.desktop', 'mpv-handler-debug.desktop'] + : ['mpv-handler'] + + return requiredFiles.every((fileName) => isFile(path.join(rootPath, fileName))) +} + +function resolveRoot(options) { + const candidates = [] + if (options.root) candidates.push(path.resolve(options.root)) + candidates.push(scriptDir) + candidates.push(process.cwd()) + + const seen = new Set() + for (const candidate of candidates) { + if (!candidate || seen.has(candidate)) continue + seen.add(candidate) + if (looksLikeRoot(candidate)) { + return candidate + } + } + + if (process.platform === 'win32') { + fail('Could not find mpv-handler files. Re-run with --root pointing at the extracted mpv-handler folder.') + } + + if (process.platform === 'linux') { + fail('Could not find a Linux mpv-handler release. Download and extract the upstream archive, then pass --root /path/to/extracted/mpv-handler-linux-amd64.') + } + + fail('Automatic setup is not available for this platform yet.') +} + +function ensureConfig(rootPath, options) { + const configPath = path.join(rootPath, 'config.toml') + if (options.skipConfig) { + return { configPath, changed: false, mpvPath: '', ytdlPath: '' } + } + + const configExists = existsSync(configPath) + const content = configExists + ? readFileSync(configPath, 'utf8') + : readFileSync(templateConfigPath, 'utf8') + + const detectedMpv = options.mpv || resolveOnPath(process.platform === 'win32' ? ['mpv.com', 'mpv.exe', 'mpv'] : ['mpv']) + const detectedYtdl = options.ytdl || resolveOnPath(process.platform === 'win32' ? ['yt-dlp.exe', 'yt-dlp'] : ['yt-dlp']) + + let changed = !configExists + let nextContent = content + + if (detectedMpv) { + const updated = upsertTomlValue(nextContent, 'mpv', detectedMpv) + changed ||= updated !== nextContent + nextContent = updated + } + + if (detectedYtdl) { + const updated = upsertTomlValue(nextContent, 'ytdl', detectedYtdl) + changed ||= updated !== nextContent + nextContent = updated + } + + if (changed && !options.dryRun) { + writeFileSync(configPath, nextContent, 'utf8') + } + + return { + configPath, + changed, + mpvPath: detectedMpv, + ytdlPath: detectedYtdl, + } +} + +function runWindowsSetup(rootPath, options) { + const powershell = resolveOnPath(['powershell.exe', 'pwsh.exe']) || 'powershell.exe' + const installScript = path.join(scriptDir, 'install-mpv-handler.ps1') + const args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', installScript, '-InstallRoot', rootPath] + + if (options.keepExisting) { + args.push('-KeepExistingProtocolKeys') + } + + if (options.dryRun) { + console.log('Dry run: would execute Windows installer') + console.log(`${powershell} ${args.map((value) => JSON.stringify(value)).join(' ')}`) + return + } + + const result = spawnSync(powershell, args, { stdio: 'inherit' }) + if (result.status !== 0) { + fail('Windows protocol registration failed. Re-run the command from an elevated PowerShell or Windows Terminal.') + } +} + +function rewriteDesktopExec(content, targetBinary) { + return content.replace(/^Exec=.*$/m, (line) => { + const prefix = 'Exec=' + const rest = line.slice(prefix.length).trim() + const firstSpaceIndex = rest.indexOf(' ') + const suffix = firstSpaceIndex === -1 ? '' : rest.slice(firstSpaceIndex) + return `${prefix}${targetBinary}${suffix}` + }) +} + +function runLinuxSetup(rootPath, options) { + const localBin = path.join(os.homedir(), '.local', 'bin') + const applicationsDir = path.join(os.homedir(), '.local', 'share', 'applications') + const targetBinary = path.join(localBin, 'mpv-handler') + const copies = [ + { + source: path.join(rootPath, 'mpv-handler'), + target: targetBinary, + executable: true, + }, + { + source: path.join(rootPath, 'mpv-handler.desktop'), + target: path.join(applicationsDir, 'mpv-handler.desktop'), + patchExec: true, + }, + { + source: path.join(rootPath, 'mpv-handler-debug.desktop'), + target: path.join(applicationsDir, 'mpv-handler-debug.desktop'), + patchExec: true, + }, + ] + + if (options.dryRun) { + console.log('Dry run: would install Linux desktop files to ~/.local') + for (const item of copies) { + console.log(`copy ${item.source} -> ${item.target}`) + } + console.log('xdg-mime default mpv-handler.desktop x-scheme-handler/mpv-handler') + console.log('xdg-mime default mpv-handler-debug.desktop x-scheme-handler/mpv-handler-debug') + return + } + + mkdirSync(localBin, { recursive: true }) + mkdirSync(applicationsDir, { recursive: true }) + + for (const item of copies) { + if (item.patchExec) { + const content = readFileSync(item.source, 'utf8') + writeFileSync(item.target, rewriteDesktopExec(content, targetBinary), 'utf8') + continue + } + + copyFileSync(item.source, item.target) + if (item.executable) { + chmodSync(item.target, 0o755) + } + } + + for (const args of [ + ['default', 'mpv-handler.desktop', 'x-scheme-handler/mpv-handler'], + ['default', 'mpv-handler-debug.desktop', 'x-scheme-handler/mpv-handler-debug'], + ]) { + const result = spawnSync('xdg-mime', args, { stdio: 'inherit' }) + if (result.status !== 0) { + fail(`xdg-mime failed for ${args[2]}. Run the command manually after fixing your desktop environment registration.`) + } + } +} + +function main() { + const options = parseArgs(process.argv.slice(2)) + + if (options.help) { + console.log(usage) + return + } + + if (!['win32', 'linux', 'darwin'].includes(process.platform)) { + fail(`Unsupported platform: ${process.platform}`) + } + + if (process.platform === 'darwin') { + console.log('Automatic macOS protocol registration is not available in this repo yet.') + console.log('The app can still browse media on macOS, but desktop playback setup must be handled manually.') + return + } + + const rootPath = resolveRoot(options) + const configResult = ensureConfig(rootPath, options) + + console.log(`Platform: ${process.platform}`) + console.log(`mpv-handler root: ${rootPath}`) + console.log(`config.toml: ${configResult.configPath}${configResult.changed ? ' (updated)' : ' (unchanged)'}`) + console.log(`mpv: ${configResult.mpvPath || 'not detected'}`) + console.log(`yt-dlp: ${configResult.ytdlPath || 'not detected'}`) + + if (process.platform === 'win32') { + runWindowsSetup(rootPath, options) + } else if (process.platform === 'linux') { + runLinuxSetup(rootPath, options) + } + + console.log('mpv-handler setup complete.') +} + +main() \ No newline at end of file diff --git a/scripts/uninstall-mpv-handler.ps1 b/scripts/uninstall-mpv-handler.ps1 new file mode 100644 index 0000000..99b5892 --- /dev/null +++ b/scripts/uninstall-mpv-handler.ps1 @@ -0,0 +1,32 @@ +#Requires -Version 5.1 +#Requires -RunAsAdministrator + +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Remove-ProtocolKey { + param( + [Parameter(Mandatory = $true)] + [string]$SchemeName + ) + + $classesRoot = [Microsoft.Win32.Registry]::ClassesRoot + try { + $classesRoot.DeleteSubKeyTree($SchemeName, $false) + } catch { + } +} + +if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) { + throw 'This uninstaller is only for Windows.' +} + +Remove-ProtocolKey -SchemeName 'mpv' +Remove-ProtocolKey -SchemeName 'mpv-debug' +Remove-ProtocolKey -SchemeName 'mpv-handler' +Remove-ProtocolKey -SchemeName 'mpv-handler-debug' + +Write-Host 'Successfully removed mpv-handler protocol registration.' -ForegroundColor Green \ No newline at end of file