{ config, lib, pkgs, ... }: with lib; let cfg = config.roles.kodi; in { options.roles.kodi = { enable = mkEnableOption "Enable Kodi"; autologin = mkOption { default = false; }; wayland = mkOption { default = true; }; jellyfinScaleFactor = mkOption { type = types.nullOr types.float; default = null; description = "Scale factor for Jellyfin Media Player UI (e.g., 1.5 for 150% scaling)"; }; stremioScaleFactor = mkOption { type = types.nullOr types.float; default = null; description = "Scale factor for Stremio UI via GDK_BACKEND=x11 + GDK_SCALE (e.g., 2.0 for 200% scaling). Forces XWayland mode since GTK4 ignores GDK_SCALE on native Wayland."; }; appLauncherServer = { enable = mkOption { type = types.bool; default = false; description = "Enable HTTP app launcher server for remote control"; }; port = mkOption { type = types.int; default = 8081; description = "Port for the app launcher HTTP server"; }; }; }; config = let kodiBasePkg = if cfg.wayland then pkgs.kodi-wayland else pkgs.kodi; kodiPkg = kodiBasePkg.withPackages (pkgs: with pkgs; [ jellyfin steam-launcher steam-library youtube ]); jellyfinMediaPlayerPkg = if cfg.jellyfinScaleFactor != null then pkgs.symlinkJoin { name = "jellyfin-media-player-scaled"; paths = [ pkgs.qt-pinned.jellyfin-media-player ]; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' mkdir -p $out/bin rm -f $out/bin/jellyfin-desktop makeWrapper ${pkgs.qt-pinned.jellyfin-media-player}/bin/jellyfin-desktop $out/bin/jellyfin-desktop \ --add-flags "--tv --scale-factor ${toString cfg.jellyfinScaleFactor}" # Update .desktop file to include scale factor and TV mode arguments mkdir -p $out/share/applications rm -f $out/share/applications/org.jellyfin.JellyfinDesktop.desktop substitute ${pkgs.qt-pinned.jellyfin-media-player}/share/applications/org.jellyfin.JellyfinDesktop.desktop \ $out/share/applications/org.jellyfin.JellyfinDesktop.desktop \ --replace-fail "Exec=jellyfin-desktop" "Exec=jellyfin-desktop --tv --scale-factor ${toString cfg.jellyfinScaleFactor}" ''; } else pkgs.qt-pinned.jellyfin-media-player; # stremio-linux-shell is a GTK4/libadwaita + WebKitGTK app (not Electron/CEF). # --force-device-scale-factor is a Chromium flag that it ignores entirely. # On Wayland, GTK4 reads scale from the compositor via wp_fractional_scale_v1. # To force a specific scale, we use GDK_BACKEND=x11 + GDK_SCALE which works # reliably under XWayland. On native Wayland, GDK_SCALE is ignored. # Format floats cleanly: 2.0 -> "2", 1.5 -> "1.5" stremioScaleStr = let sf = cfg.stremioScaleFactor; intPart = builtins.floor sf; s = builtins.toString sf; m = builtins.match "([0-9]+)\\.([0-9]*[1-9])0*" s; gdkScale = builtins.toString intPart; in if sf == intPart then builtins.toString intPart else if m != null then (builtins.elemAt m 0) + "." + (builtins.elemAt m 1) else s; stremioPkg = if cfg.stremioScaleFactor != null then pkgs.symlinkJoin { name = "stremio-scaled"; paths = [ pkgs.stremio-linux-shell ]; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' mkdir -p $out/bin rm -f $out/bin/stremio makeWrapper ${pkgs.stremio-linux-shell}/bin/stremio $out/bin/stremio \ --set GDK_BACKEND x11 \ --set GDK_SCALE ${builtins.toString (builtins.floor cfg.stremioScaleFactor)} # Update .desktop file to force XWayland + GDK_SCALE for scaling mkdir -p $out/share/applications rm -f $out/share/applications/com.stremio.Stremio.desktop substitute ${pkgs.stremio-linux-shell}/share/applications/com.stremio.Stremio.desktop \ $out/share/applications/com.stremio.Stremio.desktop \ --replace-fail "Exec=sh -c " "Exec=env GDK_BACKEND=x11 GDK_SCALE=${builtins.toString (builtins.floor cfg.stremioScaleFactor)} sh -c " ''; } else pkgs.stremio-linux-shell; in mkIf cfg.enable { users.extraUsers.kodi = { isNormalUser = true; extraGroups = [ "wheel" "networkmanager" "audio" "video" ]; }; networking.firewall = { allowedTCPPorts = [ 8080 ] ++ optional cfg.appLauncherServer.enable cfg.appLauncherServer.port; allowedUDPPorts = [ 8080 ]; }; environment.systemPackages = with pkgs; [ firefox jellyfinMediaPlayerPkg kodiPkg stremioPkg # qt-pinned.stremio # Replaced by stremio-linux-shell (Rust/CEF-based) wget ] ++ optional cfg.appLauncherServer.enable pkgs.custom.app-launcher-server; nixpkgs.config.permittedInsecurePackages = lib.warn "Allowing insecure package qtwebengine-5.15.19 as a jellyfin-media-player dependency. These are pinned to nixpkgs-qt to avoid rebuilds - update that input separately when you have time." [ "qtwebengine-5.15.19" ]; programs.kdeconnect.enable = true; systemd.user.services = mkIf cfg.appLauncherServer.enable { app-launcher-server = { description = "HTTP App Launcher Server"; wantedBy = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.custom.app-launcher-server}/bin/app-launcher-server ${toString cfg.appLauncherServer.port}"; Restart = "always"; RestartSec = "5s"; Environment = [ "PATH=${pkgs.firefox}/bin:${kodiPkg}/bin:/run/current-system/sw/bin" ]; }; }; }; services.displayManager = mkIf cfg.autologin { autoLogin.enable = true; autoLogin.user = "kodi"; defaultSession = "plasma"; }; }; }