Fix atomic media moves

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2025-07-17 22:28:24 +01:00
parent 3a110af1ec
commit 718ccc506f
16 changed files with 99 additions and 121 deletions

View File

@@ -18,7 +18,6 @@ in
(import ./shlink { inherit user home; }) (import ./shlink { inherit user home; })
(import ./sish { inherit user home; }) (import ./sish { inherit user home; })
(import ./traefik { inherit user home; }) (import ./traefik { inherit user home; })
(import ./transmission { inherit user home; })
(import ./vaultwarden { inherit user home; }) (import ./vaultwarden { inherit user home; })
(import ./whoami { inherit user home; }) (import ./whoami { inherit user home; })
]; ];

View File

@@ -18,7 +18,6 @@ in
{ {
imports = [ imports = [
(import ./jellyfin { inherit user home; }) (import ./jellyfin { inherit user home; })
(import ./jellyseerr { (import ./jellyseerr {
inherit inherit
user user
@@ -27,7 +26,6 @@ in
sonarrs sonarrs
; ;
}) })
(import ./prowlarr { (import ./prowlarr {
inherit inherit
user user
@@ -36,7 +34,6 @@ in
sonarrs sonarrs
; ;
}) })
(import ./recyclarr { (import ./recyclarr {
inherit inherit
user user
@@ -45,19 +42,18 @@ in
sonarrs sonarrs
; ;
}) })
(import ./radarr { inherit user home radarrs; }) (import ./radarr { inherit user home radarrs; })
(import ./sonarr { inherit user home sonarrs; }) (import ./sonarr { inherit user home sonarrs; })
(import ./transmission { inherit user home; })
]; ];
home-manager.users.${user} = { home-manager.users.${user} = {
systemd.user.tmpfiles.rules = [ systemd.user.tmpfiles.rules = [
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data 700 storm storm" "d /mnt/storage/private/storm/containers/storage/volumes/media/_data 700 storm storm"
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/films 755 storm storm" "d /mnt/storage/private/storm/containers/storage/volumes/media/_data/libraries/films 755 storm storm"
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/shows 755 storm storm" "d /mnt/storage/private/storm/containers/storage/volumes/media/_data/libraries/shows 755 storm storm"
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/anime/films 755 storm storm" "d /mnt/storage/private/storm/containers/storage/volumes/media/_data/libraries/anime/films 755 storm storm"
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/anime/shows 755 storm storm" "d /mnt/storage/private/storm/containers/storage/volumes/media/_data/libraries/anime/shows 755 storm storm"
]; ];
virtualisation.quadlet.networks.media = { }; virtualisation.quadlet.networks.media = { };

View File

@@ -121,7 +121,7 @@
"LyricFetcherOrder": [], "LyricFetcherOrder": [],
"PathInfos": [ "PathInfos": [
{ {
"Path": "/var/lib/media/anime/films" "Path": "/var/lib/media/libraries/anime/films"
} }
] ]
} }

View File

@@ -121,7 +121,7 @@
"LyricFetcherOrder": [], "LyricFetcherOrder": [],
"PathInfos": [ "PathInfos": [
{ {
"Path": "/var/lib/media/films" "Path": "/var/lib/media/libraries/films"
} }
] ]
} }

View File

@@ -197,7 +197,7 @@
"LyricFetcherOrder": [], "LyricFetcherOrder": [],
"PathInfos": [ "PathInfos": [
{ {
"Path": "/var/lib/media/anime/shows" "Path": "/var/lib/media/libraries/anime/shows"
} }
] ]
} }

View File

@@ -197,7 +197,7 @@
"LyricFetcherOrder": [], "LyricFetcherOrder": [],
"PathInfos": [ "PathInfos": [
{ {
"Path": "/var/lib/media/shows" "Path": "/var/lib/media/libraries/shows"
} }
] ]
} }

View File

