mirror of
https://github.com/marcopiovanello/yt-dlp-web-ui.git
synced 2025-09-29 00:54:56 +03:00
Add Nix (#177)
* add Nix support * fix formatter output * mention Nix in README * fix common import * fix frontend old version import * clarified flake pkgs order * rm old dataDir option * comment typo * fix password assertion * rm old User/Group logic * rewrite assertion boolean expr * General flake touchup - Rewrite `callPackage` exprs to be more readable - Add pre-commit support for devShell - Add direnv support * add simple test * use correct test func
This commit is contained in:
committed by
GitHub
parent
8eebd424c8
commit
650f1cad92
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
.pre-commit-config.yaml
|
||||
.direnv/
|
||||
result/
|
||||
result
|
||||
dist
|
||||
.pnpm-debug.log
|
||||
node_modules
|
||||
|
||||
@@ -235,6 +235,10 @@ It is **planned** to also expose a **gRPC** server.
|
||||
|
||||
For more information open an issue on GitHub and I will provide more info ASAP.
|
||||
|
||||
## Nix
|
||||
This repo adds support for Nix(OS) in various ways through a `flake-parts` flake.
|
||||
For more info, please refer to the [official documentation](https://nixos.org/learn/).
|
||||
|
||||
## What yt-dlp-webui is not
|
||||
`yt-dlp-webui` isn't your ordinary website where to download stuff from the internet, so don't try asking for links of where this is hosted. It's a self hosted platform for a Linux NAS.
|
||||
|
||||
|
||||
4
env.nix
4
env.nix
@@ -1,4 +0,0 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs.buildPackages; [ yt-dlp nodejs_22 yarn-berry go ];
|
||||
}
|
||||
149
flake.lock
generated
Normal file
149
flake.lock
generated
Normal file
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722555600,
|
||||
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1723637854,
|
||||
"narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1722555339,
|
||||
"narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1720386169,
|
||||
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1719082008,
|
||||
"narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9693852a2070b398ee123a329e68f0dab5526681",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks-nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1723803910,
|
||||
"narHash": "sha256-yezvUuFiEnCFbGuwj/bQcqg7RykIEqudOy/RBrId0pc=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "bfef0ada09e2c8ac55bbcd0831bd0c9d42e651ba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks-nix": "pre-commit-hooks-nix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
51
flake.nix
Normal file
51
flake.nix
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
description = "A terrible web ui for yt-dlp. Designed to be self-hosted.";
|
||||
|
||||
inputs = {
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix";
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [
|
||||
inputs.pre-commit-hooks-nix.flakeModule
|
||||
];
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
];
|
||||
perSystem = { config, self', pkgs, ... }: {
|
||||
|
||||
packages = {
|
||||
yt-dlp-web-ui-frontend = pkgs.callPackage ./nix/frontend.nix { };
|
||||
default = pkgs.callPackage ./nix/server.nix {
|
||||
inherit (self'.packages) yt-dlp-web-ui-frontend;
|
||||
};
|
||||
};
|
||||
|
||||
checks = import ./nix/tests { inherit self pkgs; };
|
||||
|
||||
pre-commit = {
|
||||
check.enable = true;
|
||||
settings = {
|
||||
hooks = {
|
||||
${self'.formatter.pname}.enable = true;
|
||||
deadnix.enable = true;
|
||||
nil.enable = true;
|
||||
statix.enable = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = pkgs.callPackage ./nix/devShell.nix {
|
||||
inputsFrom = [ config.pre-commit.devShell ];
|
||||
};
|
||||
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
};
|
||||
flake = {
|
||||
nixosModules.default = import ./nix/module.nix self.packages;
|
||||
};
|
||||
};
|
||||
}
|
||||
9
nix/common.nix
Normal file
9
nix/common.nix
Normal file
@@ -0,0 +1,9 @@
|
||||
{ lib }: {
|
||||
version = "v3.1.2";
|
||||
meta = {
|
||||
description = "A terrible web ui for yt-dlp. Designed to be self-hosted.";
|
||||
homepage = "https://github.com/marcopeocchi/yt-dlp-web-ui";
|
||||
license = lib.licenses.mpl20;
|
||||
};
|
||||
}
|
||||
|
||||
9
nix/devShell.nix
Normal file
9
nix/devShell.nix
Normal file
@@ -0,0 +1,9 @@
|
||||
{ inputsFrom ? [ ], mkShell, yt-dlp, nodejs, go }:
|
||||
mkShell {
|
||||
inherit inputsFrom;
|
||||
packages = [
|
||||
yt-dlp
|
||||
nodejs
|
||||
go
|
||||
];
|
||||
}
|
||||
37
nix/frontend.nix
Normal file
37
nix/frontend.nix
Normal file
@@ -0,0 +1,37 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, nodejs
|
||||
, pnpm
|
||||
}:
|
||||
let common = import ./common.nix { inherit lib; }; in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "yt-dlp-web-ui-frontend";
|
||||
|
||||
inherit (common) version;
|
||||
|
||||
src = lib.fileset.toSource {
|
||||
root = ../frontend;
|
||||
fileset = ../frontend;
|
||||
};
|
||||
|
||||
buildPhase = ''
|
||||
npm run build
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/dist
|
||||
cp -r dist/* $out/dist
|
||||
'';
|
||||
|
||||
nativeBuildInputs = [
|
||||
nodejs
|
||||
pnpm.configHook
|
||||
];
|
||||
|
||||
pnpmDeps = pnpm.fetchDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
hash = "sha256-NvXNDXkuoJ4vGeQA3bOhhc+KLBfke593qK0edcvzWTo=";
|
||||
};
|
||||
|
||||
inherit (common) meta;
|
||||
})
|
||||
215
nix/module.nix
Normal file
215
nix/module.nix
Normal file
@@ -0,0 +1,215 @@
|
||||
packages: { config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.services.yt-dlp-web-ui;
|
||||
inherit (pkgs.stdenv.hostPlatform) system;
|
||||
pkg = packages.${system}.default;
|
||||
in
|
||||
{
|
||||
/*
|
||||
Some notes on the module design:
|
||||
- Usually, you don't map out all of the options like this in attrsets,
|
||||
but due to the software's nonstandard "config file overrides CLI" behavior,
|
||||
we don't want to expose a config file catchall, and as such don't use '-conf'.
|
||||
|
||||
- Notably, '-driver' is missing as a configuration option.
|
||||
This should instead be customized with idiomatic Nix, overriding 'cfg.package' with
|
||||
the desired yt-dlp package.
|
||||
|
||||
- The systemd service has been sandboxed as much as possible. This restricts configuration of
|
||||
data and logs dir. If you really need a custom data and logs dir, use BindPaths (man systemd.exec)
|
||||
*/
|
||||
options.services.yt-dlp-web-ui = {
|
||||
enable = lib.mkEnableOption "yt-dlp-web-ui";
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkg;
|
||||
defaultText = lib.literalMD "`packages.default` from the yt-dlp-web-ui flake.";
|
||||
description = ''
|
||||
The yt-dlp-web-ui package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "yt-dlp-web-ui";
|
||||
description = lib.mdDoc ''
|
||||
User under which yt-dlp-web-ui runs.
|
||||
'';
|
||||
};
|
||||
|
||||
group = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "yt-dlp-web-ui";
|
||||
description = lib.mdDoc ''
|
||||
Group under which yt-dlp-web-ui runs.
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Whether to open the TCP port in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
host = lib.mkOption {
|
||||
default = "0.0.0.0";
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
Host where yt-dlp-web-ui will listen at.
|
||||
'';
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
default = 3033;
|
||||
type = lib.types.port;
|
||||
description = lib.mdDoc ''
|
||||
Port where yt-dlp-web-ui will listen at.
|
||||
'';
|
||||
};
|
||||
|
||||
downloadDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
The directory where yt-dlp-web-ui stores downloads.
|
||||
'';
|
||||
};
|
||||
|
||||
queueSize = lib.mkOption {
|
||||
default = 2;
|
||||
type = lib.types.ints.unsigned; # >= 0
|
||||
description = lib.mdDoc ''
|
||||
Queue size (concurrent downloads).
|
||||
'';
|
||||
};
|
||||
|
||||
logging = lib.mkEnableOption "logging";
|
||||
|
||||
rpcAuth = lib.mkOption {
|
||||
description = lib.mdDoc ''
|
||||
RPC Authentication settings.
|
||||
'';
|
||||
default = { };
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkEnableOption "RPC authentication";
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
Username required for auth.
|
||||
'';
|
||||
};
|
||||
passwordFile = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to the file containing the password required for auth.
|
||||
'';
|
||||
};
|
||||
insecurePasswordText = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Raw password required for auth.
|
||||
|
||||
It's strongly recommended to use 'passwordFile' instead of this option.
|
||||
|
||||
**Don't use this option unless you know what you're doing!**.
|
||||
It writes the password to the world-readable Nix store, which is a big security risk.
|
||||
More info: https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
(lib.mkIf cfg.rpcAuth.enable {
|
||||
assertion = lib.xor (cfg.rpcAuth.passwordFile == null) (cfg.rpcAuth.insecurePasswordText == null);
|
||||
message = ''
|
||||
RPC Auth is enabled for yt-dlp-web-ui! Exactly one RPC auth password source must be set!
|
||||
|
||||
Tip: You should set 'services.yt-dlp-web-ui.rpcAuth.passwordfile'!
|
||||
'';
|
||||
})
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
|
||||
|
||||
users.users = lib.mkIf (cfg.user == "yt-dlp-web-ui") {
|
||||
yt-dlp-web-ui = {
|
||||
inherit (cfg) group;
|
||||
isSystemUser = true;
|
||||
};
|
||||
};
|
||||
|
||||
users.groups = lib.mkIf (cfg.group == "yt-dlp-web-ui") { yt-dlp-web-ui = { }; };
|
||||
|
||||
systemd.services.yt-dlp-web-ui = {
|
||||
description = "yt-dlp-web-ui system service";
|
||||
after = [ "network.target" ];
|
||||
path = [ cfg.package pkgs.tree ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig =
|
||||
rec {
|
||||
ExecStart =
|
||||
let
|
||||
password =
|
||||
if cfg.rpcAuth.passwordFile == null
|
||||
then cfg.rpcAuth.insecurePasswordText
|
||||
else "$(cat ${cfg.rpcAuth.passwordFile})";
|
||||
args = [
|
||||
"-host ${cfg.host}"
|
||||
"-port ${builtins.toString cfg.port}"
|
||||
''-out "${cfg.downloadDir}"''
|
||||
"-qs ${builtins.toString cfg.queueSize}"
|
||||
] ++ (lib.optionals cfg.logging [
|
||||
"-fl"
|
||||
''-lf "/var/log/${LogsDirectory}/yt-dlp-web-ui.log"''
|
||||
]) ++ (lib.optionals cfg.rpcAuth.enable [
|
||||
"-auth"
|
||||
"-user ${cfg.rpcAuth.user}"
|
||||
"-pass ${password}"
|
||||
]);
|
||||
in
|
||||
"${lib.getExe cfg.package} ${lib.concatStringsSep " " args}";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = "read-only";
|
||||
StateDirectory = "yt-dlp-web-ui";
|
||||
WorkingDirectory = "/var/lib/${StateDirectory}"; # equivalent to the dir above
|
||||
LogsDirectory = "yt-dlp-web-ui";
|
||||
ReadWritePaths = [
|
||||
cfg.downloadDir
|
||||
];
|
||||
BindReadOnlyPaths = [
|
||||
builtins.storeDir
|
||||
# required for youtube DNS lookup
|
||||
"${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt"
|
||||
] ++ lib.optionals (cfg.rpcAuth.enable && cfg.rpcAuth.passwordFile != null) [
|
||||
cfg.rpcAuth.passwordFile
|
||||
];
|
||||
CapabilityBoundingSet = "";
|
||||
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
PrivateDevices = true;
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" "~@privileged" ];
|
||||
RestrictRealtime = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
ProtectHostname = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
52
nix/server.nix
Normal file
52
nix/server.nix
Normal file
@@ -0,0 +1,52 @@
|
||||
{ yt-dlp-web-ui-frontend, buildGoModule, lib, makeWrapper, yt-dlp, ... }:
|
||||
let
|
||||
fs = lib.fileset;
|
||||
common = import ./common.nix { inherit lib; };
|
||||
in
|
||||
buildGoModule {
|
||||
pname = "yt-dlp-web-ui";
|
||||
inherit (common) version;
|
||||
src = fs.toSource rec {
|
||||
root = ../.;
|
||||
fileset = fs.difference root (fs.unions [
|
||||
### LIST OF FILES TO IGNORE ###
|
||||
# frontend (this is included by the frontend.nix drv instead)
|
||||
../frontend
|
||||
# documentation
|
||||
../examples
|
||||
# docker
|
||||
../Dockerfile
|
||||
../docker-compose.yml
|
||||
# nix
|
||||
./devShell.nix
|
||||
../.envrc
|
||||
./tests
|
||||
# make
|
||||
../Makefile # this derivation does not use the project Makefile
|
||||
# repo commons
|
||||
../.github
|
||||
../README.md
|
||||
../LICENSE.md
|
||||
../.gitignore
|
||||
../.vscode
|
||||
]);
|
||||
};
|
||||
|
||||
# https://github.com/golang/go/issues/44507
|
||||
preBuild = ''
|
||||
cp -r ${yt-dlp-web-ui-frontend} frontend
|
||||
'';
|
||||
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/yt-dlp-web-ui \
|
||||
--prefix PATH : ${lib.makeBinPath [ yt-dlp ]}
|
||||
'';
|
||||
|
||||
vendorHash = "sha256-guM/U9DROJMx2ctPKBQis1YRhaf6fKvvwEWgswQKMG0=";
|
||||
|
||||
meta = common.meta // {
|
||||
mainProgram = "yt-dlp-web-ui";
|
||||
};
|
||||
}
|
||||
20
nix/tests/default.nix
Normal file
20
nix/tests/default.nix
Normal file
@@ -0,0 +1,20 @@
|
||||
{ self, pkgs }: {
|
||||
testServiceStarts = pkgs.testers.runNixOSTest (_: {
|
||||
name = "service-starts";
|
||||
nodes = {
|
||||
machine = _: {
|
||||
imports = [
|
||||
self.nixosModules.default
|
||||
];
|
||||
|
||||
services.yt-dlp-web-ui = {
|
||||
enable = true;
|
||||
downloadDir = "/var/lib/yt-dlp-web-ui";
|
||||
};
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
machine.wait_for_unit("yt-dlp-web-ui")
|
||||
'';
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user