From aa0f47607f564ca6374ad71204f4a4ee288a40a6 Mon Sep 17 00:00:00 2001 From: Evan Fiordeliso Date: Tue, 16 Apr 2024 09:59:09 -0400 Subject: [PATCH] add nixos module to flake, add mainProgram option to package, and fix typo in readme --- README.md | 2 +- flake.nix | 1 + nix/module.nix | 229 ++++++++++++++++++++++++++++++++++++++++++++++++ nix/package.nix | 1 + 4 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 nix/module.nix diff --git a/README.md b/README.md index 62868e0..9eda1a9 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ You can configure the application using environment variables | YTDL_COOKIES_FROMBROWSER_BROWSER | The name of the browser to load cookies from. (if specified, it disables YTDL_COOKIES_FILEPATH) | ` ` | `brave`, `chrome`, `chromium`, `edge`, `firefox`, `opera`, `safari`, `vivaldi` | | YTDL_COOKIES_FROMBROWSER_KEYRING | The name of the keyring for decrypting cookies for the chromium browser on linux | ` ` | `basictext`, `gnomekeyring`, `kwallet` | | YTDL_COOKIES_FROMBROWSER_PROFILE | The browser profile to load cookies from | ` ` | | -| YTDL_COOKIES_FROMBROWSER_CONTAINER | The container name (if firefox) top load the cookies from | ` ` | | +| YTDL_COOKIES_FROMBROWSER_CONTAINER | The container name (if firefox) to load the cookies from | ` ` | | ## Building from source diff --git a/flake.nix b/flake.nix index 433016a..45c7c72 100644 --- a/flake.nix +++ b/flake.nix @@ -50,6 +50,7 @@ # agnostic ones like nixosModule and system-enumerating ones, although # those are more easily expressed in perSystem. + nixosModules.default = import ./nix/module.nix; }; }; } diff --git a/nix/module.nix b/nix/module.nix new file mode 100644 index 0000000..ad49c05 --- /dev/null +++ b/nix/module.nix @@ -0,0 +1,229 @@ +{ 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 = { }; + }; + }; + }; +} diff --git a/nix/package.nix b/nix/package.nix index bf98f6c..830c882 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -35,5 +35,6 @@ buildGoModule rec { description = "Yet another yt-dlp web frontend written in Go."; homepage = "https://git.fifitido.net/apps/ytdl-web"; license = licenses.gpl3Only; + mainProgram = "ytdl-web"; }; }