feat: Jellyfin media server
This commit is contained in:
39
modules/nixos/deertopia/jellyfin.nix
Normal file
39
modules/nixos/deertopia/jellyfin.nix
Normal file
@@ -0,0 +1,39 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let cfg = config.sydnix.deertopia.jellyfin;
|
||||
in {
|
||||
options = {
|
||||
sydnix.deertopia.jellyfin = {
|
||||
enable = mkEnableOption "Deertopia's Jellyfin media server";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
sydnix.impermanence =
|
||||
let jcfg = config.services.jellyfin;
|
||||
in {
|
||||
directories = [
|
||||
jcfg.dataDir
|
||||
jcfg.configDir
|
||||
];
|
||||
cache.directories = [
|
||||
jcfg.cacheDir
|
||||
];
|
||||
};
|
||||
|
||||
services.jellyfin = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
|
||||
sydnix.deertopia.nginx.vhosts."watch".vhost = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://localhost:8096"; # Uses default port.
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -31,6 +31,11 @@ in {
|
||||
default = true;
|
||||
};
|
||||
|
||||
symlinkToAnnexHome = lib.mkOption {
|
||||
default = null;
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
};
|
||||
|
||||
path = lib.mkOption {
|
||||
description = ''
|
||||
Path to the repo.
|
||||
@@ -47,6 +52,9 @@ in {
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "annex";
|
||||
description = ''
|
||||
The user that the Git-annex assistant will be run as.
|
||||
'';
|
||||
};
|
||||
|
||||
group = lib.mkOption {
|
||||
@@ -81,11 +89,21 @@ in {
|
||||
# Necessary to enable cloning over SSH.
|
||||
isNormalUser = true;
|
||||
group = "annex";
|
||||
# TODO: Don't hardcode extra groups!
|
||||
extraGroups = [ "jellyfin" ];
|
||||
home = "/var/sydnix/annex";
|
||||
homeMode = "770";
|
||||
createHome = true;
|
||||
openssh.authorizedKeys.keyFiles = cfg.keyFiles;
|
||||
};
|
||||
|
||||
# HACK: Some Jellyfin libraries are served by Git-annex. Give Jellyfin
|
||||
# permission to symlink some of those libraries into the annex user's home
|
||||
# dir.
|
||||
users.users.${config.services.jellyfin.user}.extraGroups =
|
||||
lib.mkIf config.sydnix.deertopia.jellyfin.enable
|
||||
[ "annex" ];
|
||||
|
||||
system.activationScripts.initialiseUserAnnex =
|
||||
let gitconfig-file = pkgs.writeText "gitconfig" ''
|
||||
[user]
|
||||
@@ -95,23 +113,16 @@ in {
|
||||
defaultBranch = main
|
||||
[core]
|
||||
symlinks = true
|
||||
[safe]
|
||||
${lib.strings.concatMapStrings
|
||||
(repo: "\tdirectory = ${repo.path}\n")
|
||||
(builtins.attrValues cfg.repos)}
|
||||
'';
|
||||
in ''
|
||||
set -e
|
||||
|
||||
annexHome="${config.users.users.annex.home}"
|
||||
ln -sf "${gitconfig-file}" "$annexHome/.gitconfig"
|
||||
|
||||
# Symlink repos into annex's home for easy access. This is particularly nice for cloning:
|
||||
# git clone annex@deertopia.net:org
|
||||
# instead of
|
||||
# git clone annex@deertopia.net:/persist/deertopia.net/org
|
||||
# Less assumptions about the host's file system!
|
||||
${lib.strings.toShellVar "repos" (builtins.attrNames cfg.repos)}
|
||||
for repoPath in ''${!repos[@]}; do
|
||||
target="$annexHome/$(basename "$repoPath")"
|
||||
ln -sf "$repoPath" "$target"
|
||||
done
|
||||
'';
|
||||
|
||||
systemd.services =
|
||||
@@ -124,8 +135,18 @@ in {
|
||||
[ -e .git ] || git init
|
||||
[ -e .git/annex ] || git annex init
|
||||
|
||||
# Symlink repo into user `annex` for easy access.
|
||||
ln -sf "$(pwd)" "$HOME/"
|
||||
# git config set user.name "${cfg.user.name}"
|
||||
# git config set user.email "${cfg.user.email}"
|
||||
|
||||
# Symlink repos into annex's home for easy access. This is
|
||||
# particularly nice for cloning:
|
||||
# git clone annex@deertopia.net:org
|
||||
# instead of
|
||||
# git clone annex@deertopia.net:/persist/deertopia.net/dav/org
|
||||
# Less assumptions about the host's file system!
|
||||
annexHome="${config.users.users.annex.home}"
|
||||
${lib.optionalString (repo.symlinkToAnnexHome != null)
|
||||
''ln -sf "$PWD" "$annexHome/${repo.symlinkToAnnexHome}"''}
|
||||
|
||||
${lib.strings.toShellVar "remotes" repo.remotes}
|
||||
for remoteName in ''${!remotes[@]}; do
|
||||
@@ -140,26 +161,26 @@ in {
|
||||
'';
|
||||
|
||||
mkAssistantService = repo: {
|
||||
description = "git-annex assistant (${repo.path})";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig.User = repo.user;
|
||||
serviceConfig.WorkingDirectory = repo.path;
|
||||
path = [ pkgs.git pkgs.git-annex pkgs.openssh ];
|
||||
# Set the default file mode for new files created by this process to
|
||||
# 775. Oddly, `UMask = x` will set the mode to `0777 & x`, so 002
|
||||
# gets us the desired figure of 775. We want 775 because Nginx,
|
||||
# running under user `nginx` needs to be able to read/write these
|
||||
# files to server them over WebDAV.
|
||||
serviceConfig.UMask = "002";
|
||||
serviceConfig.ExecStartPre =
|
||||
lib.optionalString
|
||||
repo.managed
|
||||
"${pkgs.bash}/bin/bash ${init-if-necessary repo}";
|
||||
serviceConfig.ExecStart = ''
|
||||
${pkgs.git-annex}/bin/git-annex assistant --foreground
|
||||
'';
|
||||
};
|
||||
description = "git-annex assistant (${repo.path})";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig.User = repo.user;
|
||||
serviceConfig.WorkingDirectory = repo.path;
|
||||
path = [ pkgs.git pkgs.git-annex pkgs.openssh ];
|
||||
# Set the default file mode for new files created by this process to
|
||||
# 775. Oddly, `UMask = x` will set the mode to `0777 & x`, so 002
|
||||
# gets us the desired figure of 775. We want 775 because Nginx,
|
||||
# running under user `nginx` needs to be able to read/write these
|
||||
# files to server them over WebDAV.
|
||||
serviceConfig.UMask = "002";
|
||||
serviceConfig.ExecStartPre =
|
||||
lib.optionalString
|
||||
repo.managed
|
||||
"${pkgs.bash}/bin/bash ${init-if-necessary repo}";
|
||||
serviceConfig.ExecStart = ''
|
||||
${pkgs.git-annex}/bin/git-annex assistant --foreground
|
||||
'';
|
||||
};
|
||||
in builtins.listToAttrs
|
||||
(builtins.map
|
||||
(repo: {
|
||||
|
||||
@@ -38,6 +38,28 @@ in {
|
||||
Name of the group whose members have access to the persist directory.
|
||||
'';
|
||||
};
|
||||
|
||||
cache = {
|
||||
directories = mkOption {
|
||||
description = ''
|
||||
While functionally identical to `directories` (at the moment),
|
||||
`cache.directories` carries additional semantics: these directories
|
||||
/can/ be erased, but typically /shouldn't/ be.
|
||||
'';
|
||||
default = [];
|
||||
type = types.listOf types.anything;
|
||||
};
|
||||
|
||||
files = mkOption {
|
||||
description = ''
|
||||
While functionally identical to `files` (at the moment),
|
||||
`cache.files` carries additional semantics: these files /can/ be
|
||||
erased, but typically /shouldn't/ be.
|
||||
'';
|
||||
default = [];
|
||||
type = types.listOf types.anything;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -72,8 +94,8 @@ in {
|
||||
programs.fuse.userAllowOther = true;
|
||||
|
||||
environment.persistence."${cfg.persistDirectory}/root" = {
|
||||
directories = cfg.directories;
|
||||
files = cfg.files;
|
||||
directories = cfg.directories ++ cfg.cache.directories;
|
||||
files = cfg.files ++ cfg.cache.files;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user