{ config, lib, pkgs, ... }: let inherit (lib.options) mkOption mkEnableOption mkPackageOption; inherit (lib.modules) mkIf; inherit (lib.types) str enum int path submodule nullOr bool; cfg = config.services.ytdl-web; in { options.services.ytdl-web = { enable = mkEnableOption "ytdl-web"; package = mkPackageOption pkgs "ytdl-web" { }; user = mkOption { type = str; default = "ytdl-web"; description = '' The user account ytdl-web will run under. ''; }; group = mkOption { type = str; default = "ytdl-web"; description = '' The group ytdl-web will run under. ''; }; appEnvironment = mkOption { type = enum [ "Development" "Staging" "Production" ]; default = "Production"; description = '' The application environment mode. ''; }; ytdlPackage = mkPackageOption pkgs "yt-dlp" { }; port = mkOption { type = int; default = 8080; description = '' The tcp port for the web server to listen on. ''; }; listen = mkOption { type = str; default = "0.0.0.0"; example = "127.0.0.1"; description = '' The address for the web server to listen on. ''; }; openFirewall = mkOption { type = bool; default = false; example = literalExpression "true"; description = '' Open ports in the firewall for the ytdl-web server. ''; }; basePath = mkOption { type = str; default = "/"; example = "/ytdl-web"; description = '' The base path that the web application is hosted under. Useful for reverse-proxies that proxy the app with a path prefix. ''; }; cacheTTL = mkOption { type = str; default = "1h"; example = "2m"; description = '' How long to keep cached metadata for. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". ''; }; cacheDir = mkOption { type = path; default = "/var/cache/ytdl-web"; example = "/tmp/ytdl-web"; description = '' The directory containing the ytdl metadata cache. ''; }; cookies = mkOption { type = submodule { options = { enable = mkEnableOption "ytdl cookies"; file = mkOption { type = nullOr path; default = null; example = "/etc/ytdl-web/cookies.txt"; description = '' The file that contains the netscape formatted cookies. ''; }; fromBrowser = mkOption { description = '' Settings for obtaining cookies from a local browser's cookie storage. ''; type = submodule { options = { browser = mkOption { type = nullOr (enum [ "brave" "chrome" "chromium" "edge" "firefox" "opera" "safari" "vivaldi" ]); default = null; description = '' The name of the browser to load cookies from. ''; }; keyring = mkOption { type = nullOr (enum [ "basictext" "gnomekeyring" "kwallet" ]); default = null; description = '' The name of the keyring to use to decrypt cookies when using the chromium browser. ''; }; profile = mkOption { type = nullOr str; default = null; description = '' The name of the browser profile to load the cookies from. ''; }; container = mkOption { type = nullOr str; default = null; description = '' The container name to load the cookies from when using the firefox browser. ''; }; }; }; }; }; }; }; }; config = mkIf cfg.enable { assertions = [ { assertion = cfg.cookies.file != null && cfg.cookies.fromBrowser.browser != null; message = "The `services.ytdl-web.cookies.file` and `services.ytdl-web.cookies.fromBrowser.browser` options are mutually exclusive."; } { assertion = cfg.cookies.fromBrowser.browser != null && !(builtins.elem cfg.cookies.fromBrowser.browser [ "brave" "chrome" "chromium" "edge" "opera" "vivaldi" ]) && cfg.cookies.fromBrowser.keyring != null; message = "The `services.ytdl-web.cookies.fromBrowser.keyring` only functions with a chromium-based browser."; } { assertion = cfg.cookies.fromBrowser.browser != null && cfg.cookies.fromBrowser.browser != "firefox" && cfg.cookies.fromBrowser.container != null; message = "The `services.ytdl-web.cookies.fromBrowser.container` only functions with the firefox browser."; } ]; systemd.services.ytdl-web = { description = "ytdl-web"; after = [ "networking.target" ]; wantedBy = [ "multi-user.target" ]; environment = { YTDL_ENV = cfg.appEnvironment; YTDL_BINARY_PATH = toString cfg.ytdlPackage; YTDL_HTTP_PORT = toString cfg.port; YTDL_HTTP_LISTEN = cfg.listen; YTDL_HTTP_BASEPATH = cfg.basePath; YTDL_CACHE_TTL = cfg.cacheTTL; YTDL_CACHE_DIRPATH = cfg.cacheDir; YTDL_COOKIES_ENABLED = if cfg.cookies.enable then "true" else "false"; YTDL_COOKIES_FILEPATH = cfg.cookies.file; YTDL_COOKIES_FROMBROWSER_BROWSER = cfg.cookies.fromBrowser.browser; YTDL_COOKIES_FROMBROWSER_KEYRING = cfg.cookies.fromBrowser.keyring; YTDL_COOKIES_FROMBROWSER_PROFILE = cfg.cookies.fromBrowser.profile; YTDL_COOKIES_FROMBROWSER_CONTAINER = cfg.cookies.fromBrowser.container; }; serviceConfig = { Type = "simple"; User = cfg.user; Group = cfg.group; ExecStart = "${lib.getExe cfg.package} serve"; Restart = "on-failure"; }; }; systemd.tmpfiles.rules = [ "d ${cfg.cacheDir} 0770 ${cfg.user} ${cfg.group} -" ]; networking.firewall = mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; users = { users = mkIf (cfg.user == "ytdl-web") { ytdl-web = { isSystemUser = true; group = cfg.group; }; }; groups = mkIf (cfg.group == "ytdl-web") { ytdl-web = { }; }; }; }; }