diff --git a/hosts/jupiter/users/storm/configs/console/podman/media/default.nix b/hosts/jupiter/users/storm/configs/console/podman/media/default.nix
index a98a907..7541386 100644
--- a/hosts/jupiter/users/storm/configs/console/podman/media/default.nix
+++ b/hosts/jupiter/users/storm/configs/console/podman/media/default.nix
@@ -15,6 +15,43 @@ let
inherit (hmConfig.virtualisation.quadlet) containers volumes networks;
jellyfinAutheliaClientId = "59TRpNutxEeRRCAZbDsK7rsnrA5NC69HAdAO45CEfc740xl4hgIacDy2u03oiFc89Exb67udBQvmfwxgeAQtJPiNAJxA5OzGmdQf";
+
+ mkApp = type: name: shortName: urlBase: mediaFolderBase: {
+ inherit
+ type
+ name
+ shortName
+ urlBase
+ mediaFolderBase
+ ;
+ };
+
+ arrs = [
+ (mkApp "radarr" "Radarr" "radarr" "/manage/films" "/films")
+ (mkApp "radarr" "Radarr (UHD)" "radarr-uhd" "/manage/films/uhd" "/films")
+ (mkApp "radarr" "Radarr (Anime)" "radarr-anime" "/manage/anime/films" "/anime/films")
+ (mkApp "sonarr" "Sonarr" "sonarr" "/manage/shows" "/shows")
+ (mkApp "sonarr" "Sonarr (UHD)" "sonarr-uhd" "/manage/shows/uhd" "/shows")
+ (mkApp "sonarr" "Sonarr (Anime)" "sonarr-anime" "/manage/anime/shows" "/anime/shows")
+ ];
+
+ arrMapping = {
+ radarr = {
+ port = 7878;
+ prowlarr = {
+ implementation = "Radarr";
+ configContract = "RadarrSettings";
+ };
+ };
+
+ sonarr = {
+ port = 8989;
+ prowlarr = {
+ implementation = "Sonarr";
+ configContract = "SonarrSettings";
+ };
+ };
+ };
in
{
home-manager.users.${user} = {
@@ -27,233 +64,116 @@ in
];
sops = {
- secrets = {
- "jellyfin/admin".sopsFile = ../../../../../../secrets/secrets.yaml;
- "jellyfin/authelia/password".sopsFile = ../../../../../../secrets/secrets.yaml;
- "jellyfin/authelia/digest".sopsFile = ../../../../../../secrets/secrets.yaml;
+ secrets =
+ {
+ "jellyfin/admin".sopsFile = ../../../../../../secrets/secrets.yaml;
+ "jellyfin/authelia/password".sopsFile = ../../../../../../secrets/secrets.yaml;
+ "jellyfin/authelia/digest".sopsFile = ../../../../../../secrets/secrets.yaml;
- "prowlarr/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
+ "prowlarr/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
+ }
+ // builtins.listToAttrs (
+ builtins.map (arr: {
+ name = "${arr.shortName}/apiKey";
+ value.sopsFile = ../../../../../../secrets/secrets.yaml;
+ }) arrs
+ );
- "radarr/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
- "radarr-uhd/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
- "radarr-anime/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
- "sonarr/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
- "sonarr-uhd/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
- "sonarr-anime/apiKey".sopsFile = ../../../../../../secrets/secrets.yaml;
- };
+ templates =
+ {
+ jellyfin-env.content = ''
+ JELLYFIN_ADMIN_PASSWORD=${hmConfig.sops.placeholder."jellyfin/admin"}
+ JELLYFIN_OIDC_SECRET=${hmConfig.sops.placeholder."jellyfin/authelia/password"}
+ '';
- templates = {
- jellyfin-env.content = ''
- JELLYFIN_ADMIN_PASSWORD=${hmConfig.sops.placeholder."jellyfin/admin"}
- JELLYFIN_OIDC_SECRET=${hmConfig.sops.placeholder."jellyfin/authelia/password"}
- '';
+ authelia-jellyfin.content = builtins.readFile (
+ (pkgs.formats.yaml { }).generate "jellyfin.yaml" {
+ identity_providers.oidc = {
+ authorization_policies.jellyfin = {
+ default_policy = "deny";
+ rules = [
+ {
+ policy = "one_factor";
+ subject = "group:jellyfin";
+ }
+ ];
+ };
- authelia-jellyfin.content = builtins.readFile (
- (pkgs.formats.yaml { }).generate "jellyfin.yaml" {
- identity_providers.oidc = {
- authorization_policies.jellyfin = {
- default_policy = "deny";
- rules = [
+ clients = [
{
- policy = "one_factor";
- subject = "group:jellyfin";
+ client_id = jellyfinAutheliaClientId;
+ client_name = "Jellyfin";
+ client_secret = hmConfig.sops.placeholder."jellyfin/authelia/digest";
+ redirect_uris = [ "https://media.karaolidis.com/sso/OID/redirect/authelia" ];
+ authorization_policy = "jellyfin";
+ require_pkce = true;
+ pkce_challenge_method = "S256";
+ scopes = [
+ "openid"
+ "profile"
+ "groups"
+ ];
+ token_endpoint_auth_method = "client_secret_post";
}
];
};
+ }
+ );
- clients = [
- {
- client_id = jellyfinAutheliaClientId;
- client_name = "Jellyfin";
- client_secret = hmConfig.sops.placeholder."jellyfin/authelia/digest";
- redirect_uris = [ "https://media.karaolidis.com/sso/OID/redirect/authelia" ];
- authorization_policy = "jellyfin";
- require_pkce = true;
- pkce_challenge_method = "S256";
- scopes = [
- "openid"
- "profile"
- "groups"
- ];
- token_endpoint_auth_method = "client_secret_post";
+ prowlarr-env.content = ''
+ API_KEY=${hmConfig.sops.placeholder."prowlarr/apiKey"}
+ '';
+ }
+ // builtins.listToAttrs (
+ builtins.map (arr: {
+ name = "${arr.shortName}-env";
+ value.content = ''
+ API_KEY=${hmConfig.sops.placeholder."${arr.shortName}/apiKey"}
+ '';
+ }) arrs
+ )
+ // builtins.listToAttrs (
+ builtins.map (arr: {
+ name = "prowlarr-${arr.shortName}";
+ value.content = builtins.readFile (
+ (pkgs.formats.json { }).generate "${arr.shortName}.json" {
+ enable = true;
+ name = arr.name;
+ inherit (arrMapping.${arr.type}.prowlarr) implementation configContract;
+ syncLevel = "fullSync";
+ fields = [
+ {
+ name = "prowlarrUrl";
+ value = "http://prowlarr:9696";
+ }
+ {
+ name = "baseUrl";
+ value = "http://${arr.shortName}:${builtins.toString arrMapping.${arr.type}.port}";
+ }
+ {
+ name = "apiKey";
+ value = hmConfig.sops.placeholder."${arr.shortName}/apiKey";
+ }
+ ];
+ }
+ );
+ }) arrs
+ )
+ // builtins.listToAttrs (
+ builtins.map (arr: {
+ name = "recyclarr-${arr.shortName}";
+ value.content = builtins.readFile (
+ (pkgs.formats.yaml { }).generate "${arr.shortName}.yaml" (
+ import ./recyclarr/${arr.shortName}.nix {
+ base_url = "http://${arr.shortName}:${
+ builtins.toString arrMapping.${arr.type}.port
+ }${arr.urlBase}/";
+ api_key = hmConfig.sops.placeholder."${arr.shortName}/apiKey";
}
- ];
- };
- }
+ )
+ );
+ }) arrs
);
-
- prowlarr-env.content = ''
- PROWLARR_API_KEY=${hmConfig.sops.placeholder."prowlarr/apiKey"}
- '';
-
- prowlarr-radarr.content = builtins.readFile (
- (pkgs.formats.json { }).generate "radarr.json" {
- enable = true;
- name = "Radarr";
- implementation = "Radarr";
- configContract = "RadarrSettings";
- syncLevel = "fullSync";
- fields = [
- {
- name = "prowlarrUrl";
- value = "http://prowlarr:9696";
- }
- {
- name = "baseUrl";
- value = "http://radarr:7878";
- }
- {
- name = "apiKey";
- value = hmConfig.sops.placeholder."radarr/apiKey";
- }
- ];
- }
- );
-
- prowlarr-radarr-uhd.content = builtins.readFile (
- (pkgs.formats.json { }).generate "radarr-uhd.json" {
- enable = true;
- name = "Radarr (UHD)";
- implementation = "Radarr";
- configContract = "RadarrSettings";
- syncLevel = "fullSync";
- fields = [
- {
- name = "prowlarrUrl";
- value = "http://prowlarr:9696";
- }
- {
- name = "baseUrl";
- value = "http://radarr-uhd:7878";
- }
- {
- name = "apiKey";
- value = hmConfig.sops.placeholder."radarr-uhd/apiKey";
- }
- ];
- }
- );
-
- prowlarr-radarr-anime.content = builtins.readFile (
- (pkgs.formats.json { }).generate "radarr-anime.json" {
- enable = true;
- name = "Radarr (Anime)";
- implementation = "Radarr";
- configContract = "RadarrSettings";
- syncLevel = "fullSync";
- fields = [
- {
- name = "prowlarrUrl";
- value = "http://prowlarr:9696";
- }
- {
- name = "baseUrl";
- value = "http://radarr-anime:7878";
- }
- {
- name = "apiKey";
- value = hmConfig.sops.placeholder."radarr-anime/apiKey";
- }
- ];
- }
- );
-
- prowlarr-sonarr.content = builtins.readFile (
- (pkgs.formats.json { }).generate "sonarr.json" {
- enable = true;
- name = "Sonarr";
- implementation = "Sonarr";
- configContract = "SonarrSettings";
- syncLevel = "fullSync";
- fields = [
- {
- name = "prowlarrUrl";
- value = "http://prowlarr:9696";
- }
- {
- name = "baseUrl";
- value = "http://sonarr:8989";
- }
- {
- name = "apiKey";
- value = hmConfig.sops.placeholder."sonarr/apiKey";
- }
- ];
- }
- );
-
- prowlarr-sonarr-uhd.content = builtins.readFile (
- (pkgs.formats.json { }).generate "sonarr-uhd.json" {
- enable = true;
- name = "Sonarr (UHD)";
- implementation = "Sonarr";
- configContract = "SonarrSettings";
- syncLevel = "fullSync";
- fields = [
- {
- name = "prowlarrUrl";
- value = "http://prowlarr:9696";
- }
- {
- name = "baseUrl";
- value = "http://sonarr-uhd:8989";
- }
- {
- name = "apiKey";
- value = hmConfig.sops.placeholder."sonarr-uhd/apiKey";
- }
- ];
- }
- );
-
- prowlarr-sonarr-anime.content = builtins.readFile (
- (pkgs.formats.json { }).generate "sonarr-anime.json" {
- enable = true;
- name = "Sonarr (Anime)";
- implementation = "Sonarr";
- configContract = "SonarrSettings";
- syncLevel = "fullSync";
- fields = [
- {
- name = "prowlarrUrl";
- value = "http://prowlarr:9696";
- }
- {
- name = "baseUrl";
- value = "http://sonarr-anime:8989";
- }
- {
- name = "apiKey";
- value = hmConfig.sops.placeholder."sonarr-anime/apiKey";
- }
- ];
- }
- );
-
- radarr-env.content = ''
- RADARR_API_KEY=${hmConfig.sops.placeholder."radarr/apiKey"}
- '';
-
- radarr-uhd-env.content = ''
- RADARR_API_KEY=${hmConfig.sops.placeholder."radarr-uhd/apiKey"}
- '';
-
- radarr-anime-env.content = ''
- RADARR_API_KEY=${hmConfig.sops.placeholder."radarr-anime/apiKey"}
- '';
-
- sonarr-env.content = ''
- SONARR_API_KEY=${hmConfig.sops.placeholder."sonarr/apiKey"}
- '';
-
- sonarr-uhd-env.content = ''
- SONARR_API_KEY=${hmConfig.sops.placeholder."sonarr-uhd/apiKey"}
- '';
-
- sonarr-anime-env.content = ''
- SONARR_API_KEY=${hmConfig.sops.placeholder."sonarr-anime/apiKey"}
- '';
- };
};
virtualisation.quadlet = {
@@ -263,457 +183,232 @@ in
flaresolverr = { };
};
- volumes = {
- jellyfin-config = { };
- jellyfin-data = { };
- jellyfin-log = { };
- jellyfin-cache = { };
+ volumes =
+ {
+ jellyfin-config = { };
+ jellyfin-data = { };
+ jellyfin-log = { };
+ jellyfin-cache = { };
- prowlarr = { };
+ prowlarr = { };
+ }
+ // builtins.listToAttrs (
+ builtins.map (arr: {
+ name = arr.shortName;
+ value = { };
+ }) arrs
+ );
- radarr = { };
- radarr-uhd = { };
- radarr-anime = { };
- sonarr = { };
- sonarr-uhd = { };
- sonarr-anime = { };
- };
+ containers =
+ {
+ jellyfin = {
+ containerConfig = {
+ image = "docker-archive:${selfPkgs.docker-jellyfin}";
+ networks = [
+ networks.jellyfin.ref
+ networks.traefik.ref
+ ];
+ volumes =
+ let
+ setup = pkgs.writeTextFile {
+ name = "setup.sh";
+ executable = true;
+ text = builtins.readFile ./jellyfin/setup.sh;
+ };
+ in
+ [
+ "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
+ "${setup}:/etc/jellyfin/setup.sh:ro"
+ "${./jellyfin/libraries}:/etc/jellyfin/libraries:ro"
+ "${volumes.jellyfin-config.ref}:/etc/jellyfin"
+ "${volumes.jellyfin-data.ref}:/var/lib/jellyfin"
+ "${volumes.jellyfin-log.ref}:/var/log/jellyfin"
+ "${volumes.jellyfin-cache.ref}:/tmp/jellyfin"
+ ];
+ environments.JELLYFIN_OIDC_CLIENT_ID = jellyfinAutheliaClientId;
+ environmentFiles = [ hmConfig.sops.templates.jellyfin-env.path ];
+ labels = [
+ "traefik.enable=true"
+ "traefik.http.routers.jellyfin.rule=Host(`media.karaolidis.com`)"
+ ];
+ podmanArgs = [ "--cdi-spec-dir=/run/cdi" ];
+ devices = [ "nvidia.com/gpu=all" ];
+ };
- containers = {
- jellyfin = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-jellyfin}";
- networks = [
- networks.jellyfin.ref
- networks.traefik.ref
- ];
- volumes =
+ unitConfig =
let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./jellyfin/setup.sh;
- };
+ dependencies = [ "sops-nix.service" ];
in
- [
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- "${setup}:/etc/jellyfin/setup.sh:ro"
- "${./jellyfin/libraries}:/etc/jellyfin/libraries:ro"
- "${volumes.jellyfin-config.ref}:/etc/jellyfin"
- "${volumes.jellyfin-data.ref}:/var/lib/jellyfin"
- "${volumes.jellyfin-log.ref}:/var/log/jellyfin"
- "${volumes.jellyfin-cache.ref}:/tmp/jellyfin"
- ];
- environments.JELLYFIN_OIDC_CLIENT_ID = jellyfinAutheliaClientId;
- environmentFiles = [ hmConfig.sops.templates.jellyfin-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`media.karaolidis.com`)"
- ];
- podmanArgs = [ "--cdi-spec-dir=/run/cdi" ];
- devices = [ "nvidia.com/gpu=all" ];
+ {
+ After = dependencies;
+ Requires = dependencies;
+ };
};
-
- unitConfig =
- let
- dependencies = [ "sops-nix.service" ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- flaresolverr.containerConfig = {
- image = "docker-archive:${selfPkgs.docker-flaresolverr}";
- networks = [ networks.flaresolverr.ref ];
- };
-
- prowlarr = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-prowlarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.flaresolverr.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./prowlarr/setup.sh;
- };
-
- postStart = pkgs.writeTextFile {
- name = "post-start.sh";
- executable = true;
- text = builtins.readFile ./prowlarr/post-start.sh;
- };
- in
- [
- "${setup}:/etc/prowlarr/setup.sh:ro"
- "${postStart}:/etc/prowlarr/post-start.sh:ro"
- "${./prowlarr/indexers}:/etc/prowlarr/indexers:ro"
- "${hmConfig.sops.templates.prowlarr-radarr.path}:/etc/prowlarr/apps/radarr.json:ro"
- "${hmConfig.sops.templates.prowlarr-radarr-uhd.path}:/etc/prowlarr/apps/radarr-uhd.json:ro"
- "${hmConfig.sops.templates.prowlarr-radarr-anime.path}:/etc/prowlarr/apps/radarr-anime.json:ro"
- "${hmConfig.sops.templates.prowlarr-sonarr.path}:/etc/prowlarr/apps/sonarr.json:ro"
- "${hmConfig.sops.templates.prowlarr-sonarr-uhd.path}:/etc/prowlarr/apps/sonarr-uhd.json:ro"
- "${hmConfig.sops.templates.prowlarr-sonarr-anime.path}:/etc/prowlarr/apps/sonarr-anime.json:ro"
- "${volumes.prowlarr.ref}:/var/lib/prowlarr"
- ];
- environments.PROWLARR_URL_BASE = "/manage/indexers";
- environmentFiles = [ hmConfig.sops.templates.prowlarr-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.prowlarr.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/indexers`)"
- "traefik.http.routers.prowlarr.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "sops-nix.service"
- "${containers.transmission._serviceName}.service"
- "${containers.flaresolverr._serviceName}.service"
- "${containers.radarr._serviceName}.service"
- "${containers.radarr-uhd._serviceName}.service"
- "${containers.radarr-anime._serviceName}.service"
- "${containers.sonarr._serviceName}.service"
- "${containers.sonarr-uhd._serviceName}.service"
- "${containers.sonarr-anime._serviceName}.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- radarr = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-radarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./radarr/setup.sh;
- };
- in
- [
- "${setup}:/etc/radarr/setup.sh:ro"
- "${volumes.radarr.ref}:/var/lib/radarr"
- "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- ];
- environments = {
- RADARR_INSTANCE_NAME = "Radarr";
- RADARR_URL_BASE = "/manage/films";
- RADARR_ROOT_FOLDER = "/var/lib/media/films";
- RADARR_DOWNLOAD_CATEGORY = "radarr";
- };
- environmentFiles = [ hmConfig.sops.templates.radarr-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.radarr.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/films`)"
- "traefik.http.routers.radarr.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "${containers.transmission._serviceName}.service"
- "sops-nix.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- radarr-uhd = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-radarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./radarr/setup.sh;
- };
- in
- [
- "${setup}:/etc/radarr/setup.sh:ro"
- "${volumes.radarr-uhd.ref}:/var/lib/radarr"
- "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- ];
- environments = {
- RADARR_INSTANCE_NAME = "Radarr (UHD)";
- RADARR_URL_BASE = "/manage/films/uhd";
- RADARR_ROOT_FOLDER = "/var/lib/media/films";
- RADARR_DOWNLOAD_CATEGORY = "radarr-uhd";
- };
- environmentFiles = [ hmConfig.sops.templates.radarr-uhd-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.radarr-uhd.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/films/uhd`)"
- "traefik.http.routers.radarr-uhd.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "${containers.transmission._serviceName}.service"
- "sops-nix.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- radarr-anime = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-radarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./radarr/setup.sh;
- };
- in
- [
- "${setup}:/etc/radarr/setup.sh:ro"
- "${volumes.radarr-anime.ref}:/var/lib/radarr"
- "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- ];
- environments = {
- RADARR_INSTANCE_NAME = "Radarr (Anime)";
- RADARR_URL_BASE = "/manage/anime/films";
- RADARR_ROOT_FOLDER = "/var/lib/media/anime/films";
- RADARR_DOWNLOAD_CATEGORY = "radarr-anime";
- };
- environmentFiles = [ hmConfig.sops.templates.radarr-anime-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.radarr-anime.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/anime/films`)"
- "traefik.http.routers.radarr-anime.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "${containers.transmission._serviceName}.service"
- "sops-nix.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- sonarr = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-sonarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./sonarr/setup.sh;
- };
- in
- [
- "${setup}:/etc/sonarr/setup.sh:ro"
- "${volumes.sonarr.ref}:/var/lib/sonarr"
- "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- ];
- environments = {
- SONARR_INSTANCE_NAME = "Sonarr";
- SONARR_URL_BASE = "/manage/shows";
- SONARR_ROOT_FOLDER = "/var/lib/media/shows";
- SONARR_DOWNLOAD_CATEGORY = "sonarr";
- };
- environmentFiles = [ hmConfig.sops.templates.sonarr-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.sonarr.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/shows`)"
- "traefik.http.routers.sonarr.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "${containers.transmission._serviceName}.service"
- "sops-nix.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- sonarr-uhd = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-sonarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./sonarr/setup.sh;
- };
- in
- [
- "${setup}:/etc/sonarr/setup.sh:ro"
- "${volumes.sonarr-uhd.ref}:/var/lib/sonarr"
- "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- ];
- environments = {
- SONARR_INSTANCE_NAME = "Sonarr (UHD)";
- SONARR_URL_BASE = "/manage/shows/uhd";
- SONARR_ROOT_FOLDER = "/var/lib/media/shows";
- SONARR_DOWNLOAD_CATEGORY = "sonarr-uhd";
- };
- environmentFiles = [ hmConfig.sops.templates.sonarr-uhd-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.sonarr-uhd.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/shows/uhd`)"
- "traefik.http.routers.sonarr-uhd.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "${containers.transmission._serviceName}.service"
- "sops-nix.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- sonarr-anime = {
- containerConfig = {
- image = "docker-archive:${selfPkgs.docker-sonarr}";
- networks = [
- networks.media.ref
- networks.transmission.ref
- networks.traefik.ref
- ];
- volumes =
- let
- setup = pkgs.writeTextFile {
- name = "setup.sh";
- executable = true;
- text = builtins.readFile ./sonarr/setup.sh;
- };
- in
- [
- "${setup}:/etc/sonarr/setup.sh:ro"
- "${volumes.sonarr-anime.ref}:/var/lib/sonarr"
- "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
- "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
- ];
- environments = {
- SONARR_INSTANCE_NAME = "Sonarr (Anime)";
- SONARR_URL_BASE = "/manage/anime/shows";
- SONARR_ROOT_FOLDER = "/var/lib/media/anime/shows";
- SONARR_DOWNLOAD_CATEGORY = "sonarr-anime";
- };
- environmentFiles = [ hmConfig.sops.templates.sonarr-anime-env.path ];
- labels = [
- "traefik.enable=true"
- "traefik.http.routers.sonarr-anime.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/anime/shows`)"
- "traefik.http.routers.sonarr-anime.middlewares=authelia@docker"
- ];
- };
-
- unitConfig =
- let
- dependencies = [
- "${containers.transmission._serviceName}.service"
- "sops-nix.service"
- ];
- in
- {
- After = dependencies;
- Requires = dependencies;
- };
- };
-
- authelia.containerConfig.volumes =
+ }
+ // (
let
- mediaConfig = (pkgs.formats.yaml { }).generate "media.yaml" {
- access_control.rules = [
- {
- domain = "media.karaolidis.com";
- policy = "one_factor";
- resources = [ "^/manage([/?].*)?$" ];
- subject = [ "group:media" ];
- }
- {
- domain = "media.karaolidis.com";
- policy = "deny";
- resources = [ "^/manage([/?].*)?$" ];
- }
- {
- domain = "media.karaolidis.com";
- policy = "bypass";
- }
- ];
- };
+ arrServices = builtins.map (arr: "${containers.${arr.shortName}._serviceName}.service") arrs;
in
- [
- "${mediaConfig}:/etc/authelia/conf.d/media.yaml:ro"
- "${hmConfig.sops.templates.authelia-jellyfin.path}:/etc/authelia/conf.d/jellyfin.yaml:ro"
- ];
- };
+ {
+ flaresolverr.containerConfig = {
+ image = "docker-archive:${selfPkgs.docker-flaresolverr}";
+ networks = [ networks.flaresolverr.ref ];
+ };
+
+ prowlarr = {
+ containerConfig = {
+ image = "docker-archive:${selfPkgs.docker-prowlarr}";
+ networks = [
+ networks.media.ref
+ networks.transmission.ref
+ networks.flaresolverr.ref
+ networks.traefik.ref
+ ];
+ volumes =
+ let
+ setup = pkgs.writeTextFile {
+ name = "setup.sh";
+ executable = true;
+ text = builtins.readFile ./prowlarr/setup.sh;
+ };
+ in
+ [
+ "${setup}:/etc/prowlarr/setup.sh:ro"
+ "${./prowlarr/indexers}:/etc/prowlarr/indexers:ro"
+ "${volumes.prowlarr.ref}:/var/lib/prowlarr"
+ ]
+ ++ builtins.map (
+ arr:
+ "${
+ hmConfig.sops.templates."prowlarr-${arr.shortName}".path
+ }:/etc/prowlarr/apps/${arr.shortName}.json:ro"
+ ) arrs;
+ environments.URL_BASE = "/manage/indexers";
+ environmentFiles = [ hmConfig.sops.templates.prowlarr-env.path ];
+ labels = [
+ "traefik.enable=true"
+ "traefik.http.routers.prowlarr.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/indexers`)"
+ "traefik.http.routers.prowlarr.middlewares=authelia@docker"
+ ];
+ };
+
+ unitConfig =
+ let
+ dependencies = [ "sops-nix.service" ];
+ in
+ {
+ After =
+ dependencies
+ ++ [
+ "${containers.transmission._serviceName}.service"
+ "${containers.flaresolverr._serviceName}.service"
+ ]
+ ++ arrServices;
+ Requires = dependencies;
+ };
+ };
+
+ recyclarr = {
+ containerConfig = {
+ image = "docker-archive:${selfPkgs.docker-recyclarr}";
+ networks = [ networks.media.ref ];
+ volumes = builtins.map (
+ arr:
+ "${
+ hmConfig.sops.templates."recyclarr-${arr.shortName}".path
+ }:/var/lib/recyclarr/configs/${arr.shortName}.yaml:ro"
+ ) arrs;
+ };
+
+ unitConfig =
+ let
+ dependencies = [ "sops-nix.service" ];
+ in
+ {
+ After = dependencies ++ arrServices;
+ Requires = dependencies;
+ };
+ };
+ }
+ )
+ // builtins.listToAttrs (
+ builtins.map (arr: {
+ name = arr.shortName;
+ value = {
+ containerConfig = {
+ image = "docker-archive:${selfPkgs."docker-${arr.type}"}";
+ networks = [
+ networks.media.ref
+ networks.transmission.ref
+ networks.traefik.ref
+ ];
+ volumes =
+ let
+ setup = pkgs.writeTextFile {
+ name = "setup.sh";
+ executable = true;
+ text = builtins.readFile ./${arr.type}/setup.sh;
+ };
+ in
+ [
+ "${setup}:/etc/${arr.type}/setup.sh:ro"
+ "${volumes.${arr.shortName}.ref}:/var/lib/${arr.type}"
+ "/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
+ "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
+ ];
+ environments = {
+ INSTANCE_NAME = arr.name;
+ URL_BASE = arr.urlBase;
+ ROOT_FOLDER = "/var/lib/media${arr.mediaFolderBase}";
+ DOWNLOAD_CATEGORY = arr.shortName;
+ };
+ environmentFiles = [ hmConfig.sops.templates."${arr.shortName}-env".path ];
+ labels = [
+ "traefik.enable=true"
+ "traefik.http.routers.${arr.shortName}.rule=Host(`media.karaolidis.com`) && PathPrefix(`${arr.urlBase}`)"
+ "traefik.http.routers.${arr.shortName}.middlewares=authelia@docker"
+ ];
+ };
+
+ unitConfig =
+ let
+ dependencies = [ "sops-nix.service" ];
+ in
+ {
+ After = dependencies ++ [ "${containers.transmission._serviceName}.service" ];
+ Requires = dependencies;
+ };
+ };
+ }) arrs
+ )
+ // {
+ authelia.containerConfig.volumes =
+ let
+ mediaConfig = (pkgs.formats.yaml { }).generate "media.yaml" {
+ access_control.rules = [
+ {
+ domain = "media.karaolidis.com";
+ policy = "one_factor";
+ resources = [ "^/manage([/?].*)?$" ];
+ subject = [ "group:media" ];
+ }
+ {
+ domain = "media.karaolidis.com";
+ policy = "deny";
+ resources = [ "^/manage([/?].*)?$" ];
+ }
+ {
+ domain = "media.karaolidis.com";
+ policy = "bypass";
+ }
+ ];
+ };
+ in
+ [
+ "${mediaConfig}:/etc/authelia/conf.d/media.yaml:ro"
+ "${hmConfig.sops.templates.authelia-jellyfin.path}:/etc/authelia/conf.d/jellyfin.yaml:ro"
+ ];
+ };
};
};
}
diff --git a/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/post-start.sh b/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/post-start.sh
deleted file mode 100644
index 4c6b75c..0000000
--- a/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/post-start.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-# shellcheck shell=sh
-
-for filepath in /etc/prowlarr/indexers/*.json; do
- curl -sf --retry 10 "$PROWLARR_HOST/api/v1/indexer?forceSave=true" \
- -X POST \
- -H "Content-Type: application/json" \
- -H "X-Api-Key: $PROWLARR_API_KEY" \
- --data-binary @"$filepath" || true
-done
diff --git a/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/setup.sh b/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/setup.sh
index c31d561..da4fae2 100644
--- a/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/setup.sh
+++ b/hosts/jupiter/users/storm/configs/console/podman/media/prowlarr/setup.sh
@@ -1,17 +1,17 @@
# shellcheck shell=sh
# Tag ID: 1
-curl -sf "$PROWLARR_HOST/api/v1/tag" \
+curl -sf "$HOST/api/v1/tag" \
-X POST \
-H 'Content-Type: application/json' \
- -H "X-Api-Key: $PROWLARR_API_KEY" \
+ -H "X-Api-Key: $API_KEY" \
--data-raw '{"label": "flaresolverr"}' || true
{
- curl -sf --retry 5 "$PROWLARR_HOST/api/v1/indexerProxy?forceSave=true" \
+ curl -sf --retry 5 "$HOST/api/v1/indexerProxy?forceSave=true" \
-X POST \
-H 'Content-Type: application/json' \
- -H "X-Api-Key: $PROWLARR_API_KEY" \
+ -H "X-Api-Key: $API_KEY" \
--data-binary @- <' > /var/lib/prowlarr/config.xml
-
- xmlstarlet ed -L \
- -s /Config -t elem -n LaunchBrowser -v "False" \
- -s /Config -t elem -n ApiKey -v "$PROWLARR_API_KEY" \
- -s /Config -t elem -n AuthenticationMethod -v "External" \
- -s /Config -t elem -n AuthenticationRequired -v "DisabledForLocalAddresses" \
- -s /Config -t elem -n LogLevel -v "info" \
- -s /Config -t elem -n UrlBase -v "$PROWLARR_URL_BASE" \
- -s /Config -t elem -n InstanceName -v "${PROWLARR_INSTANCE_NAME:-prowlarr}" \
- -s /Config -t elem -n AnalyticsEnabled -v "False" \
- /var/lib/prowlarr/config.xml
fi
+set_config_value() {
+ name="$1"
+ value="$2"
+
+ CONFIG_FILE="/var/lib/prowlarr/config.xml"
+
+ if xmlstarlet sel -t -v "/Config/$name" "$CONFIG_FILE" >/dev/null 2>&1; then
+ xmlstarlet ed -L -u "/Config/$name" -v "$value" "$CONFIG_FILE"
+ else
+ xmlstarlet ed -L -s "/Config" -t elem -n "$name" -v "$value" "$CONFIG_FILE"
+ fi
+}
+
+set_config_value "LaunchBrowser" "False"
+set_config_value "ApiKey" "$API_KEY"
+set_config_value "AuthenticationMethod" "External"
+set_config_value "AuthenticationRequired" "DisabledForLocalAddresses"
+set_config_value "LogLevel" "info"
+set_config_value "UrlBase" "$URL_BASE"
+set_config_value "InstanceName" "${INSTANCE_NAME:-Prowlarr}"
+set_config_value "AnalyticsEnabled" "False"
+
Prowlarr -data=/var/lib/prowlarr -nobrowser "$@" &
PID=$!
-PROWLARR_HOST="http://localhost:9696$PROWLARR_URL_BASE"
+HOST="http://localhost:9696$URL_BASE"
-if [ ! -f /var/lib/prowlarr/init ]; then
- curl -sf --retry 10 --retry-connrefused \
- -H "X-Api-Key: $PROWLARR_API_KEY" \
- "$PROWLARR_HOST/api/v1/health"
+curl -sf --retry 10 --retry-connrefused \
+ -H "X-Api-Key: $API_KEY" \
+ "$HOST/api/v1/health"
- if [ -f /etc/prowlarr/setup.sh ]; then
- # shellcheck disable=SC1091
- . /etc/prowlarr/setup.sh
- fi
-
- touch /var/lib/prowlarr/init
-fi
-
-if [ -f /etc/prowlarr/post-start.sh ]; then
+if [ -f /etc/prowlarr/setup.sh ]; then
# shellcheck disable=SC1091
- . /etc/prowlarr/post-start.sh
+ . /etc/prowlarr/setup.sh
fi
trap 'kill -INT "$PID"' INT TERM
diff --git a/packages/docker/radarr/entrypoint.sh b/packages/docker/radarr/entrypoint.sh
index 8148fbc..f93491f 100644
--- a/packages/docker/radarr/entrypoint.sh
+++ b/packages/docker/radarr/entrypoint.sh
@@ -3,39 +3,46 @@
set -o errexit
set -o nounset
-RADARR_URL_BASE="${RADARR_URL_BASE:-}"
+URL_BASE="${URL_BASE:-}"
-if [ ! -f /var/lib/radarr/init ]; then
+if [ ! -f /var/lib/radarr/config.xml ]; then
echo '' > /var/lib/radarr/config.xml
-
- xmlstarlet ed -L \
- -s /Config -t elem -n LaunchBrowser -v "False" \
- -s /Config -t elem -n ApiKey -v "$RADARR_API_KEY" \
- -s /Config -t elem -n AuthenticationMethod -v "External" \
- -s /Config -t elem -n AuthenticationRequired -v "DisabledForLocalAddresses" \
- -s /Config -t elem -n LogLevel -v "info" \
- -s /Config -t elem -n UrlBase -v "$RADARR_URL_BASE" \
- -s /Config -t elem -n InstanceName -v "${RADARR_INSTANCE_NAME:-Radarr}" \
- -s /Config -t elem -n AnalyticsEnabled -v "False" \
- /var/lib/radarr/config.xml
fi
+set_config_value() {
+ name="$1"
+ value="$2"
+
+ CONFIG_FILE="/var/lib/radarr/config.xml"
+
+ if xmlstarlet sel -t -v "/Config/$name" "$CONFIG_FILE" >/dev/null 2>&1; then
+ xmlstarlet ed -L -u "/Config/$name" -v "$value" "$CONFIG_FILE"
+ else
+ xmlstarlet ed -L -s "/Config" -t elem -n "$name" -v "$value" "$CONFIG_FILE"
+ fi
+}
+
+set_config_value "LaunchBrowser" "False"
+set_config_value "ApiKey" "$API_KEY"
+set_config_value "AuthenticationMethod" "External"
+set_config_value "AuthenticationRequired" "DisabledForLocalAddresses"
+set_config_value "LogLevel" "info"
+set_config_value "UrlBase" "$URL_BASE"
+set_config_value "InstanceName" "${INSTANCE_NAME:-Radarr}"
+set_config_value "AnalyticsEnabled" "False"
+
Radarr -data=/var/lib/radarr -nobrowser "$@" &
PID=$!
-RADARR_HOST="http://localhost:7878$RADARR_URL_BASE"
+HOST="http://localhost:7878$URL_BASE"
-if [ ! -f /var/lib/radarr/init ]; then
- curl -sf --retry 10 --retry-connrefused \
- -H "X-Api-Key: $RADARR_API_KEY" \
- "$RADARR_HOST/api/v1/health"
+curl -sf --retry 10 --retry-connrefused \
+ -H "X-Api-Key: $API_KEY" \
+ "$HOST/api/v1/health"
- if [ -f /etc/radarr/setup.sh ]; then
- # shellcheck disable=SC1091
- . /etc/radarr/setup.sh
- fi
-
- touch /var/lib/radarr/init
+if [ -f /etc/radarr/setup.sh ]; then
+ # shellcheck disable=SC1091
+ . /etc/radarr/setup.sh
fi
trap 'kill -INT "$PID"' INT TERM
diff --git a/packages/docker/recyclarr/default.nix b/packages/docker/recyclarr/default.nix
new file mode 100644
index 0000000..abb9124
--- /dev/null
+++ b/packages/docker/recyclarr/default.nix
@@ -0,0 +1,46 @@
+{ pkgs, ... }:
+let
+ entrypoint = pkgs.writeTextFile {
+ name = "entrypoint";
+ executable = true;
+ destination = "/bin/entrypoint";
+ text = builtins.readFile ./entrypoint.sh;
+ };
+
+ crontab = pkgs.writeTextDir "/var/cron/tabs/root" ''
+ */0 * * * * recyclarr sync
+ '';
+in
+pkgs.dockerTools.buildImage {
+ name = "recyclarr";
+ fromImage = import ../base { inherit pkgs; };
+
+ copyToRoot = pkgs.buildEnv {
+ name = "root";
+ paths = with pkgs; [
+ entrypoint
+ crontab
+ recyclarr
+ cron
+ ];
+ pathsToLink = [
+ "/bin"
+ "/lib"
+ "/var"
+ ];
+ };
+
+ runAsRoot = ''
+ ${pkgs.dockerTools.shadowSetup}
+ mkdir -p /var/run
+ '';
+
+ config = {
+ Entrypoint = [ "entrypoint" ];
+ Env = [ "RECYCLARR_APP_DATA=/var/lib/recyclarr" ];
+ WorkingDir = "/var/lib/recyclarr";
+ Volumes = {
+ "/var/lib/recyclarr" = { };
+ };
+ };
+}
diff --git a/packages/docker/recyclarr/entrypoint.sh b/packages/docker/recyclarr/entrypoint.sh
new file mode 100644
index 0000000..1205f16
--- /dev/null
+++ b/packages/docker/recyclarr/entrypoint.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env sh
+
+set -o errexit
+set -o nounset
+
+recyclarr sync
+
+cron
+trap : TERM INT; sleep infinity & wait
diff --git a/packages/docker/sonarr/entrypoint.sh b/packages/docker/sonarr/entrypoint.sh
index b74ea41..b9b1fab 100644
--- a/packages/docker/sonarr/entrypoint.sh
+++ b/packages/docker/sonarr/entrypoint.sh
@@ -3,39 +3,46 @@
set -o errexit
set -o nounset
-SONARR_URL_BASE="${SONARR_URL_BASE:-}"
+URL_BASE="${URL_BASE:-}"
-if [ ! -f /var/lib/sonarr/init ]; then
+if [ ! -f /var/lib/sonarr/config.xml ]; then
echo '' > /var/lib/sonarr/config.xml
-
- xmlstarlet ed -L \
- -s /Config -t elem -n LaunchBrowser -v "False" \
- -s /Config -t elem -n ApiKey -v "$SONARR_API_KEY" \
- -s /Config -t elem -n AuthenticationMethod -v "External" \
- -s /Config -t elem -n AuthenticationRequired -v "DisabledForLocalAddresses" \
- -s /Config -t elem -n LogLevel -v "info" \
- -s /Config -t elem -n UrlBase -v "$SONARR_URL_BASE" \
- -s /Config -t elem -n InstanceName -v "${SONARR_INSTANCE_NAME:-Sonarr}" \
- -s /Config -t elem -n AnalyticsEnabled -v "False" \
- /var/lib/sonarr/config.xml
fi
+set_config_value() {
+ name="$1"
+ value="$2"
+
+ CONFIG_FILE="/var/lib/sonarr/config.xml"
+
+ if xmlstarlet sel -t -v "/Config/$name" "$CONFIG_FILE" >/dev/null 2>&1; then
+ xmlstarlet ed -L -u "/Config/$name" -v "$value" "$CONFIG_FILE"
+ else
+ xmlstarlet ed -L -s "/Config" -t elem -n "$name" -v "$value" "$CONFIG_FILE"
+ fi
+}
+
+set_config_value "LaunchBrowser" "False"
+set_config_value "ApiKey" "$API_KEY"
+set_config_value "AuthenticationMethod" "External"
+set_config_value "AuthenticationRequired" "DisabledForLocalAddresses"
+set_config_value "LogLevel" "info"
+set_config_value "UrlBase" "$URL_BASE"
+set_config_value "InstanceName" "${INSTANCE_NAME:-Sonarr}"
+set_config_value "AnalyticsEnabled" "False"
+
Sonarr -data=/var/lib/sonarr -nobrowser "$@" &
PID=$!
-SONARR_HOST="http://localhost:8989$SONARR_URL_BASE"
+HOST="http://localhost:8989$URL_BASE"
-if [ ! -f /var/lib/sonarr/init ]; then
- curl -sf --retry 10 --retry-connrefused \
- -H "X-Api-Key: $SONARR_API_KEY" \
- "$SONARR_HOST/api/v1/health"
+curl -sf --retry 10 --retry-connrefused \
+ -H "X-Api-Key: $API_KEY" \
+ "$HOST/api/v1/health"
- if [ -f /etc/sonarr/setup.sh ]; then
- # shellcheck disable=SC1091
- . /etc/sonarr/setup.sh
- fi
-
- touch /var/lib/sonarr/init
+if [ -f /etc/sonarr/setup.sh ]; then
+ # shellcheck disable=SC1091
+ . /etc/sonarr/setup.sh
fi
trap 'kill -INT "$PID"' INT TERM