at 25.11-pre 4.9 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8 9let 10 cfg = config.services.ytdl-sub; 11 12 settingsFormat = pkgs.formats.yaml { }; 13in 14{ 15 meta.maintainers = with lib.maintainers; [ defelo ]; 16 17 options.services.ytdl-sub = { 18 package = lib.mkPackageOption pkgs "ytdl-sub" { }; 19 20 user = lib.mkOption { 21 type = lib.types.str; 22 default = "ytdl-sub"; 23 description = "User account under which ytdl-sub runs."; 24 }; 25 26 group = lib.mkOption { 27 type = lib.types.str; 28 default = "ytdl-sub"; 29 description = "Group under which ytdl-sub runs."; 30 }; 31 32 instances = lib.mkOption { 33 default = { }; 34 description = "Configuration for ytdl-sub instances."; 35 type = lib.types.attrsOf ( 36 lib.types.submodule ( 37 { name, ... }: 38 { 39 options = { 40 enable = lib.mkEnableOption "ytdl-sub instance"; 41 42 schedule = lib.mkOption { 43 type = lib.types.nullOr lib.types.str; 44 description = "How often to run ytdl-sub. See {manpage}`systemd.time(7)` for the format."; 45 default = null; 46 example = "0/6:0"; 47 }; 48 49 config = lib.mkOption { 50 type = settingsFormat.type; 51 description = "Configuration for ytdl-sub. See <https://ytdl-sub.readthedocs.io/en/latest/config_reference/config_yaml.html> for more information."; 52 default = { }; 53 example = { 54 presets."YouTube Playlist" = { 55 download = "{subscription_value}"; 56 output_options = { 57 output_directory = "YouTube"; 58 file_name = "{channel}/{playlist_title}/{playlist_index_padded}_{title}.{ext}"; 59 maintain_download_archive = true; 60 }; 61 }; 62 }; 63 }; 64 65 subscriptions = lib.mkOption { 66 type = settingsFormat.type; 67 description = "Subscriptions for ytdl-sub. See <https://ytdl-sub.readthedocs.io/en/latest/config_reference/subscription_yaml.html> for more information."; 68 default = { }; 69 example = { 70 "YouTube Playlist" = { 71 "Some Playlist" = "https://www.youtube.com/playlist?list=..."; 72 }; 73 }; 74 }; 75 }; 76 77 config = { 78 config.configuration.working_directory = "/run/ytdl-sub/${utils.escapeSystemdPath name}"; 79 }; 80 } 81 ) 82 ); 83 }; 84 }; 85 86 config = lib.mkIf (cfg.instances != { }) { 87 systemd.services = 88 let 89 mkService = 90 name: instance: 91 let 92 configFile = settingsFormat.generate "config.yaml" instance.config; 93 subscriptionsFile = settingsFormat.generate "subscriptions.yaml" instance.subscriptions; 94 in 95 lib.nameValuePair "ytdl-sub-${utils.escapeSystemdPath name}" { 96 inherit (instance) enable; 97 98 wants = [ "network-online.target" ]; 99 after = [ "network-online.target" ]; 100 101 startAt = lib.optional (instance.schedule != null) instance.schedule; 102 103 serviceConfig = { 104 User = cfg.user; 105 Group = cfg.group; 106 107 RuntimeDirectory = "ytdl-sub/${utils.escapeSystemdPath name}"; 108 StateDirectory = "ytdl-sub/${utils.escapeSystemdPath name}"; 109 WorkingDirectory = "/var/lib/ytdl-sub/${utils.escapeSystemdPath name}"; 110 111 ExecStart = "${lib.getExe cfg.package} --config ${configFile} sub ${subscriptionsFile}"; 112 113 # Hardening 114 CapabilityBoundingSet = [ "" ]; 115 DeviceAllow = [ "" ]; 116 LockPersonality = true; 117 PrivateDevices = true; 118 PrivateTmp = true; 119 PrivateUsers = true; 120 ProcSubset = "pid"; 121 ProtectClock = true; 122 ProtectControlGroups = true; 123 ProtectHome = true; 124 ProtectHostname = true; 125 ProtectKernelLogs = true; 126 ProtectKernelModules = true; 127 ProtectKernelTunables = true; 128 ProtectProc = "invisible"; 129 ProtectSystem = "strict"; 130 RestrictAddressFamilies = [ 131 "AF_INET" 132 "AF_INET6" 133 "AF_UNIX" 134 ]; 135 RestrictNamespaces = true; 136 RestrictRealtime = true; 137 RestrictSUIDSGID = true; 138 SystemCallArchitectures = "native"; 139 }; 140 }; 141 in 142 lib.mapAttrs' mkService cfg.instances; 143 144 users.users = lib.mkIf (cfg.user == "ytdl-sub") { 145 ytdl-sub = { 146 isSystemUser = true; 147 group = cfg.group; 148 }; 149 }; 150 151 users.groups = lib.mkIf (cfg.group == "ytdl-sub") { 152 ytdl-sub = { }; 153 }; 154 }; 155}