{ config, lib, pkgs, ... }: with lib; let cfg = config.roles.plasma-bigscreen; # Plasma Bigscreen package — not yet in nixpkgs, built from upstream master plasma-bigscreen = pkgs.kdePackages.callPackage ./package.nix {}; 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; in { options.roles.plasma-bigscreen = { enable = mkEnableOption "Plasma Bigscreen TV interface"; autologin = mkOption { type = types.bool; default = false; description = "Auto-login to Plasma Bigscreen session"; }; user = mkOption { type = types.str; default = "kodi"; description = "User account for the Bigscreen session"; }; jellyfinScaleFactor = mkOption { type = types.nullOr types.float; default = null; description = "Scale factor for Jellyfin Media Player UI (e.g., 1.0 for 100% scaling)"; }; 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 = mkIf cfg.enable { # Create the bigscreen user users.extraUsers.${cfg.user} = { isNormalUser = true; extraGroups = [ "wheel" "networkmanager" "audio" "video" ]; }; # Plasma Bigscreen is a Plasma 6 shell — needs Plasma 6 desktop manager services.desktopManager.plasma6.enable = true; # Register the bigscreen session with the display manager services.displayManager = { sessionPackages = [ plasma-bigscreen ]; } // optionalAttrs cfg.autologin { autoLogin.enable = true; autoLogin.user = cfg.user; defaultSession = "plasma-bigscreen-wayland"; }; xdg.portal.configPackages = [ plasma-bigscreen ]; # Fix homescreen not being focused after quitting app or on boot # xwaylandvideobridge can interfere with focus; exclude if present environment.plasma6.excludePackages = lib.optional (pkgs.kdePackages ? xwaylandvideobridge) pkgs.kdePackages.xwaylandvideobridge; # Firewall for remote control networking.firewall = { allowedTCPPorts = optional cfg.appLauncherServer.enable cfg.appLauncherServer.port; }; environment.systemPackages = with pkgs; [ plasma-bigscreen firefox jellyfinMediaPlayerPkg qt-pinned.stremio 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/stremio dependency." [ "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:/run/current-system/sw/bin" ]; }; }; }; }; }