waffentrager: init

This commit is contained in:
2026-06-24 00:06:21 +02:00
parent 29adb6e6b0
commit fb653eb0f4
30 changed files with 1325 additions and 11 deletions
@@ -0,0 +1,150 @@
{ lib, config, mkk, ... }:
{
options.waffentragerService.jellyfin.enable = mkk.lib.mkBoolOpt false "Enable jellyfin";
config =
let
cfg = config.waffentragerService.jellyfin;
in
lib.mkIf cfg.enable {
services.jellyfin = rec {
enable = true;
openFirewall = true;
user = "materus";
group = "nextcloud";
dataDir = config.waffentragerService.elements.jellyfinDir;
cacheDir = "${dataDir}/cache";
};
/*
services.jellyseerr = {
enable = true;
openFirewall = true;
};*/
services.nginx = {
appendHttpConfig = ''
map $request_uri $h264Level { ~(h264-level=)(.+?)& $2; }
map $request_uri $h264Profile { ~(h264-profile=)(.+?)& $2; }
'';
proxyCachePath."jellyfin" = {
enable = true;
maxSize = "1g";
levels = "1:2";
keysZoneName = "jellyfin";
keysZoneSize = "100m";
inactive = "1d";
useTempPath = false;
};
virtualHosts = {
"noot.materus.pl" = {
extraConfig = ''
client_max_body_size 20M;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "0"; # Do NOT enable. This is obsolete/dangerous
add_header X-Content-Type-Options "nosniff";
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;
'';
sslTrustedCertificate = "/var/lib/mnt_acme/materus.pl/chain.pem";
sslCertificateKey = "/var/lib/mnt_acme/materus.pl/key.pem";
sslCertificate = "/var/lib/mnt_acme/materus.pl/fullchain.pem";
addSSL = true;
http2 = false;
http3 = true;
locations."~ /Items/(.*)/Images" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
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_cache jellyfin;
proxy_cache_revalidate on;
proxy_cache_lock on;
'';
};
locations."~ ^/web/htmlVideoPlayer-plugin.[0-9a-z]+.chunk.js$" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_set_header Accept-Encoding "";
sub_filter_types *;
sub_filter 'return u=30' 'return u=600';
sub_filter 'return u=6' 'return u=60';
sub_filter 'maxBufferLength:u' 'maxBufferLength:u,maxBufferSize:180000000';
sub_filter_once on;
'';
};
locations."~* ^/Videos/(.*)/(?!live)" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
# Set size of a slice (this amount will be always requested from the backend by nginx)
# Higher value means more latency, lower more overhead
# This size is independent of the size clients/browsers can request
slice 2m;
proxy_cache jellyfin;
proxy_cache_valid 200 206 301 302 30d;
proxy_ignore_headers Expires Cache-Control Set-Cookie X-Accel-Expires;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_connect_timeout 15s;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Transmit slice range to the backend
proxy_set_header Range $slice_range;
# This saves bandwidth between the proxy and jellyfin, as a file is only downloaded one time instead of multiple times when multiple clients want to at the same time
# The first client will trigger the download, the other clients will have to wait until the slice is cached
# Esp. practical during SyncPlay
proxy_cache_lock on;
proxy_cache_lock_age 60s;
proxy_cache_key "jellyvideo$uri?MediaSourceId=$arg_MediaSourceId&VideoCodec=$arg_VideoCodec&AudioCodec=$arg_AudioCodec&AudioStreamIndex=$arg_AudioStreamIndex&VideoBitrate=$arg_VideoBitrate&AudioBitrate=$arg_AudioBitrate&SubtitleMethod=$arg_SubtitleMethod&TranscodingMaxAudioChannels=$arg_TranscodingMaxAudioChannels&RequireAvc=$arg_RequireAvc&SegmentContainer=$arg_SegmentContainer&MinSegments=$arg_MinSegments&BreakOnNonKeyFrames=$arg_BreakOnNonKeyFrames&h264-profile=$h264Profile&h264-level=$h264Level&slicerange=$slice_range";
'';
};
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_pass_request_headers 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-Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
'';
};
locations."/socket" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_pass_request_headers on;
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;
'';
};
};
};
};
};
}
@@ -0,0 +1,240 @@
{ config, pkgs, lib, mkk, ... }:
{
options.waffentragerService.scrobbling.enable = mkk.lib.mkBoolOpt false "Enable scrobbling";
config =
let
cfg = config.waffentragerService.scrobbling;
in
#### MALOJA --------------------------------------------------------------------
lib.mkIf cfg.enable {
sops.templates."maloja.env".content = ''
MALOJA_DATA_DIRECTORY=/data
MALOJA_DIRECTORY_STATE=/data/state
MALOJA_DIRECTORY_CACHE=/data/cache
MALOJA_SKIP_SETUP=yes
MALOJA_FORCE_PASSWORD=${config.sops.placeholder.maloja}
MALOJA_SPOTIFY_API_ID=${config.sops.placeholder.spotify-client-id}
MALOJA_SPOTIFY_API_SECRET=${config.sops.placeholder.spotify-client-secret}
MALOJA_NAME=Melody
MALOJA_WEEK_OFFSET=1
PUID=${builtins.toString config.users.users.scrobbler.uid}
PGID=${builtins.toString config.users.groups.scrobbler.gid}
TC=Europe/Warsaw
TIMEZONE=Europe/Warsaw
'';
services.nginx.virtualHosts = {
"melody.materus.pl" = {
sslTrustedCertificate = "/var/lib/mnt_acme/materus.pl/chain.pem";
sslCertificateKey = "/var/lib/mnt_acme/materus.pl/key.pem";
sslCertificate = "/var/lib/mnt_acme/materus.pl/fullchain.pem";
addSSL = true;
http2 = false;
http3 = true;
locations."/" = {
proxyPass = "http://127.0.0.1:42010";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
};
virtualisation.oci-containers.containers.maloja =
{
image = "krateng/maloja:latest";
ports = [
"42010:42010"
];
volumes = [
"${config.waffentragerService.elements.malojaDir}:/data"
];
environmentFiles = [
config.sops.templates."maloja.env".path
];
};
systemd.services."${config.virtualisation.oci-containers.backend}-maloja" =
let
malojaCfg = pkgs.writeText "settings.ini" ''[MALOJA]
directory_config = /data
lastfm_api_key = False
audiodb_api_key = False
spotify_api_id = False
spotify_api_secret = False
delimiters_feat = ["ft.","ft","feat.","feat","featuring","Ft.","Ft","Feat.","Feat","Featuring"]
delimiters_informal = ["vs.","vs","&","with"]
delimiters_formal = ["; ",";"]
metadata_providers = ["spotify","deezer","lastfm","audiodb","musicbrainz"]
'';
in
{
requires = [ "elements-mount.service" ];
after = [ "elements-mount.service" ];
preStart = ''cp --update=none ${malojaCfg} ${config.waffentragerService.elements.malojaDir}/settings.ini'';
};
#### MULTI SCROBBLER --------------------------------------------------------------------
users.groups.scrobbler = { gid = 3000; };
users.users.scrobbler = {
group = "scrobbler";
uid = 3000;
isSystemUser = true;
};
sops.templates."multi-scrobbler.env".content = ''
TC=Europe/Warsaw
CONFIG_DIR=/config
PUID=${builtins.toString config.users.users.scrobbler.uid}
PGID=${builtins.toString config.users.groups.scrobbler.gid}
'';
sops.templates."multi-scrobbler.json".owner = "scrobbler";
sops.templates."multi-scrobbler.json".group = "scrobbler";
sops.templates."multi-scrobbler.json".content = builtins.toJSON {
baseUrl = "https://scrobbler.materus.pl";
disableWeb = false;
debugMode = false;
sources = [
{
name = "materus-spotify";
enable = true;
clients = [ "maloja" ];
data = {
clientId = "${config.sops.placeholder.spotify-client-id}";
clientSecret = "${config.sops.placeholder.spotify-client-secret}";
redirectUri = "https://scrobbler.materus.pl/callback";
interval = 30;
};
type = "spotify";
}
{
name = "materus-jellyfin";
enable = true;
clients = [ "maloja" ];
data = {
users = [
"materus"
];
servers = [
"waffentrager"
];
};
options = {
logPayload = false;
logFilterFailure = "warn";
};
type = "jellyfin";
}
];
clients = [
{
name = "maloja";
enable = true;
data = {
url = "https://melody.materus.pl/";
apiKey = "${config.sops.placeholder.maloja-api}";
};
type = "maloja";
}
{
name = "materus-brainz";
enable = true;
configureAs = "client";
data = {
token = "${config.sops.placeholder.listenbrainz-api}";
username = "materus";
};
type = "listenbrainz";
}
{
name = "materus-lastfm";
enable = true;
configureAs = "client";
data = {
apiKey = "${config.sops.placeholder.lastfm-api}";
secret = "${config.sops.placeholder.lastfm-secret}";
redirectUri = "https://scrobbler.materus.pl/lastfm/callback";
};
type = "lastfm";
}
];
};
services.nginx.virtualHosts = {
"scrobbler.materus.pl" = {
sslTrustedCertificate = "/var/lib/mnt_acme/materus.pl/chain.pem";
sslCertificateKey = "/var/lib/mnt_acme/materus.pl/key.pem";
sslCertificate = "/var/lib/mnt_acme/materus.pl/fullchain.pem";
addSSL = true;
http2 = false;
http3 = true;
locations."/" = {
proxyPass = "http://127.0.0.1:42011";
extraConfig = ''
allow ${mkk.wireguard.ip-masks.main};
allow 192.168.100.0/24;
deny all;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
};
systemd.services."${config.virtualisation.oci-containers.backend}-multi-scrobbler" =
{
preStart = ''cp -f ${config.sops.templates."multi-scrobbler.json".path} ${config.waffentragerService.elements.malojaDir}/multi-scrobbler/config.json'';
requires = [ "elements-mount.service" ];
after = [ "elements-mount.service" ];
};
virtualisation.oci-containers.containers.multi-scrobbler = {
image = "foxxmd/multi-scrobbler:latest";
ports = [
"127.0.0.1:42011:9078"
];
volumes = [
"${config.waffentragerService.elements.malojaDir}/multi-scrobbler:/config"
];
environmentFiles = [
config.sops.templates."multi-scrobbler.env".path
];
};
};
}