From d05cadaf39ed47b47c47f3f10af46f6c6516ffdf Mon Sep 17 00:00:00 2001 From: ekisu Date: Fri, 11 Sep 2026 13:39:52 -0300 Subject: [PATCH 1/2] Document encoder PATH and codec requirements --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 23a8953..02e9f4f 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ Place [this][build] in your mpv `scripts` folder. The `scripts` folder can be fo Additional details about the folder structure can be found in the [mpv's manual][file locations]. +### Encoder executable + +Encoding starts a separate `mpv` process. The `mpv` executable must be available on the `PATH` inherited by the player, including when the player is opened from a desktop shortcut or file manager. + +- **Windows:** add the directory containing `mpv.exe` to your user `Path` environment variable, then restart mpv. Run `where mpv` in a new Command Prompt to check that Windows can find it. +- **Linux/macOS:** run `command -v mpv` to check your shell's `PATH`. If encoding reports `mpv: command not found` when launched from the desktop, ensure that launch environment also includes the executable's directory. Homebrew commonly installs it in `/opt/homebrew/bin` on Apple Silicon and `/usr/local/bin` on Intel Macs. + +If encoding instead reports a missing codec, check `mpv --ovc=help` for video encoders or `mpv --oac=help` for audio encoders. These lists come from the FFmpeg libraries used by **mpv**; installing a separate `ffmpeg` executable does not necessarily add codecs to mpv. On macOS, the Homebrew formula (`brew install mpv`) is one source of an encoder-enabled build. + By default, the script is activated by the W (shift+w) key. ## Usage From bb173b74878171a93015a313e072753bf2cd369d Mon Sep 17 00:00:00 2001 From: ekisu Date: Sat, 12 Sep 2026 16:40:42 -0300 Subject: [PATCH 2/2] Explain Windows encoder setup and report mpv startup failures --- README.md | 2 +- docs/windows-encoder-setup.md | 63 +++++++++++++++++++++++++ src/encode.moon | 17 +++++++ tests/testcases/base_test_case.py | 3 +- tests/testcases/test_encoder_startup.py | 34 +++++++++++++ 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 docs/windows-encoder-setup.md create mode 100644 tests/testcases/test_encoder_startup.py diff --git a/README.md b/README.md index 02e9f4f..fcffac2 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Additional details about the folder structure can be found in the [mpv's manual] Encoding starts a separate `mpv` process. The `mpv` executable must be available on the `PATH` inherited by the player, including when the player is opened from a desktop shortcut or file manager. -- **Windows:** add the directory containing `mpv.exe` to your user `Path` environment variable, then restart mpv. Run `where mpv` in a new Command Prompt to check that Windows can find it. +- **Windows:** follow the [Windows encoder setup guide](docs/windows-encoder-setup.md) for step-by-step instructions to add mpv to `Path`, verify it, and troubleshoot desktop launches. - **Linux/macOS:** run `command -v mpv` to check your shell's `PATH`. If encoding reports `mpv: command not found` when launched from the desktop, ensure that launch environment also includes the executable's directory. Homebrew commonly installs it in `/opt/homebrew/bin` on Apple Silicon and `/usr/local/bin` on Intel Macs. If encoding instead reports a missing codec, check `mpv --ovc=help` for video encoders or `mpv --oac=help` for audio encoders. These lists come from the FFmpeg libraries used by **mpv**; installing a separate `ffmpeg` executable does not necessarily add codecs to mpv. On macOS, the Homebrew formula (`brew install mpv`) is one source of an encoder-enabled build. diff --git a/docs/windows-encoder-setup.md b/docs/windows-encoder-setup.md new file mode 100644 index 0000000..8198ed4 --- /dev/null +++ b/docs/windows-encoder-setup.md @@ -0,0 +1,63 @@ +# Windows encoder setup + +mpv-webm starts a second `mpv.exe` to encode your clip. Windows must be able to +find it through the `Path` environment variable, even when you open the player +by double-clicking a video. + +## 1. Locate mpv + +If needed, get a Windows build from the [mpv installation page](https://mpv.io/installation/). +Extract the archive into a permanent folder, for example `C:\Tools\mpv`. +Check that `mpv.exe` is directly inside that folder. Use your actual folder in +the steps below; do not add the archive or the executable filename to `Path`. + +## 2. Add that folder to your user Path + +1. Open **Start**, search for **Edit environment variables for your account**, + and open that settings dialog. +2. Under **User variables for your account**, select **Path**, then **Edit**. +3. Click **New** and enter the folder containing `mpv.exe`, for example + `C:\Tools\mpv`. Keep the existing entries. Do not put quotes around the folder. +4. If your account has no `Path` variable yet, click **New** in the User variables + section, use `Path` as the name and the mpv folder as the value. +5. Click **OK** in each dialog to save the change. + +## 3. Verify and restart the player + +Close existing Command Prompt and mpv windows. Open a **new Command Prompt** +from Start and run: + +```bat +where mpv +mpv --version +``` + +`where mpv` should print the path to your `mpv.exe`, and `mpv --version` should +print version information. If Windows cannot find it, check that the folder +from step 2 really contains `mpv.exe` and that the change was saved. + +Open mpv again and try encoding. If a terminal launch works but opening a video +from Explorer still fails, sign out of Windows and sign back in so Explorer +and other launchers inherit the updated environment. Restart third-party +launchers too. File associations alone do not put mpv on `Path`. + +If `where mpv` lists multiple copies, Windows normally selects the first one. +Check that it is the build you intended to use. + +## 4. If mpv starts but an encoder is missing + +Run: + +```bat +mpv --ovc=help +mpv --oac=help +``` + +These list the video and audio encoders in your mpv build. Select a supported +format or install an mpv build with the required codec. Installing a separate +FFmpeg executable does not change the codecs compiled into mpv. + +The message **Cannot start the mpv encoder** points to an executable startup +problem; the message **mpv encoder failed its startup check** means mpv was +launched but returned an error. Run `mpv --version` and inspect the player logs +for the underlying diagnostic. diff --git a/src/encode.moon b/src/encode.moon index cff523e..dc1af1d 100644 --- a/src/encode.moon +++ b/src/encode.moon @@ -275,8 +275,25 @@ find_path = (startTime, endTime) -> return path, is_stream, is_temporary, startTime, endTime +-- Probe the same executable before entering any launch mode (including the +-- progress shell and detached mode, which otherwise hide spawn failures). +check_encoder = -> + result = utils.subprocess({args: {"mpv", "--no-config", "--version"}, cancellable: false}) + if result.status == 0 + return true + explanation = "Cannot start the mpv encoder. Add the folder containing mpv to PATH, then restart the player. See README: Encoder executable." + if result.status and result.status > 0 + explanation = "The mpv encoder failed its startup check. Run mpv --version and check the logs for details." + msg.error(explanation) + msg.error("Encoder startup check: ", result.error or "", result.stderr or "", result.stdout or "") + message(explanation, 10) + emit_event("encode-finished", "fail", explanation) + return false + encode = (region, startTime, endTime) -> format = formats[options.output_format] + if not check_encoder! + return originalStartTime = startTime originalEndTime = endTime diff --git a/tests/testcases/base_test_case.py b/tests/testcases/base_test_case.py index 2d10fde..4443745 100644 --- a/tests/testcases/base_test_case.py +++ b/tests/testcases/base_test_case.py @@ -17,6 +17,7 @@ class BaseTestCase(unittest.TestCase): # GUI subclasses supply a real video output and an isolated DISPLAY. + mpv_executable = "mpv" mpv_args = () mpv_env = None @@ -31,7 +32,7 @@ def setUp(self): self.log_reader = None socket_address = str(self.tempdir / "ipc") args = [ - "mpv", "-v", "--no-config", "--vo=null", "--ao=null", + self.mpv_executable, "-v", "--no-config", "--vo=null", "--ao=null", "--load-scripts=no", "--scripts-clr", "--idle=yes", "--input-ipc-server=" + socket_address, *self.mpv_args, diff --git a/tests/testcases/test_encoder_startup.py b/tests/testcases/test_encoder_startup.py new file mode 100644 index 0000000..7e0a047 --- /dev/null +++ b/tests/testcases/test_encoder_startup.py @@ -0,0 +1,34 @@ +import os +import shutil +import tempfile + +from .base_test_case import BaseTestCase, ROOT + + +class TestMissingEncoder(BaseTestCase): + def setUp(self): + # Start a real player by absolute path, but make its child mpv lookup + # fail. This reproduces a desktop launch with an incomplete PATH. + self.mpv_executable = shutil.which("mpv") + self.assertIsNotNone(self.mpv_executable) + empty_path = tempfile.TemporaryDirectory(prefix="mpv-empty-path-") + self.addCleanup(empty_path.cleanup) + self.mpv_env = {**os.environ, "PATH": empty_path.name} + super().setUp() + + def test_missing_encoder_reports_actionable_error_in_all_launch_modes(self): + self.openTestVideoFile(ROOT / "tests/videos/big_buck_bunny_10s.mp4") + self.setRange(0, 1) + for mode in ({"display_progress": False, "run_detached": False, "twopass": False}, + {"display_progress": True, "run_detached": False, "twopass": False}, + {"display_progress": False, "run_detached": True, "twopass": False}, + {"display_progress": False, "run_detached": False, "twopass": True}): + with self.subTest(mode=mode): + self.updateScriptOptions({**mode, "output_template": "missing"}) + event = self.scriptMessage("mpv-webm-encode", event="webm-encode-finished") + self.assertEqual(event.args[:2], ["webm-encode-finished", "fail"]) + self.assertIn("Cannot start the mpv encoder", event.args[2]) + self.assertIn("PATH", event.args[2]) + self.assertIn("restart the player", event.args[2]) + self.waitUntil(lambda: event.args[2] in self.getLog(), "actionable encoder error log") + self.assertFalse((self.tempdir / "missing.webm").exists())