@@ -54,20 +54,6 @@ libraries="$(
]' ]'
)" )"
tmpfile="$(mktemp)"
jq -s \
--arg serverId "$serverId" \
--arg apiKey "$jellyseerr_key" \
--argjson libraries "$libraries" \
'.[0] * .[1] # merge default + existing
| .jellyfin.serverId = $serverId
| .jellyfin.apiKey = $apiKey
| .jellyfin.libraries = $libraries' \
/var/lib/jellyseerr/settings.json \
/etc/jellyseerr/settings.default.json \
> "$tmpfile"
mv "$tmpfile" /var/lib/jellyseerr/settings.json
try_forever() { try_forever() {
until "$@" 2>&1; do until "$@" 2>&1; do
echo "Try failed: $* - retrying in 1s" echo "Try failed: $* - retrying in 1s"
@@ -139,6 +125,8 @@ for f in /etc/jellyseerr/apps/sonarr/*.json; do
sonarr_json="$(echo "$sonarr_json" "$enriched" | jq -s '.[0] + [.[1]]')" sonarr_json="$(echo "$sonarr_json" "$enriched" | jq -s '.[0] + [.[1]]')"
done done
[ -f /var/lib/jellyseerr/settings.json ] || echo '{}' > /var/lib/jellyseerr/settings.json
tmpfile=$(mktemp) tmpfile=$(mktemp)
jq -s \ jq -s \
--argjson libs "$libraries" \ --argjson libs "$libraries" \

View File

@@ -139,7 +139,7 @@ build_transmission_payload() {
"fields": [ "fields": [
{ "name": "host", "value": "transmission" }, { "name": "host", "value": "transmission" },
{ "name": "port", "value": 9091 }, { "name": "port", "value": 9091 },
{ "name": "urlBase", "value": "" } { "name": "urlBase", "value": "/manage/torrents/" }
], ],
"categories": [], "categories": [],
"implementation": "Transmission", "implementation": "Transmission",

View File

@@ -79,7 +79,7 @@ rec {
hostname = hostName; hostname = hostName;
baseUrl = urlBase; baseUrl = urlBase;
activeProfileName = recyclarrProfile; activeProfileName = recyclarrProfile;
activeDirectory = "/var/lib/media${mediaFolderBase}"; activeDirectory = "/var/lib/media/libraries${mediaFolderBase}";
minimumAvailability = "released"; minimumAvailability = "released";
isDefault = !isAnime; isDefault = !isAnime;
externalUrl = "https://media.karaolidis.com${urlBase}"; externalUrl = "https://media.karaolidis.com${urlBase}";

View File

@@ -35,6 +35,11 @@ in
); );
}; };
systemd.user.tmpfiles.rules = builtins.map (
radarr:
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/downloads/transmission/${radarr.hostName} 755 storm storm"
) radarrs;
virtualisation.quadlet = { virtualisation.quadlet = {
networks.media = { }; networks.media = { };
@@ -67,13 +72,12 @@ in
[ [
"${postStart}:/etc/radarr/post-start.sh:ro" "${postStart}:/etc/radarr/post-start.sh:ro"
"${volumes.${radarr.hostName}.ref}:/var/lib/radarr" "${volumes.${radarr.hostName}.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" "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
]; ];
environments = { environments = {
INSTANCE_NAME = radarr.name; INSTANCE_NAME = radarr.name;
URL_BASE = radarr.urlBase; URL_BASE = radarr.urlBase;
ROOT_FOLDER = "/var/lib/media${radarr.mediaFolderBase}"; ROOT_FOLDER = "/var/lib/media/libraries${radarr.mediaFolderBase}";
DOWNLOAD_CATEGORY = radarr.hostName; DOWNLOAD_CATEGORY = radarr.hostName;
}; };
environmentFiles = [ hmConfig.sops.templates."${radarr.hostName}-env".path ]; environmentFiles = [ hmConfig.sops.templates."${radarr.hostName}-env".path ];

View File

@@ -129,7 +129,7 @@ build_transmission_payload() {
"fields": [ "fields": [
{ "name": "host", "value": "transmission" }, { "name": "host", "value": "transmission" },
{ "name": "port", "value": 9091 }, { "name": "port", "value": 9091 },
{ "name": "urlBase", "value": "" }, { "name": "urlBase", "value": "/manage/torrents/" },
{ "name": "movieCategory", "value": "$DOWNLOAD_CATEGORY" } { "name": "movieCategory", "value": "$DOWNLOAD_CATEGORY" }
], ],
"implementation": "Transmission", "implementation": "Transmission",

View File

@@ -79,9 +79,9 @@ rec {
hostname = hostName; hostname = hostName;
baseUrl = urlBase; baseUrl = urlBase;
activeProfileName = recyclarrProfile; activeProfileName = recyclarrProfile;
activeDirectory = "/var/lib/media${mediaFolderBase}"; activeDirectory = "/var/lib/media/libraries${mediaFolderBase}";
activeAnimeProfileName = recyclarrProfile; activeAnimeProfileName = recyclarrProfile;
activeAnimeDirectory = "/var/lib/media${mediaFolderBase}"; activeAnimeDirectory = "/var/lib/media/libraries${mediaFolderBase}";
isDefault = !isAnime; isDefault = !isAnime;
enableSeasonFolders = true; enableSeasonFolders = true;
externalUrl = "https://media.karaolidis.com${urlBase}"; externalUrl = "https://media.karaolidis.com${urlBase}";

View File

@@ -35,6 +35,11 @@ in
); );
}; };
systemd.user.tmpfiles.rules = builtins.map (
sonarr:
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/downloads/transmission/${sonarr.hostName} 755 storm storm"
) sonarrs;
virtualisation.quadlet = { virtualisation.quadlet = {
networks.media = { }; networks.media = { };
@@ -67,13 +72,12 @@ in
[ [
"${postStart}:/etc/sonarr/post-start.sh:ro" "${postStart}:/etc/sonarr/post-start.sh:ro"
"${volumes.${sonarr.hostName}.ref}:/var/lib/sonarr" "${volumes.${sonarr.hostName}.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" "/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
]; ];
environments = { environments = {
INSTANCE_NAME = sonarr.name; INSTANCE_NAME = sonarr.name;
URL_BASE = sonarr.urlBase; URL_BASE = sonarr.urlBase;
ROOT_FOLDER = "/var/lib/media${sonarr.mediaFolderBase}"; ROOT_FOLDER = "/var/lib/media/libraries${sonarr.mediaFolderBase}";
DOWNLOAD_CATEGORY = sonarr.hostName; DOWNLOAD_CATEGORY = sonarr.hostName;
}; };
environmentFiles = [ hmConfig.sops.templates."${sonarr.hostName}-env".path ]; environmentFiles = [ hmConfig.sops.templates."${sonarr.hostName}-env".path ];

View File

@@ -129,7 +129,7 @@ build_transmission_payload() {
"fields": [ "fields": [
{ "name": "host", "value": "transmission" }, { "name": "host", "value": "transmission" },
{ "name": "port", "value": 9091 }, { "name": "port", "value": 9091 },
{ "name": "urlBase", "value": "" }, { "name": "urlBase", "value": "/manage/torrents/" },
{ "name": "tvCategory", "value": "$DOWNLOAD_CATEGORY" } { "name": "tvCategory", "value": "$DOWNLOAD_CATEGORY" }
], ],
"implementation": "Transmission", "implementation": "Transmission",

View File

@@ -0,0 +1,69 @@
{ user, home }:
{
config,
inputs,
pkgs,
system,
...
}:
let
selfPkgs = inputs.self.packages.${system};
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes networks;
in
{
home-manager.users.${user} = {
sops.secrets."transmission/protonvpn".sopsFile = ../../../../../../../secrets/secrets.yaml;
systemd.user.tmpfiles.rules = [
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/downloads/transmission 755 storm storm"
];
virtualisation.quadlet = {
networks.transmission = { };
volumes.transmission-config = { };
containers.transmission = {
containerConfig = {
image = "docker-archive:${selfPkgs.docker-transmission-protonvpn}";
networks = [
networks.transmission.ref
networks.traefik.ref
];
addCapabilities = [ "NET_ADMIN" ];
volumes =
let
config = (pkgs.formats.json { }).generate "settings.override.json" {
ratio-limit-enabled = true;
ratio-limit = 5;
download-queue-size = 10;
peer-limit-per-torrent = 50;
peer-limit-global = 500;
rpc-url = "/manage/torrents/";
download-dir = "/var/lib/media/downloads/transmission";
incomplete-dir = "/var/lib/media/downloads/transmission/incomplete";
};
in
[
"${hmConfig.sops.secrets."transmission/protonvpn".path}:/etc/wireguard/privatekey:ro"
"${config}:/etc/transmission/settings.override.json:ro"
"${volumes.transmission-config.ref}:/etc/transmission"
"/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
];
environments = {
WIREGUARD_PUBLIC_KEY = "zctOjv4DH2gzXtLQy86Tp0vnT+PNpMsxecd2vUX/i0U=";
WIREGUARD_ENDPOINT = "146.70.179.50:51820";
};
labels = [
"traefik.enable=true"
"traefik.http.routers.transmission.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/torrents`)"
"traefik.http.routers.transmission.middlewares=authelia@docker"
];
};
unitConfig.After = [ "sops-nix.service" ];
};
};
};
}

View File

@@ -1,82 +0,0 @@
{ user, home }:
{
config,
inputs,
pkgs,
system,
...
}:
let
selfPkgs = inputs.self.packages.${system};
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes networks;
in
{
home-manager.users.${user} = {
sops.secrets."transmission/protonvpn".sopsFile = ../../../../../../secrets/secrets.yaml;
systemd.user.tmpfiles.rules = [
"d /mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data 700 storm storm"
];
virtualisation.quadlet = {
networks.transmission = { };
volumes.transmission-config = { };
containers = {
transmission = {
containerConfig = {
image = "docker-archive:${selfPkgs.docker-transmission-protonvpn}";
networks = [
networks.transmission.ref
networks.traefik.ref
];
addCapabilities = [ "NET_ADMIN" ];
volumes =
let
config = (pkgs.formats.json { }).generate "settings.override.json" {
ratio-limit-enabled = true;
ratio-limit = 5;
download-queue-size = 10;
peer-limit-per-torrent = 50;
peer-limit-global = 500;
};
in
[
"${hmConfig.sops.secrets."transmission/protonvpn".path}:/etc/wireguard/privatekey:ro"
"${config}:/etc/transmission/settings.override.json:ro"
"${volumes.transmission-config.ref}:/etc/transmission"
"/mnt/storage/private/storm/containers/storage/volumes/transmission-data/_data:/var/lib/transmission"
];
environments = {
WIREGUARD_PUBLIC_KEY = "zctOjv4DH2gzXtLQy86Tp0vnT+PNpMsxecd2vUX/i0U=";
WIREGUARD_ENDPOINT = "146.70.179.50:51820";
};
labels = [
"traefik.enable=true"
"traefik.http.routers.transmission.rule=Host(`torrent.karaolidis.com`)"
"traefik.http.routers.transmission.middlewares=authelia@docker"
];
};
unitConfig.After = [ "sops-nix.service" ];
};
authelia.containerConfig.volumes =
let
config = (pkgs.formats.yaml { }).generate "transmission.yaml" {
access_control.rules = [
{
domain = "torrent.karaolidis.com";
policy = "one_factor";
subject = [ "group:media" ];
}
];
};
in
[ "${config}:/etc/authelia/conf.d/transmission.yaml:ro" ];
};
};
};
}