me like nix
1{ pkgs, lib, ... }:
2
3let
4 # Patch Invidious addon to prepend instance URL to relative stream/dash URLs
5 # and pass local=true to API so Invidious proxies video streams
6 patchInvidious = drv: drv.overrideAttrs (old: {
7 postPatch = (old.postPatch or "") + ''
8 substituteInPlace resources/lib/invidious_plugin.py \
9 --replace-fail \
10 'url = video_info["dashUrl"]' \
11 'url = video_info["dashUrl"]; url = self.api_client.instance_url + url if url.startswith("/") else url' \
12 --replace-fail \
13 'url = video_info["formatStreams"][-1]["url"]' \
14 'url = video_info["formatStreams"][-1]["url"]; url = self.api_client.instance_url + url if url.startswith("/") else url'
15 substituteInPlace resources/lib/invidious_api.py \
16 --replace-fail \
17 'response = self.make_get_request("videos/", video_id)' \
18 'response = self.make_get_request("videos/", video_id, local="true")'
19 '';
20 });
21
22 myKodi = pkgs.kodi-gbm.withPackages (kodiPkgs: [
23 kodiPkgs.jellyfin
24 (patchInvidious kodiPkgs.invidious)
25 ]);
26
27 kodiAdvancedSettings = pkgs.writeText "advancedsettings.xml" ''
28 <advancedsettings version="1.0">
29 <videoplayer>
30 <useprimedecoder>true</useprimedecoder>
31 <useprimedecoderforhw>true</useprimedecoderforhw>
32 </videoplayer>
33 </advancedsettings>
34 '';
35in
36{
37 imports = [ ../pi-common/wifi.nix ];
38
39 networking.hostName = "kodi-pi";
40
41 # Use the new generational bootloader (matches nixos-raspberrypi installer)
42 boot.loader.raspberry-pi.bootloader = "kernel";
43
44 # Force 4K30 output when EDID read fails (TV may not be ready at boot)
45 boot.kernelParams = [ "video=HDMI-A-1:3840x2160@30D" ];
46
47 # Filesystems (matching nixos-raspberrypi installer layout)
48 fileSystems."/" = {
49 device = "/dev/disk/by-label/NIXOS_SD";
50 fsType = "ext4";
51 options = [ "noatime" ];
52 };
53 fileSystems."/boot/firmware" = {
54 device = "/dev/disk/by-label/FIRMWARE";
55 fsType = "vfat";
56 options = [ "noatime" "noauto" "x-systemd.automount" "x-systemd.idle-timeout=1min" ];
57 };
58
59 # Graphics
60 hardware.graphics.enable = true;
61
62 # Pi firmware config.txt settings for Kodi
63 hardware.raspberry-pi.config.all.options = {
64 gpu_mem = { enable = true; value = 256; };
65 hdmi_force_hotplug = { enable = true; value = true; };
66 };
67
68 # Audio: ALSA only for HDMI passthrough
69 services.pipewire.enable = false;
70 services.pulseaudio.enable = false;
71
72 # Kodi via GBM (direct DRM, no compositor needed)
73 systemd.services.kodi = {
74 description = "Kodi media center";
75 wantedBy = [ "multi-user.target" ];
76 after = [ "network-online.target" "sound.target" "systemd-user-sessions.service" ];
77 wants = [ "network-online.target" ];
78 serviceConfig = {
79 Type = "simple";
80 User = "kodi";
81 ExecStartPre = pkgs.writeShellScript "kodi-setup" ''
82 mkdir -p /home/kodi/.kodi/userdata
83 cp -f ${kodiAdvancedSettings} /home/kodi/.kodi/userdata/advancedsettings.xml
84
85 # Enable web remote control interface (port 8080)
86 GUISETTINGS=/home/kodi/.kodi/userdata/guisettings.xml
87 if [ -f "$GUISETTINGS" ]; then
88 ${pkgs.gnused}/bin/sed -i \
89 -e 's|"services.webserver"[^>]*>[^<]*<|"services.webserver">true<|' \
90 -e 's|"services.webserverauthentication"[^>]*>[^<]*<|"services.webserverauthentication">false<|' \
91 "$GUISETTINGS"
92 fi
93
94 # Patch Jellyfin addon settings (Pi 5 has HW HEVC decoder, no need to transcode)
95 JFSETTINGS=/home/kodi/.kodi/userdata/addon_data/plugin.video.jellyfin/settings.xml
96 if [ -f "$JFSETTINGS" ]; then
97 ${pkgs.gnused}/bin/sed -i \
98 -e 's|"transcode_h265">true|"transcode_h265">false|' \
99 -e 's|"transcodeHi10P">true|"transcodeHi10P">false|' \
100 -e 's|"kodiCompanion">false|"kodiCompanion">true|' \
101 "$JFSETTINGS"
102 fi
103
104 # Configure Invidious addon to use a specific instance (auto-detect is unreliable)
105 INVSETTINGS=/home/kodi/.kodi/userdata/addon_data/plugin.video.invidious/settings.xml
106 if [ -f "$INVSETTINGS" ]; then
107 ${pkgs.gnused}/bin/sed -i \
108 -e 's|"auto_instance">true|"auto_instance">false|' \
109 -e 's|"instance_url"[^<]*<|"instance_url">http://mira:3001<|' \
110 -e 's|"disable_dash"[^>]*>[^<]*<|"disable_dash">false<|' \
111 "$INVSETTINGS"
112 else
113 mkdir -p /home/kodi/.kodi/userdata/addon_data/plugin.video.invidious
114 cat > "$INVSETTINGS" <<'INVEOF'
115<settings version="2">
116 <setting id="auto_instance">false</setting>
117 <setting id="instance_url">http://mira:3001</setting>
118 <setting id="disable_dash">false</setting>
119</settings>
120INVEOF
121 fi
122
123 '';
124 ExecStart = "${myKodi}/bin/kodi-standalone";
125 Restart = "always";
126 TimeoutStopSec = "15s";
127 TimeoutStopFailureMode = "kill";
128 };
129 };
130
131 # Kodi user
132 users.users.kodi = {
133 isNormalUser = true;
134 extraGroups = [ "video" "audio" "input" "render" ];
135 };
136
137 # Admin user
138 users.users.sean = {
139 isNormalUser = true;
140 extraGroups = [ "wheel" ];
141 openssh.authorizedKeys.keys = [
142 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCIqgZ7kedxo+mOW7YG73Vp3zel3h180y3GKvHtRsXfGlpIIvRDy7pgCBQ4AGXYD4y78URQmFohYSAPqCPOPaWcU2un3XG9KvCzEsHmsbskPonitUmCiKvrKkb6oW4jCBtd7AEtBn+AiajAQFtPZ7NN2Df3AmTypvR6Irg7R+nxnfc9NTIHmGvxSDyWcbb4pguL20sctUSqGL6xGh8q/bqhdOThSimM+z9bEUNxK/5rPhwkNniMrp4pJcUrUiAh5/4DiRFG6KT+oeg+/myoz/Z1sPvAs7u/8JDQI4RshRD8Hu0oTkRBN6Hxj478q2SXbeBUZlD6IdjP3RhGpmSecoDdtWqKbpuV3eVRtQtba3KL86GBeV/bugaOdJ1Aud+1SOFJreAAuvxzMMKT+cdQZk6oOPP148DA/No+mDm/2S43lcdCXh79wA6YRAmKQ8jmZxTCtPutrvuZK1rguvvUlEoG/vhdNHh7eDa4Td07V6bjCRPUl8qk/e4M0E3pwsTlZc="
143 "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOIgEteUEW06dnBHe2z8vNLwz2iMKe8bba6JgMmOUpcBAAAABHNzaDo= sean@framework16"
144 ];
145 };
146
147 # SSH
148 services.openssh = {
149 enable = true;
150 settings = {
151 PasswordAuthentication = false;
152 PermitRootLogin = "no";
153 };
154 };
155
156 # Accept unsigned store paths from local builds
157 nix.settings.require-sigs = false;
158
159 # Binary caches
160 nix.settings.substituters = [
161 "https://nixos-raspberrypi.cachix.org"
162 "https://seanaye.cachix.org"
163 ];
164 nix.settings.trusted-public-keys = [
165 "nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
166 "seanaye.cachix.org-1:0Qf3cZ1SwnTwqaiNGltYySksjGHnemzRPiodThnvibA="
167 ];
168
169 # Networking
170 networking.useDHCP = true;
171 security.sudo.wheelNeedsPassword = false;
172
173 # Firewall
174 networking.firewall.allowedTCPPorts = [
175 22 # SSH
176 8080 # Kodi web remote
177 ];
178
179 environment.systemPackages = [ myKodi ];
180
181 system.stateVersion = "24.11";
182}