I recently moved my entire mpv config into a Nix tracked one. Home manager has some really useful utilities for configuring mpv, but there were some weird things that I hadn’t seen before.
What the frick is wrapping?
If you look up mpv in Nix pacakges, you’ll be greeted with these two (main) packages:
mpv
mpv-unwrapped
“Um ok, mpv seems more generic? Guess I’ll use that one…”
To help explain what is happening here we need to take a look at a little example configuration.
wrapMpv (pkgs.mpv-unwrapped.override { ffmpeg = pkgs.ffmpeg-full }) {
youtubeSupport = true;
scripts = with pkgs.mpvScripts; [
uosc
thumbfast
mpris
];
}
What the heck is this??? Lets start by looking in the parenthesis. We have the line:
pkgs.mpv-unwrapped.override { ffmpeg = pkgs.ffmpeg-full }
Ah, this looks familiar! This is just some simple attribute overriding. Lets see if we can find the line… here it is.
Ok so we can give this a temporary name to make it easier to read, my-unwrapped-mpv
. Cool. Lets look back at what we got.
wrapMpv my-unwrapped-mpv {
youtubeSupport = true;
scripts = with pkgs.mpvScripts; [
uosc
thumbfast
mpris
];
}
This is just a function! Here’s the declaration.
Look! There’s the youtubeSupport
and scripts
attribute. So what does wrapping actually do?
Explaining Wrapping
Some resources:
Looking into the definition for wrapMpv
we see that in the postBuild
script it calls makeWrapper
.
makeWrapper
essentially just makes it easier to run a package with certain arguments and environment variables.
For mpv this specifically adds the executables to the PATH
that are needed for certain features (youtube, python, lua …).
On top of that, it specifies arguments to run the executable with. In this case it bundles up all the scripts we specified and
adds them as arguments.
So that’s what the different packages do, and that’s what wrapping does as well! We’re now ready to package our own scripts.
Scripts
Looking at the declaration of the wrapper, we see this:
Scripts: a set of derivations (probably from
mpvScripts
) where each is expected to have ascriptName
passthru attribute that points to the name of the script that would reside in the script’s derivation’s$out/share/mpv/scripts/
. A script can optionally also provide anextraWrapperArgs
passthru attribute.
So we have need a $out/share/mpv/scripts/<name>.lua
file and we also have extraWrapperArgs
that add arguments for the wrapper
(those attributes and environment variables we were talking about!).
So we could manually do this, but nixpkgs has a buildLua function that does most of this for us.
Here’s what it does:
- Extracts the script name from the package name (mpv-uosc → uosc).
- Finds a
main.lua
within thesrc
and copies that to the$out/share/mpv/scripts/<name>.lua
(uosc.lua
) - Sets some meta attributes
- The package is done
Packaging our own script
Lets put this to the test by packaging skipsilence.
Giving this link a quick nurl we get this source:
fetchFromGitea {
domain = "codeberg.org";
owner = "ferreum";
repo = "mpv-skipsilence";
rev = "2c9b50cc492ee517a41d5e8555c6e491f0b3998c";
hash = "sha256-J06+gP/ND0wGQQPx1oTZuo6xhja2Ix2vK9xPtvhpJ8w=";
}
Checking this repository out we see that there exists a main.lua
so we don’t have to set passthru.scriptName
to the main file, but it should be really easy if you need to.
Put it together and we have:
{
pkgs,
fetchFromGitea,
lib,
unstableGitUpdater,
}:
pkgs.mpvScripts.buildLua {
pname = "mpv-skipsilence";
version = "unstable-2024-04-06";
passthru.updateScript = unstableGitUpdater {};
src = fetchFromGitea {
domain = "codeberg.org";
owner = "ferreum";
repo = "mpv-skipsilence";
rev = "2c9b50cc492ee517a41d5e8555c6e491f0b3998c";
hash = "sha256-J06+gP/ND0wGQQPx1oTZuo6xhja2Ix2vK9xPtvhpJ8w=";
};
meta = {
description = "Increase playback speed during silence";
homepage = "https://codeberg.org/ferreum/mpv-skipsilence";
license = lib.licenses.unfree; # At the time of writing this is unlicensed
};
}
I put this in skipsilence.nix
, so now in our scripts
option for the wrap, we can add
(pkgs.callPacakage ./skipsilence.nix { })
to our script list.
Shaders
Shaders are actually easier than scripts (if you’re using home-manager).
For shaders I like to set them up in a respective profile and then have an input or another script handle loading the profiles.
In home manager you have the option programs.mpv.profiles
. Here’s how I would
go about setting up CFL Prediction profile.
let
cflPrediction = pkgs.fetchFromGitHub {
owner = "Artoriuz";
repo = "glsl-chroma-from-luma-prediction";
rev = "37a449a94f8c532b4ed06822ea8b0398f4209737";
hash = "sha256-3Q8aDgdsKnJhd3fmEoJMTDoAkA5s2g1ve9SB1HKAB1U=";
};
in
config.programs.mpv.profiles = {
"interpolate-shaders" = {
glsl-shaders-toggle = [ "${cflPrediction}/CfL_Prediction.glsl" ];
};
};
Then when using apply-profile interpolate-shaders
it will toggle the shader. With home-manager,
I recommend using the -toggle
, -clr
, and -append
options for lists. -set
may work for one,
but with list home-manager just duplicates the config option. For set you can use -clr
and then
-append
. Clear doesn’t need any option, it can just be glsl-shaders-clr = ""
.