yaymukund’s weblog

My horrible mpv Nix flake

"This is my song
And nothing can make it die
It's been so long and it's stronger
I know why"

— Labi Siffre, My Song (YouTube)

Note: Some blog posts advocate for a way of doing something. This is not that. This post is to document what works for me.

An mpv install on aarch64-darwin consists of two things:

  1. An mpv binary.
  2. An mpv.app package that can be installed to /Applications.

I want both of these. The obvious approach is to use the mpv package on Nixpkgs. However, this requires building swift which on my M2 machine takes a long time to build, and periodically breaks (most recently fixed 4 days ago). Alternatively, you can install mpv outside of Nix, either manually or using Homebrew.

What if you don’t like either of these options?

It’s easy enough to write a flake that pulls the latest release from GitHub and installs it. This removes the swift build dependency yet keeps everything in Nix. That looks like this:

# mpv/flake.nix
{
  description = "mpv binary flake (to avoid compiling swift)";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs }:
    let
      pkgs = import nixpkgs { system = "aarch64-darwin"; };
      variant = "macos-15-arm";
      version = "v0.41.0";
    in
    {
      packages.aarch64-darwin.default = pkgs.stdenv.mkDerivation {
        pname = "mpv";
        inherit version;
        src = builtins.fetchurl {
          url = "https://github.com/mpv-player/mpv/releases/download/v${version}/mpv-v${version}-${variant}.zip";
          sha256 = "sha256-SJz2pU9XxU+GrY187a9bsmhIdw1Y3AWQISFOL2ie55k=";
        };

        dontBuild = true;
        nativeBuildInputs = with pkgs ; [
          unzip
        ];

        unpackPhase = ''
          unzip $src
          tar -xzf mpv.tar.gz
        '';

        installPhase = ''
          runHook preInstall

          mkdir -p $out/Applications
          mv mpv.app $out/Applications

          mkdir -p $out/bin
          ln -s $out/Applications/mpv.app/Contents/MacOS/mpv $out/bin/mpv

          runHook postInstall
        '';
      };
    };
}

Checking for updates

Optionally, you can add a script to check for updates. This is straightforward:

  1. Put the version is a file called mpv-version:

    v0.41.0
    
  2. Update the flake to read from mpv-version:

    let
      version = pkgs.lib.removeSuffix "\n" (builtins.readFile ./mpv-version);
    in
      # ...
    
  3. Finally, add the check-for-updates script:

    #!/usr/bin/env zsh
    #
    # mpv/check-for-updates
    #
    set -euo pipefail
    
    ROOT_DIR=${0:a:h}
    VERSION_FILE="$ROOT_DIR/mpv-version"
    
    # Read local version
    local_version=$(<"$VERSION_FILE")
    
    # Fetch latest version from GitHub and strip leading "v"
    latest_version=$(
      curl -fsSL https://api.github.com/repos/mpv-player/mpv/releases/latest \
        | jq -r '.tag_name | sub("^v"; "")'
    )
    
    if [[ "$local_version" == "$latest_version" ]]; then
      echo "✓ mpv v$local_version is up to date"
    else
      echo "✗ ERROR: mpv is outdated"
      echo "✗ Update available: $local_version → $latest_version"
      exit 1
    fi
    

Crime 1: Installing the .app

This almost behaves like a regular click-and-drag install of mpv. However, the .app is somewhere in the recesses of the Nix store. Typically:

 /nix/store/abc123-mpv/Applications/mpv.app

Once it’s installed, I run a script that cp -Rs it to /Applications.

Crime 2: Adding it to the dock

If you want the app to be docked, you’ll need to add it to the dock yourself with a utility such as dockutil.

What’s the catch? dockutil itself depends upon swift, so you need to write a flake for dockutil as well!:

{
  description = "dockutil binary flake (to avoid compiling swift)";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs }:
    let
      pkgs = import nixpkgs { system = "aarch64-darwin"; };
      version = pkgs.lib.removeSuffix "\n" (builtins.readFile ./dockutil-version);
    in
    {
      packages.aarch64-darwin.default = pkgs.stdenv.mkDerivation {
        pname = "dockutil";
        inherit version;
        src = builtins.fetchurl {
          url = "https://github.com/kcrawford/dockutil/releases/download/${version}/dockutil-${version}.pkg";
          sha256 = "sha256-9g24Jz/oDXxIJFiL7bU4pTh2dcORftsAENq59S0/JYI=";
        };

        dontBuild = true;
        nativeBuildInputs = with pkgs ; [
          libarchive
          p7zip
        ];

        unpackPhase = ''
          7z x $src
          bsdtar -xf Payload~
        '';

        installPhase = ''
          runHook preInstall

          mkdir -p $out/bin
          install -Dm755 usr/local/bin/dockutil -t $out/bin

          runHook postInstall
        '';
      };
    };
}

It’s flakes all the way down…