From 2fc129ec216a0cca89a82782c5ee9455a83fe68a Mon Sep 17 00:00:00 2001 From: mrkmntal Date: Tue, 27 Oct 2026 16:42:04 -0400 Subject: [PATCH] Add Makefile for user-scoped install, as well as DEVLOG --- DEVLOG.md | 75 +++++++++++++++++++++++++++++++++++++++++++ Makefile | 37 +++++++++++++++++++++ README.md | 25 +++++++++++++++ mpvremote/__main__.py | 7 ++++ 4 files changed, 144 insertions(+) create mode 100644 DEVLOG.md create mode 100644 Makefile diff --git a/DEVLOG.md b/DEVLOG.md new file mode 100644 index 0000000..c2275a6 --- /dev/null +++ b/DEVLOG.md @@ -0,0 +1,75 @@ +# Development Log + +This file records the app's architecture and the implementation choices made +to improve startup and queue responsiveness. It describes intended behavior; +no formal performance benchmark has been run. + +## Architecture + +- `mpvremote/config.py` is the central settings module. It defines the mpv IPC + socket path, supported video extensions, key bindings, app version, and media + directory. The media directory defaults to `~/Videos`; an override is read + from `~/.config/mpvremotemedia` and can be edited in Settings. +- `mpvremote/mpv_client.py` sends JSON commands to mpv over its Unix-domain IPC + socket and decodes JSON responses. +- `mpvremote/views.py` contains the curses views: + - `MainView` provides playback controls and status. + - `PlaylistView` displays and edits mpv's current playlist. + - `QueueView` searches the configured media library and queues files. + - `SettingsView` edits the media-directory setting. + - `AboutView` displays app information and key bindings. +- `mpvremote/app.py` initializes curses, constructs the views, owns the media + cache and scan worker, and coordinates drawing and input in the application + loop. +- `mpvremote/__init__.py` exports `App` and `SOCKET`. Running + `python3 -m mpvremote` loads the package and enters `mpvremote/__main__.py`. + The entry point handles `--help`, checks that the configured socket exists, + then uses `curses.wrapper` to set up and restore the terminal. The app enables + keypad mode and handles input through its `getch()` loop. + +## Startup and media scanning + +The initial recursive media scan runs on a background worker instead of +blocking `App` initialization. The worker scans immediately and waits 120 +seconds between subsequent scans. An event marks completion of the first scan. +If Queue is opened before that scan completes, it displays a scanning message +and populates automatically when the scan publishes its results. + +The scan commits an immutable tuple of entries containing the display name, +absolute path, and a pre-normalized `casefold()` name. Cache replacement and +version increments happen under a lock. `get_videos_with_version()` returns +the snapshot and version together; `get_videos()` retains the simpler +`(name, path)` interface for other callers. + +## Responsiveness changes + +### Escape-key decoding + +After curses initializes, the app requests a 50 ms Escape-sequence delay using +`curses.set_escdelay()`, when the platform supports it. This reduces the wait +when Escape is pressed while retaining a short interval for distinguishing +Escape from terminal key sequences such as arrows. + +### Queue filtering and rendering + +`QueueView` caches its indexed media snapshot and filtered results. Filtering +runs only when the search query or media-cache version changes; rendering does +not rescan or refilter the list. While Queue is active, the event loop uses a +100 ms input timeout and calls the view's update hook, allowing completed +background scans to appear without requiring a keypress. Other views continue +to wait for input normally. + +## User-level installation + +The root `Makefile` provides `install`, `verify`, and `uninstall` targets. +Installation copies the package beneath `~/.local/share/mpvremote` and creates +an executable `~/.local/bin/mpvremote` wrapper that sets `PYTHONPATH` and runs +`python3 -m mpvremote`. It does not edit shell startup files; it prints a PATH +instruction when needed. The verification target runs the installed launcher +with `--help`, which does not require mpv to be running. + +## Known performance limits + +The main view still retrieves playback status properties synchronously through +mpv IPC. Those requests have not been batched or moved to a background poller; +they remain a possible area for future profiling and optimization. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..390a29f --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +PREFIX ?= $(HOME)/.local +PYTHON ?= python3 + +SHARE_DIR := $(PREFIX)/share +APP_DIR := $(SHARE_DIR)/mpvremote +BIN_DIR := $(PREFIX)/bin +LAUNCHER := $(BIN_DIR)/mpvremote + +.PHONY: help install uninstall verify + +help: + @echo "make install Install mpvremote under $(PREFIX)" + @echo "make verify Check the installed launcher and module" + @echo "make uninstall Remove the user-level installation" + +install: + install -d "$(APP_DIR)" "$(BIN_DIR)" + cp -R mpvremote/. "$(APP_DIR)/" + @printf '%s\n' \ + '#!/bin/sh' \ + 'export PYTHONPATH="$(SHARE_DIR)$${PYTHONPATH:+:$$PYTHONPATH}"' \ + 'exec "$(PYTHON)" -m mpvremote "$$@"' \ + > "$(LAUNCHER)" + chmod 755 "$(LAUNCHER)" + @case ":$$PATH:" in \ + *":$(BIN_DIR):"*) echo "$(BIN_DIR) is already in PATH" ;; \ + *) echo 'Add $(BIN_DIR) to PATH: export PATH="$(BIN_DIR):$$PATH"' ;; \ + esac + +verify: + test -d "$(APP_DIR)" + test -x "$(LAUNCHER)" + cd "$(HOME)" && "$(LAUNCHER)" --help + +uninstall: + rm -f "$(LAUNCHER)" + rm -rf "$(APP_DIR)" diff --git a/README.md b/README.md index 878dd28..8904eab 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,31 @@ cd mpvremote python3 -m mpvremote ``` +### Install for your user + +Install a launcher and module copy under `~/.local` without root privileges: + +```bash +make install +``` + +If `~/.local/bin` is not already on your `PATH`, add this line to your shell +startup file (for example, `~/.bashrc`) and open a new shell: + +```bash +export PATH="$HOME/.local/bin:$PATH" +``` + +Verify the installation and print command-line usage with: + +```bash +make verify +mpvremote --help +``` + +Remove the user-level installation with `make uninstall`. Set `PREFIX` to +install under a different directory. + ## Configuration The IPC socket and video extensions are set in `mpvremote/config.py`. The media diff --git a/mpvremote/__main__.py b/mpvremote/__main__.py index a484907..d08be22 100644 --- a/mpvremote/__main__.py +++ b/mpvremote/__main__.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import argparse import sys import os import curses @@ -7,6 +8,12 @@ from mpvremote.config import SOCKET def main(): + parser = argparse.ArgumentParser( + prog="mpvremote", + description="A curses-based remote control for a running mpv player.", + ) + parser.parse_args() + if not os.path.exists(SOCKET): print(f"Error: Socket {SOCKET} not found or mpv is not running.", file=sys.stderr) sys.exit(1)