Something went wrong. Try again.
a Jellyfin & Subsonic client for the terminal — powered by mpv, Chromecast and UPnP MediaRenderer
mpv chromecast mpris navidrome jellyfin upnp tui
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194{ description = "fin - a neon-electric Jellyfin TUI client for mpv & Chromecast";
inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# Current crane doesn't expose a `nixpkgs` input, so we don't follow it. crane.url = "github:ipetkov/crane";
fenix = { url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; inputs.rust-analyzer-src.follows = ""; };
flake-utils.url = "github:numtide/flake-utils";
advisory-db = { url = "github:rustsec/advisory-db"; flake = false; }; };
outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; };
inherit (pkgs) lib;
craneLib = crane.mkLib pkgs;
src = craneLib.cleanCargoSource ./.;
# fin uses rustls end-to-end, so no openssl. # mpv is a *runtime* requirement (we spawn it for local playback and # the CLI does a preflight `mpv --version` at startup). We fold it # into PATH via a makeWrapper post-fixup below. commonArgs = { inherit src;
pname = "fin"; version = "0.6.0"; strictDeps = true;
# rockbox-playback pulls in rockbox-codecs + rockbox-dsp, whose # build scripts compile Rockbox's C codec/DSP sources with the `cc` # crate — so a C compiler must be on PATH. `stdenv.cc` is the # toolchain for this platform (clang on Darwin, gcc on Linux). # TLS is pure-Rust (rustls), so still no openssl. # Modern nixpkgs (post-25.05) auto-links the Darwin SDK, so no # framework references here — `darwin.apple_sdk_11_0` was removed # as a legacy compatibility stub. nativeBuildInputs = [ pkgs.pkg-config pkgs.stdenv.cc ] ++ lib.optionals pkgs.stdenv.isDarwin [ # coreaudio-sys generates its CoreAudio bindings with bindgen at # build time; bindgenHook provides libclang (LIBCLANG_PATH) and # points clang at the Nix Apple SDK headers. pkgs.rustPlatform.bindgenHook ];
buildInputs = lib.optionals pkgs.stdenv.isDarwin [ pkgs.libiconv ] ++ lib.optionals pkgs.stdenv.isLinux [ # cpal links against ALSA on Linux for the local audio output path. # MPRIS needs no dbus dev lib: zbus is a pure-Rust D-Bus # implementation that speaks to the session bus socket directly. pkgs.alsa-lib ];
# Workspace has one bin target — build just that. cargoExtraArgs = "--locked --bin fin"; };
craneLibLLvmTools = craneLib.overrideToolchain (fenix.packages.${system}.complete.withComponents [ "cargo" "llvm-tools" "rustc" ]);
# Cache the dependency graph separately from the crate source. cargoArtifacts = craneLib.buildDepsOnly commonArgs;
finUnwrapped = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; doCheck = false; });
# Wrap the binary so `mpv` is always discoverable at runtime, even # when installed via `nix profile install`. fin = pkgs.symlinkJoin { name = "fin-${finUnwrapped.version}"; paths = [ finUnwrapped ]; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' wrapProgram $out/bin/fin \ --prefix PATH : ${lib.makeBinPath [ pkgs.mpv ]} ''; meta = { description = "A neon-electric Jellyfin TUI client for mpv & Chromecast"; homepage = "https://github.com/tsirysndr/fin"; license = lib.licenses.mpl20; mainProgram = "fin"; platforms = lib.platforms.unix; }; };
in { checks = { inherit fin;
fin-clippy = craneLib.cargoClippy (commonArgs // { inherit cargoArtifacts; cargoClippyExtraArgs = "--all-targets -- --deny warnings"; });
fin-doc = craneLib.cargoDoc (commonArgs // { inherit cargoArtifacts; });
fin-fmt = craneLib.cargoFmt { inherit src; };
fin-audit = craneLib.cargoAudit { inherit src advisory-db; };
fin-nextest = craneLib.cargoNextest (commonArgs // { inherit cargoArtifacts; partitions = 1; partitionType = "count"; }); } // lib.optionalAttrs (system == "x86_64-linux") { fin-coverage = craneLib.cargoTarpaulin (commonArgs // { inherit cargoArtifacts; }); };
packages = { default = fin; fin = fin; fin-unwrapped = finUnwrapped;
fin-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs // { inherit cargoArtifacts; }); };
apps.default = flake-utils.lib.mkApp { drv = fin; name = "fin"; };
devShells.default = pkgs.mkShell { inputsFrom = builtins.attrValues self.checks.${system};
# Build-time tools. pkg-config is required so cpal's build.rs can # resolve libasound on Linux; stdenv.cc supplies the C compiler the # rockbox-codecs / rockbox-dsp build scripts need to compile # Rockbox's C sources. nativeBuildInputs = with pkgs; [ cargo rustc rustfmt clippy rust-analyzer mpv pkg-config stdenv.cc ];
# Link-time libraries. Position matters: pkg-config only picks up # `.pc` files from `buildInputs`, so alsa-lib MUST live here (not # in nativeBuildInputs) for the cpal → ALSA link to resolve. buildInputs = with pkgs; lib.optionals stdenv.isDarwin [ libiconv ] ++ lib.optionals stdenv.isLinux [ alsa-lib ];
shellHook = '' echo "⚡ fin dev shell — mpv $(mpv --version | head -n1 | cut -d' ' -f2) ready" ''; }; });}