# curated mpv mods/scripts/users: # - # mpv docs: # - # - # extensions i use: # - # other extensions that could be useful: # - list: # - list: # - browse DLNA shares: # - act as a DLNS renderer (sink): # debugging: # - enter console by pressing backtick. # > `set volume 50` -> sets application volume to 50% # > `set ao-volume 50` -> sets system-wide volume to 50% # > `show-text "vol: ${volume}"` -> get the volume # - show script output by running mpv with `--msg-level=all=trace` # - and then just `print(...)` from lua & it'll show in terminal # - invoke mpv with `--no-config` to have it not read ~/.config/mpv/* # - press `i` to show decoder info # # usage tips: # - `<` or `>` to navigate prev/next-file-in-folder (uosc) # - shift+enter to view the playlist, then arrow-keys to navigate (mpv-playlistmanager) { config, lib, pkgs, ... }: let cfg = config.sane.programs.mpv; uosc = pkgs.mpvScripts.uosc.overrideAttrs (upstream: { # patch so that the volume control corresponds to `ao-volume`, i.e. the system-wide volume. # this is particularly nice for moby, because it avoids the awkwardness that system volume # is hard to adjust while screen is on. # note that only under alsa (`-ao=alsa`) does `ao-volume` actually correspond to system volume. postPatch = (upstream.postPatch or "") + '' substituteInPlace src/uosc/main.lua \ --replace-fail "mp.observe_property('volume'" "mp.observe_property('ao-volume'" substituteInPlace src/uosc/elements/Volume.lua \ --replace-fail "mp.commandv('set', 'volume'" "mp.commandv('set', 'ao-volume'" \ --replace-fail "mp.set_property_native('volume'" "mp.set_property('ao-volume'" # `ao-volume` isn't actually an observable property. # as of 2024/03/02, they *may* be working on that: # - # in the meantime, just query the volume every tick (i.e. frame). # alternative is mpv's JSON IPC feature, where i could notify its socket whenever pipewire volume changes. cat <> src/uosc/main.lua function update_ao_volume() local vol = mp.get_property('ao-volume') if vol ~= nil then vol = tonumber(vol) if vol ~= state.volume then set_state('volume', vol) request_render() end end end -- tick seems to occur on every redraw (even when volume is hidden). -- in practice: for every new frame of the source, or whenever the cursor is moved. mp.register_event('tick', update_ao_volume) -- if paused and cursor isn't moving, then `tick` isn't called. fallback to a timer. mp.add_periodic_timer(2, update_ao_volume) -- invoke immediately to ensure state.volume is non-nil update_ao_volume() if state.volume == nil then state.volume = 0 end EOF ''; }); in { sane.programs.mpv = { packageUnwrapped = with pkgs; wrapMpv mpv-unwrapped { scripts = [ mpvScripts.mpris mpvScripts.mpv-playlistmanager uosc # pkgs.mpv-uosc-latest ]; # extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [ # # 2023/08/29: fixes an error where mpv on moby launches with the message # # "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory" # # audio still works, and controls, screenshotting, etc -- just not the actual rendering # # # # this is likely a regression for mpv 0.36.0. # # the actual error message *appears* to come from the mesa library, but it's tough to trace. # # # # 2024/03/02: no longer necessary, with mesa 23.3.1: # # # # backend compatibility (2023/10/22): # # run with `--vo=help` to see a list of all output options. # # non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video)) # # ? null Null video output # # A (default) # # A dmabuf-wayland Wayland dmabuf video output # # A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window) # # A vdpau VDPAU with X11 # # F drm Direct Rendering Manager (software scaling) # # F gpu-next Video output based on libplacebo # # F vaapi VA API with X11 # # F x11 X11 (software scaling) # # F xv X11/Xv # # U gpu Shader-based GPU Renderer # # W caca libcaca (terminal rendering) # # W sdl SDL 2.0 Renderer # # W wlshm Wayland SHM video output (software scaling) # "--add-flags" "--vo=${cfg.config.vo}" # ]; }; suggestedPrograms = [ "blast-to-default" "go2tv" "xdg-terminal-exec" ]; sandbox.method = "bwrap"; sandbox.autodetectCliPaths = true; sandbox.net = "all"; sandbox.whitelistAudio = true; sandbox.whitelistDbus = [ "user" ]; #< mpris sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU sandbox.whitelistWayland = true; sandbox.extraHomePaths = [ ".config/mpv" #< else mpris plugin crashes on launch ".local/share/applications" #< for xdg-terminal-exec (go2tv) # it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file. # CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work. "Books/local" "Books/servo" "Music" "Videos/gPodder" "Videos/local" "Videos/servo" ]; persist.byStore.plaintext = [ # for `watch_later` ".local/state/mpv" ]; fs.".config/mpv/input.conf".symlink.text = '' # docs: # - # - script-binding: # - properties: # let volume/power keys be interpreted by the system. # this is important for sxmo. # mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume POWER ignore VOLUME_UP ignore VOLUME_DOWN ignore # uosc menu # text after the shebang is parsed by uosc to construct the menu and names menu script-binding uosc/menu s script-binding uosc/subtitles #! Subtitles a script-binding uosc/audio #! Audio tracks q script-binding uosc/stream-quality #! Stream quality p script-binding uosc/items #! Playlist c script-binding uosc/chapters #! Chapters > script-binding uosc/next #! Navigation > Next < script-binding uosc/prev #! Navigation > Prev o script-binding uosc/open-file #! Navigation > Open file # set video-aspect-override "-1" #! Utils > Aspect ratio > Default # set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9 # set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3 # set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1 # script-binding uosc/audio-device #! Utils > Audio devices # script-binding uosc/editions #! Utils > Editions ctrl+s async screenshot #! Utils > Screenshot alt+i script-binding uosc/keybinds #! Utils > Key bindings O script-binding uosc/show-in-directory #! Utils > Show in directory # script-binding uosc/open-config-directory #! Utils > Open config directory ctrl+r run blast-to-default #! Audiocast ctrl+t set pause yes; run xdg-terminal-exec go2tv -v "''${stream-open-filename}" #! Cast # set pause yes; run xdg-terminal-exec go2tv -u "''${stream-open-filename}" #! Cast (...) > Stream # set pause yes; run go2tv #! Cast (...) > GUI # TODO: unify "Cast" and "Cast (stream)" options above. ''; fs.".config/mpv/mpv.conf".symlink.text = '' # write ~/.local/state/mpv/watch_later on exit, to allow resume save-position-on-quit=yes # identify resumed files by filename only, since i use so many symlinks and doubt mpv does well with that. ignore-path-in-watch-later-config # keep-open: don't exit on completion of last file in playlist keep-open=yes # seeking once at the end of the file causes auto-resume keep-open-pause=no # force GUI, even for tracks w/o album art # see: player-operation-mode=pseudo-gui # use uosc instead (for On Screen Controls) osc=no # uosc provides its own seeking/volume indicators, so you also don't need this osd-bar=no # uosc will draw its own window controls if you disable window border border=no # ao=alsa so that uosc can work with ao-volume (see my uosc patch) ao=alsa # with `ao-volume`, the max actually is 100. # to go higher you'll have to use the system's native controls. volume-max=100 ''; fs.".config/mpv/script-opts/osc.conf".symlink.text = '' # make the on-screen controls *always* visible # unfortunately, this applies to full-screen as well # - docs: # if uosc is installed, this file is unused visibility=always ''; fs.".config/mpv/script-opts/console.conf".symlink.text = '' # font size used by mpv's console (`); default 16 # font_size=28 scale=2 ''; fs.".config/mpv/script-opts/uosc.conf".symlink.text = let play_pause_btn = "cycle:play_arrow:pause:no=pause/yes=play_arrow"; rev_btn = "command:replay_10:seek -10"; fwd_btn = "command:forward_30:seek 30"; in '' # docs: # - # - # - timeline_style=bar timeline_line_width=4 timeline_size=36 timeline_persistency=paused,audio controls_persistency=paused,audio volume_persistency=audio # speed_persistency=paused,audio # vvv want a close button? top_bar=always top_bar_persistency=paused,audio controls=menu,