diff --git a/devices/nos/default.nix b/devices/nos/default.nix index b3b7bf8..65930c1 100644 --- a/devices/nos/default.nix +++ b/devices/nos/default.nix @@ -2,6 +2,7 @@ {...}: { imports = [ ./modules/arion + ./modules/jellyfin ./modules/mergerfs.nix ./modules/qbittorrent ]; diff --git a/devices/nos/modules/jellyfin/default.nix b/devices/nos/modules/jellyfin/default.nix new file mode 100644 index 0000000..dee1879 --- /dev/null +++ b/devices/nos/modules/jellyfin/default.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + ... +}: let + inherit (lib) hasAttr fileContents optionals; + inherit (config.vars) mainUser; + + optionalGroup = name: + optionals + (hasAttr name config.users.groups) + [config.users.groups.${name}.name]; +in { + imports = [ + ./jfa-go.nix + ./packages.nix + ]; + + users.users."jellyfin".extraGroups = + optionalGroup mainUser + ++ optionalGroup "input" + ++ optionalGroup "media" + ++ optionalGroup "render"; + + services = { + jellyfin.enable = true; + + nginx = { + enable = true; + config = fileContents ./nginx.conf; + }; + }; +} diff --git a/devices/nos/modules/jellyfin/images/jfa-go.nix b/devices/nos/modules/jellyfin/images/jfa-go.nix new file mode 100644 index 0000000..3854e0b --- /dev/null +++ b/devices/nos/modules/jellyfin/images/jfa-go.nix @@ -0,0 +1,8 @@ +pkgs: +pkgs.dockerTools.pullImage { + imageName = "hrfee/jfa-go"; + imageDigest = "sha256:e50d74379d91f9389afcd7db6bc4542ad2b1869f4af69c7f9fb5f9c02e7957da"; + sha256 = "02v0p4yrp4gjm88mqvdasaslfl51r194m6fj08bmq16bm6zz1n9l"; + finalImageName = "hrfee/jfa-go"; + finalImageTag = "unstable"; +} diff --git a/devices/nos/modules/jellyfin/jfa-go.nix b/devices/nos/modules/jellyfin/jfa-go.nix new file mode 100644 index 0000000..710fd22 --- /dev/null +++ b/devices/nos/modules/jellyfin/jfa-go.nix @@ -0,0 +1,15 @@ +{...}: { + systemd.services."arion-jfa-go".after = ["jellyfin.service"]; + + arion.projects."jfa-go"."jfa-go" = { + image = ./images/jfa-go.nix; + restart = "always"; + + ports = ["8056:8056"]; + + volumes = [ + "/var/lib/jellyfin/jfa-go:/data" + "/etc/localtime:/etc/localtime:ro" + ]; + }; +} diff --git a/devices/nos/modules/jellyfin/nginx.conf b/devices/nos/modules/jellyfin/nginx.conf new file mode 100644 index 0000000..3019caf --- /dev/null +++ b/devices/nos/modules/jellyfin/nginx.conf @@ -0,0 +1,96 @@ +events { + worker_connections 1024; +} + +http { + # Must be in HTTP block + # Set in-memory cache-metadata size in keys_zone, size of video caching and how many days a cached object should persist + proxy_cache_path /var/cache/nginx/jellyfin-videos levels=1:2 keys_zone=jellyfin-videos:100m inactive=90d max_size=35000m; + map $request_uri $h264Level { + ~(h264-level=)(.+?)& $2; + } + map $request_uri $h264Profile { + ~(h264-profile=)(.+?)& $2; + } + + server { + listen 8097; + listen [::]:8097; + server_name jelly.nelim.org; + + ## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc. + client_max_body_size 20M; + + # use a variable to store the upstream proxy + set $jellyfin 10.0.0.249; + + location = / { + return 302 https://$host/web/; + } + + location / { + # Proxy main Jellyfin traffic + proxy_pass http://$jellyfin:8096; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Protocol $scheme; + proxy_set_header X-Forwarded-Host $http_host; + + # Disable buffering when the nginx proxy gets very resource heavy upon streaming + proxy_buffering off; + } + + # location block for /web - This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/ + location = /web/ { + # Proxy main Jellyfin traffic + proxy_pass http://$jellyfin:8096/web/index.html; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Protocol $scheme; + proxy_set_header X-Forwarded-Host $http_host; + } + + location /socket { + # Proxy Jellyfin Websockets traffic + proxy_pass http://$jellyfin:8096; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Protocol $scheme; + proxy_set_header X-Forwarded-Host $http_host; + } + + location /accounts { + # No longer necessary on versions after v0.3.0 + # rewrite ^/accounts/(.*) /$1 break; + + # Remove the CSP header set for Jellyfin + proxy_hide_header Content-Security-Policy; + add_header Content-Security-Policy ""; + + proxy_pass http://localhost:8056/accounts; # Change as you need + + # For versions <= v0.3.0 + #proxy_pass http://localhost:8056; # Change as you need + + http2_push_preload on; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Protocol $scheme; + proxy_set_header X-Forwarded-Host $http_host; + proxy_buffering off; + } + + } +} diff --git a/devices/nos/modules/jellyfin/packages.nix b/devices/nos/modules/jellyfin/packages.nix new file mode 100644 index 0000000..66a56c4 --- /dev/null +++ b/devices/nos/modules/jellyfin/packages.nix @@ -0,0 +1,24 @@ +{ + pkgs, + lib, + ... +}: let + inherit (lib) hasAttr optionals; +in { + environment.systemPackages = [ + pkgs.jellyfin + pkgs.jellyfin-ffmpeg + + (pkgs.jellyfin-web.overrideAttrs (_: o: { + patches = + [ + (pkgs.fetchpatch { + name = "skipintro.patch"; + url = "https://pastebin.com/raw/EEgvReaw"; + hash = "sha256-kfvOz0ukDY09kkbmZi24ch5KWJsVcThNEVnjlk4sAC0="; + }) + ] + ++ optionals (hasAttr "patches" o) o.patches; + })) + ]; +}