From fe478e3153d40b93b0dee1356a38d70a1f1b0f19 Mon Sep 17 00:00:00 2001 From: Aleksandr Lebedev Date: Sat, 27 Sep 2025 22:30:53 +0200 Subject: [PATCH] Jellyfin --- flake.lock | 108 ++++++++++++++---- flake.nix | 5 + .../nixos/programs/sops/secrets/secrets.yaml | 5 +- .../stargate/services/jellyfin.nix | 66 +++++++++++ .../x86_64-linux/stargate/services/nginx.nix | 33 ++++++ 5 files changed, 190 insertions(+), 27 deletions(-) create mode 100644 systems/x86_64-linux/stargate/services/jellyfin.nix diff --git a/flake.lock b/flake.lock index 3e49bb6..1b30b6a 100644 --- a/flake.lock +++ b/flake.lock @@ -293,6 +293,28 @@ "type": "github" } }, + "declarative-jellyfin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_2", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1758815559, + "narHash": "sha256-OGfZ4GQxmiOeD0suq+QHm/0RA7gF6LsscN4igOqo2rY=", + "owner": "Sveske-Juice", + "repo": "declarative-jellyfin", + "rev": "c11f9003f04ee2ae684bdc989a37322f5615ed48", + "type": "github" + }, + "original": { + "owner": "Sveske-Juice", + "repo": "declarative-jellyfin", + "type": "github" + } + }, "deploy-rs": { "inputs": { "flake-compat": "flake-compat_5", @@ -946,7 +968,7 @@ }, "flake-utils_2": { "inputs": { - "systems": "systems_3" + "systems": "systems_4" }, "locked": { "lastModified": 1694529238, @@ -964,7 +986,7 @@ }, "flake-utils_3": { "inputs": { - "systems": "systems_4" + "systems": "systems_5" }, "locked": { "lastModified": 1731533236, @@ -997,7 +1019,7 @@ }, "flake-utils_5": { "inputs": { - "systems": "systems_8" + "systems": "systems_9" }, "locked": { "lastModified": 1694529238, @@ -1188,7 +1210,7 @@ "rose-pine-hyprcursor", "nixpkgs" ], - "systems": "systems_6" + "systems": "systems_7" }, "locked": { "lastModified": 1709914708, @@ -2073,7 +2095,7 @@ "stylix", "nixpkgs" ], - "treefmt-nix": "treefmt-nix" + "treefmt-nix": "treefmt-nix_2" }, "locked": { "lastModified": 1751320053, @@ -2219,7 +2241,7 @@ "plugin-vim-repeat": "plugin-vim-repeat", "plugin-vim-startify": "plugin-vim-startify", "plugin-which-key": "plugin-which-key", - "systems": "systems_5" + "systems": "systems_6" }, "locked": { "lastModified": 1736795850, @@ -4301,6 +4323,7 @@ "beeengine": "beeengine", "chaotic": "chaotic", "conduwuit": "conduwuit", + "declarative-jellyfin": "declarative-jellyfin", "deploy-rs": "deploy-rs", "desktopShell": "desktopShell", "dgop": "dgop_2", @@ -4570,7 +4593,7 @@ "gnome-shell": "gnome-shell", "nixpkgs": "nixpkgs_14", "nur": "nur", - "systems": "systems_9", + "systems": "systems_10", "tinted-foot": "tinted-foot", "tinted-kitty": "tinted-kitty", "tinted-schemes": "tinted-schemes", @@ -4607,7 +4630,7 @@ "type": "github" } }, - "systems_2": { + "systems_10": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -4622,6 +4645,20 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "id": "systems", + "type": "indirect" + } + }, "systems_3": { "locked": { "lastModified": 1681028828, @@ -4668,21 +4705,6 @@ } }, "systems_6": { - "locked": { - "lastModified": 1689347949, - "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", - "owner": "nix-systems", - "repo": "default-linux", - "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default-linux", - "type": "github" - } - }, - "systems_7": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -4697,6 +4719,21 @@ "type": "github" } }, + "systems_7": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, "systems_8": { "locked": { "lastModified": 1681028828, @@ -4809,6 +4846,27 @@ } }, "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "declarative-jellyfin", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1749194973, + "narHash": "sha256-eEy8cuS0mZ2j/r/FE0/LYBSBcIs/MKOIVakwHVuqTfk=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "a05be418a1af1198ca0f63facb13c985db4cb3c5", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { "inputs": { "nixpkgs": [ "stylix", @@ -4848,7 +4906,7 @@ }, "utils": { "inputs": { - "systems": "systems_2" + "systems": "systems_3" }, "locked": { "lastModified": 1731533236, @@ -4866,7 +4924,7 @@ }, "utils_2": { "inputs": { - "systems": "systems_7" + "systems": "systems_8" }, "locked": { "lastModified": 1710146030, diff --git a/flake.nix b/flake.nix index 7a47a18..3c14078 100644 --- a/flake.nix +++ b/flake.nix @@ -106,6 +106,10 @@ url = "github:ndom91/rose-pine-hyprcursor"; inputs.nixpkgs.follows = "nixpkgs"; }; + declarative-jellyfin = { + url = "github:Sveske-Juice/declarative-jellyfin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = inputs: let @@ -152,6 +156,7 @@ chaotic.nixosModules.nyx-registry lanzaboote.nixosModules.lanzaboote impermanence.nixosModules.impermanence + declarative-jellyfin.nixosModules.default ]; systems.hosts.kylekrein-framework12.modules = with inputs; [ diff --git a/modules/nixos/programs/sops/secrets/secrets.yaml b/modules/nixos/programs/sops/secrets/secrets.yaml index 30a5ade..a11b321 100644 --- a/modules/nixos/programs/sops/secrets/secrets.yaml +++ b/modules/nixos/programs/sops/secrets/secrets.yaml @@ -7,6 +7,7 @@ services: conduwuit: ENC[AES256_GCM,data:1shEq67QJTkeqrfYSr/eYG7gYWH//5ey6XQ=,iv:hy5wQmue8qU4ALfn9BrNQLnsTk8BsVVXY/8bDj18mXk=,tag:h6+hL0HjgSzd15Kc7Zg4ng==,type:str] nextcloud: ENC[AES256_GCM,data:YLRMhChTu/UQI+HIcUjNFFK+CfSCl2+0kfSkSfauAftRO2A1VHhyCjP5,iv:DLfhSvNRWXVU5XE3SwV4vZmAQI2ZVa+ak/g5Nu+Fgcg=,tag:K3nWfJRNxodeMkxGG3ljmg==,type:str] paperless: ENC[AES256_GCM,data:VjbEtwfY4T0Bpb+iutN7kDMqgcRy4ThQJiVyCHHT,iv:rlWB0ZfFYuKkpAfIzxryySH+Zl8hLf6c9UTjv1hVDVI=,tag:gHFoJZoKFOVupmE2VSJOoA==,type:str] + jellyfin: ENC[AES256_GCM,data:/a+Q7io2kDjXrchXJlAt2hmgTMRx+fwPyrHH4d9PW1qQcEfCMBf0Erbzkq9m3iikASwfWr/ROfFY28yNN55zGPxZVcS2RzCv3Y6RH3ECEMf0N6Kl9H8h1vOGK/GoNDFyb66jN9qCPSHzU91Lm7trMebOLauDgKSigx3U9E91cVpNF2H7J2Q/kQzBqjUk2+9d3gUAokGJwIn2hvqPuSGsUEareaBB9KNFLsOhY7EJmPmVIbEPpAPxr9eikjCpd+f1uY4=,iv:4MsYjE7RnI2Y/4okcnmeunNJh3Qz/hMWW0/1UBjXENg=,tag:y4n3v+L3163GJYVWolLKFA==,type:str] gitlab: dbPassword: ENC[AES256_GCM,data:itn9xyNZO+xkSk0GKvLzjLRzM0uZ+TalqLtj6tyjKXM=,iv:U8bX/On89wz6Lz4R2/fZ+FWRObehlnjFhUQdAhmxb60=,tag:oEbee14jCGfRs8i5bJZ5FA==,type:str] rootPassword: ENC[AES256_GCM,data:lXq+GIn6ooTzZL4iMYFzx3kn8gdcdsNaLQ/zVCr75Nw=,iv:mGp9gxL9uABpbod/ZNNyEllBbcfrQuFG4pQgs0v/xbk=,tag:CZzj4hauh/Qi8fvtmaZ/KQ==,type:str] @@ -55,7 +56,7 @@ sops: MU43ZWEwMXEwdGx5d0hUNlhiaGdjWU0K9UoNQOnMxTy0KdfiYOgm0TxH5qFUV3gi f7z2RzR44ndf0nHwIzr8e1bmF9q5mc685Wq9qyM7aLCE+yUU/vUO7Q== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-09-27T15:04:27Z" - mac: ENC[AES256_GCM,data:rc46drO7xZKo9ibkbOn88PXr+80zPFmnfxhFItWccg76vQWXSwUqVhz2dpwb5HUpkk1kxw0MBStNzSTXkOufvSq2LJeDSVb4SGAhxe4FuXqIfam/nGiviS9UKJkNw8fZe7a9hH7mf5SviD3rR60LYo0xaxAk89qWDVChF09L8MY=,iv:s/+ZBQM/4AV6TbNJVDU4xmovK6iz1HBugsCST7tQA04=,tag:YsaQBg/IQFqsL/uEg8fCpw==,type:str] + lastmodified: "2025-09-27T20:11:05Z" + mac: ENC[AES256_GCM,data:lZTCCM3bB6aEolUNLG5ZoxmdmaQeZWD+gxzheG+AX0HXHuqU2ZeuvzPRY1xFVQ2nQwHYaXJz5Suq6yQRM65bAX2VPpFo2knUoqVU0+dXDuzXpVCDvpMPGPsjU1uoPHGlkyuDISQF9jE1ekzXjK8wGx2hWMvFv4YuuuVkosv7bPQ=,iv:0DCa0VIEl0bUKaRYq1QSuu53VjBHngVgTCqUlzzdCDw=,tag:owfDKGdSitqZiAzgA+2IhQ==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 diff --git a/systems/x86_64-linux/stargate/services/jellyfin.nix b/systems/x86_64-linux/stargate/services/jellyfin.nix new file mode 100644 index 0000000..a6c3a56 --- /dev/null +++ b/systems/x86_64-linux/stargate/services/jellyfin.nix @@ -0,0 +1,66 @@ +{ + lib, + pkgs, + inputs, + namespace, + system, + target, + format, + virtual, + systems, + config, + ... +}: +with lib; +with lib.custom; { + sops.secrets."services/jellyfin" = { + owner = config.services.jellyfin.user; + group = config.services.jellyfin.group; + }; + services.declarative-jellyfin = { + enable = true; + openFirewall = true; + users = { + admin = { + mutable = false; + permissions.isAdministrator = true; + hashedPasswordFile = config.sops.secrets."services/jellyfin".path; + }; + }; + plugins = [ + { + name = "intro skipper"; + url = "https://github.com/intro-skipper/intro-skipper/releases/download/10.10/v1.10.10.19/intro-skipper-v1.10.10.19.zip"; + version = "1.10.10.19"; + targetAbi = "10.10.7.0"; # Required as intro-skipper doesn't provide a meta.json file + sha256 = "sha256:12hby8vkb6q2hn97a596d559mr9cvrda5wiqnhzqs41qg6i8p2fd"; + } + ]; + system = { + serverName = "Jellyfin Homeserver for Bees"; + # Use Hardware Acceleration for trickplay image generation + trickplayOptions = { + enableHwAcceleration = true; + enableHwEncoding = true; + }; + UICulture = "ru"; + }; + encoding = { + enableHardwareEncoding = true; + hardwareAccelerationType = "vaapi"; + enableDecodingColorDepth10Hevc = true; # enable if your system supports + allowHevcEncoding = true; # enable if your system supports + allowAv1Encoding = true; # enable if your system supports + hardwareDecodingCodecs = [ + # enable the codecs your system supports + "h264" + "hevc" + "mpeg2video" + "vc1" + "vp9" + "av1" + ]; + }; + }; + users.users.${config.services.jellyfin.user}.extraGroups = ["video" "render"]; +} diff --git a/systems/x86_64-linux/stargate/services/nginx.nix b/systems/x86_64-linux/stargate/services/nginx.nix index 475458d..7eac73a 100644 --- a/systems/x86_64-linux/stargate/services/nginx.nix +++ b/systems/x86_64-linux/stargate/services/nginx.nix @@ -85,6 +85,39 @@ in { proxyPass = "http://${cfg.address}:${builtins.toString cfg.port}"; }; }; + "jellyfin.kylekrein.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + ## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc. + client_max_body_size 20M; + + # Comment next line to allow TLSv1.0 and TLSv1.1 if you have very old clients + ssl_protocols TLSv1.3 TLSv1.2; + + # Security / XSS Mitigation Headers + add_header X-Content-Type-Options "nosniff"; + + # Permissions policy. May cause issues with some clients + add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), display-capture=(), document-domain=(), encrypted-media=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), payment=(), publickey-credentials-get=(), serial=(), sync-xhr=(), usb=(), xr-spatial-tracking=()" always; + + # Content Security Policy + add_header Content-Security-Policy "default-src https: data: blob: ; img-src 'self' https://* ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; font-src 'self'"; + ''; + + locations."/" = { + proxyPass = "http://127.0.0.1:8096"; + extraConfig = '' + # Disable buffering when the nginx proxy gets very resource heavy upon streaming + proxy_buffering off; + ''; + }; + + locations."/socket" = { + proxyPass = "http://127.0.0.1:8096"; + proxyWebsockets = true; + }; + }; }; };