diff --git a/hosts/tsumugi/arr/default.nix b/hosts/tsumugi/arr/default.nix index 06868d7..c1fee10 100644 --- a/hosts/tsumugi/arr/default.nix +++ b/hosts/tsumugi/arr/default.nix @@ -11,6 +11,7 @@ ./radarrAnime.nix ./sonarrAnime.nix ./sonarrMain.nix + ./shelfarr.nix ]; sops.secrets."tsumugi/autobrrSecret".restartUnits = [ "autobrr.service" ]; @@ -37,7 +38,7 @@ }; transcoding = { enableHardwareEncoding = true; - enableIntelLowPowerEncoding = true; + enableIntelLowPowerEncoding = false; maxConcurrentStreams = 6; # https://www.intel.com/content/www/us/en/support/articles/000098345/graphics.html hardwareDecodingCodecs = { @@ -100,13 +101,6 @@ }; }; }; - systemd.services = { - bandcampsync.after = [ "mnt-media.mount" ]; - jellyfin.after = [ "mnt-media.mount" ]; - radarr.after = [ "mnt-media.mount" ]; - sonarr.after = [ "mnt-media.mount" ]; - plex.after = [ "mnt-media.mount" ]; - }; services.autobrr = { enable = true; @@ -121,6 +115,24 @@ oidcDisableBuiltInLogin = true; }; }; - systemd.services.autobrr.serviceConfig.EnvironmentFile = - config.sops.secrets."tsumugi/autobrrEnv".path; + + systemd.services = lib.mkMerge [ + { + autobrr.serviceConfig.EnvironmentFile = config.sops.secrets."tsumugi/autobrrEnv".path; + } + (builtins.listToAttrs ( + map + (name: { + inherit name; + value.after = [ "mnt-media.mount" ]; + }) + [ + "bandcampsync" + "jellyfin" + "radarr" + "sonarr" + "plex" + ] + )) + ]; } diff --git a/hosts/tsumugi/arr/shelfarr.nix b/hosts/tsumugi/arr/shelfarr.nix new file mode 100644 index 0000000..9bf8f3e --- /dev/null +++ b/hosts/tsumugi/arr/shelfarr.nix @@ -0,0 +1,105 @@ +{ + inputs, + config, + pkgs, + lib, + ... +}: +let + cfg = config.virtualisation.oci-containers.containers; + version = "2026.08.24.1"; + libationBooksPath = "/mnt/media/data/appdata/libation/books"; + shelfarrState = "/var/lib/shelfarr"; + libationState = "/var/lib/shelfarr-libation"; +in +{ + sops.secrets.oci = { }; + systemd = { + services.shelfarr.after = [ "mnt-media.mount" ]; + tmpfiles.rules = [ + "d ${shelfarrState}/data 0700 root root -" + "d ${libationState}/config 0700 root root -" + "d ${libationState}/control 0700 root root -" + ]; + }; + virtualisation.oci-containers.containers = { + shelfarr = + let + tokenFolder = "/run/shelfarr-libation"; + in + { + autoStart = true; + serviceName = "shelfarr"; + hostname = "shelfarr"; + image = "ghcr.io/pedro-revez-silva/shelfarr:${version}"; + environment = { + SOLID_QUEUE_IN_PUMA = "1"; + SHELFARR_LIBATION_URL = "http://shelfarr-libation:8080"; + SHELFARR_LIBATION_TOKEN_FILE = "${tokenFolder}/token"; + SHELFARR_LIBATION_IMPORT_ROOT = "/imports/libation"; + # Keep these identical to the companion so both services can use the + # read-only token and completed-backup volumes. + PUID = "1000"; + PGID = "1000"; + CHOWN_ON_START = "auto"; + TZ = "Europe/Dublin"; + # Optional: Change internal port (default: 80) + # HTTP_PORT=8080; + # Optional: Set your own key instead of auto-generating + # RAILS_MASTER_KEY=your-64-char-hex-key; + }; + volumes = [ + # Database and app storage (required - contains auto-generated secret key) + "${shelfarrState}/data:/rails/storage" + # Audiobooks output folder (where Shelfarr places completed audiobooks) + "/mnt/media/data/media/audiobooks:/audiobooks" + # Ebooks output folder (where Shelfarr places completed ebooks) + "/mnt/media/data/media/ebooks:/ebooks" + # Download client completed folder (where downloads land before processing) + "/mnt/media/data/appdata/shelfarr/downloads:/downloads" + # Completed Audible backups from the optional Libation companion + "${libationBooksPath}:${cfg.shelfarr.environment.SHELFARR_LIBATION_IMPORT_ROOT}:ro" + # Read-only bridge credential generated by the companion + "${libationState}/control:${tokenFolder}:ro" + ]; + ports = [ "127.0.0.1:5056:80" ]; + }; + shelfarr-libation = + let + tokenFolder = "/control"; + in + { + autoStart = true; + serviceName = "shelfarr-libation"; + hostname = "shelfarr-libation"; + dependsOn = [ "shelfarr" ]; + image = "ghcr.io/pedro-revez-silva/shelfarr-libation:${version}"; + environment = rec { + inherit (cfg.shelfarr.environment) + PUID + PGID + CHOWN_ON_START + TZ + ; + LIBATION_FILES_DIR = "/config"; + LIBATION_BOOKS_DIR = "/data"; + LIBATION_IN_PROGRESS_DIR = "${LIBATION_FILES_DIR}/in-progress"; + COMPANION_STATE_DIR = "${LIBATION_FILES_DIR}/shelfarr-companion"; + COMPANION_TOKEN_FILE = "${tokenFolder}/token"; + COMPANION_MAX_ACTIVE_JOBS = "500"; + COMPANION_MAX_TERMINAL_JOBS = "5000"; + COMPANION_TERMINAL_JOB_RETENTION_DAYS = "30"; + ASPNETCORE_URLS = "http://0.0.0.0:8080"; + }; + volumes = [ + # Private account tokens, device keys, Libation database, and job state + "${libationState}/config:${cfg.shelfarr-libation.environment.LIBATION_FILES_DIR}" + # Completed backups shared with Shelfarr + "${libationBooksPath}:${cfg.shelfarr-libation.environment.LIBATION_BOOKS_DIR}" + # Narrow credential handoff: Shelfarr cannot see the private state volume + "${libationState}/control:${tokenFolder}" + ]; + ports = [ "127.0.0.1:5057:8080" ]; + }; + }; +} diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index f109260..4b6e3e7 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -2,6 +2,7 @@ #ENC[AES256_GCM,data:l4jl9mtPulDpv+9U4BgTOmrfe0kmvI+6FVh9bxF50IRwS3DSk1OfphPxNhhWC7GeRW2x7YvZesU=,iv:8nfh3eGloOCeH76tQijHOdnkFUhXfjeHKWOLOau7mGs=,tag:uJRDAITtdxt2b3GOC9Z/2g==,type:comment] #ENC[AES256_GCM,data:eUQCB4+jpMkMHb9oYEmN/4j4DniOhRIjD39W4Pqwkr8WGdrPu1mwf6Y/4k24P5k7m4Sr6VkTio5zySDpHl11qxg+lsNlBE8O,iv:fzpp6bDyQAsYyG3FRn/gu7TlzdZQNaWOFxpvKhhvO90=,tag:KmLeJq/k8mSHMs8YulcS7Q==,type:comment] git: ENC[AES256_GCM,data:sUhHS+P82ySkqTuCy9nhSPoQV0TpswSPEsA1UtnscJMgT/0S0jDvrXrVGPFbsyP5hE892WAdUjwVDYQrn+81YF/+mK+iRTBaYUI8iQ3oZVSwEtaMQrjeqG8+89aT,iv:AQ+oQnphXn5BNEHJxa7IbAnOYcQqCTYG3q8zd7wawaM=,tag:v77FXCUEnULnlrltvFQHbw==,type:str] +oci: ENC[AES256_GCM,data:4qpjpISDgSG8o1K5CWvqsoCD2Spn3J9EYPW63w2K7Q5ECnAN92wVoQ==,iv:rAhjWg9JLrrzJCOnqLUImwWbEb68Dve0nktNCp+1NFs=,tag:uySIiO3NGdGeTYrmlxnddQ==,type:str] benisuzume: cifs: ENC[AES256_GCM,data:hjy635OH2DXoqoW2v4XZ/xBE2ZajwLCK1zIaQ/FUQNlUYd4c04i/UfQ=,iv:kk6HUN9FMf259ImEMj7b1Utw+13CiQBqAMBkj1c0yfk=,tag:3DZl94VeSoQA1Wxs+QMaHw==,type:str] tsumugi: @@ -78,7 +79,7 @@ sops: JFaroA1RPAmhCC0nSl4seBSIuRl8QrPjfcBJy4mFycll5Mp/0/WwLA== -----END AGE ENCRYPTED FILE----- recipient: age1cpqgsevvfgk0nnmxacsdcwsrkefd0xcx7wkdlyaqj8vgvfvnm4rq4npjv6 - lastmodified: "2026-08-20T13:10:43Z" - mac: ENC[AES256_GCM,data:KI0CrrlltBxKS46AxcGNsPsrgc0Njua2gmF7sX+z6hKi5U/JEZQzwnHGB0SCvDDTdds6lT6/G3L7URv0rEqP+ih0kgsWVs7ICUSbYV5X8YzcnwYdQTvOAN5iDidbnddgIbyUWP25Yn9xbaXZIjyKK5JLBmZ6pfUGfTSjVIfoxS8=,iv:9doFqYd5J+ds2uotiNZWEADJqz+ejSkb9aqxAj+USnY=,tag:vGlCMZuG3LncyiELqiqFsA==,type:str] + lastmodified: "2026-08-28T16:50:37Z" + mac: ENC[AES256_GCM,data:eMhgA5nyUNsKrKSIMBbeoHLWoWQ5GxViI7OsbU6QRJqjvSN45vOFLTMtefNP3MZz77dolzNDuGGR7OH+4ouzm5diu2Cwxd+GW3HxnujDu56G1cnQucRWzPfnRiZ23HhGFvfkIEL5ZqbBrt31n6s8v7GNzbRegnJjb9DcAT1JFlk=,iv:cIZ0cd+2eO/+cW6Rog+NE24cmd10o1QR2XvsD6OsPlA=,tag:SJaCJsLOrgr+7bPbPczj3g==,type:str] unencrypted_suffix: _unencrypted version: 3.13.3