Add Makefile for user-scoped install, as well as DEVLOG

This commit is contained in:
mrkmntal 2026-10-27 16:42:04 -04:00
commit 2fc129ec21
4 changed files with 144 additions and 0 deletions

75
DEVLOG.md Normal file
View file

@ -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.

37
Makefile Normal file
View file

@ -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)"

View file

@ -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

View file

@ -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)