home/modules/downloads-sorter: extend
This commit is contained in:
@@ -10,7 +10,7 @@ in
|
|||||||
options.services.downloads-sorter = {
|
options.services.downloads-sorter = {
|
||||||
enable = lib.mkEnableOption "downloads sorter units, path activated units to keep the download dir clean";
|
enable = lib.mkEnableOption "downloads sorter units, path activated units to keep the download dir clean";
|
||||||
|
|
||||||
downloadsDir = lib.mkOption {
|
downloadsDirectory = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
description = "Which directory to keep clean";
|
description = "Which directory to keep clean";
|
||||||
default = if config.xdg.userDirs.enable then config.xdg.userDirs.download else "${config.home.homeDirectory}/Downloads";
|
default = if config.xdg.userDirs.enable then config.xdg.userDirs.download else "${config.home.homeDirectory}/Downloads";
|
||||||
@@ -22,9 +22,57 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: allow specifying a dynamic filter together with a system path trigger in an attrset.
|
|
||||||
mappings = lib.mkOption {
|
mappings = lib.mkOption {
|
||||||
type = with lib.types; attrsOf (listOf str);
|
type = let
|
||||||
|
mappingType = lib.types.submodule ({ name, ... }: {
|
||||||
|
options = {
|
||||||
|
unitName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
The basename of the path/service unit responsible for this mapping
|
||||||
|
'';
|
||||||
|
default = "downloads-sorter@${name}";
|
||||||
|
example = "downloads-sorter@asdf";
|
||||||
|
};
|
||||||
|
|
||||||
|
dir = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
Absolute path to the directory where matching files should be moved.
|
||||||
|
'';
|
||||||
|
default = if builtins.substring 0 1 name == "/" then name else "${cfg.downloadsDirectory}/${name}";
|
||||||
|
defaultText = ''
|
||||||
|
if builtins.substring 0 1 name == "/" then name else "''${config.services.downloads-sorter.downloadsDirectory}/''${name}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
globs = lib.mkOption {
|
||||||
|
type = with lib.types; listOf str;
|
||||||
|
description = ''
|
||||||
|
A list of globs that match the files that should be moved.
|
||||||
|
'';
|
||||||
|
example = [
|
||||||
|
"*.jpg"
|
||||||
|
"IMG_*_2020_*.png"
|
||||||
|
];
|
||||||
|
apply = map (g: "${cfg.downloadsDirectory}/${g}");
|
||||||
|
};
|
||||||
|
|
||||||
|
createDirIfNotExists = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
description = ''
|
||||||
|
Whether to create the target directory if it does not exist yet.
|
||||||
|
|
||||||
|
Turn this off if you'd like the target directory to be a symlink or similar.
|
||||||
|
'';
|
||||||
|
default = true;
|
||||||
|
example = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO: allow specifying a dynamic filter together with a system path trigger in an attrset.
|
||||||
|
};
|
||||||
|
});
|
||||||
|
in with lib.types; attrsOf (coercedTo (listOf str) (globs: { inherit globs; }) mappingType);
|
||||||
description = ''
|
description = ''
|
||||||
A mapping from a file pattern to the location where it should be moved.
|
A mapping from a file pattern to the location where it should be moved.
|
||||||
|
|
||||||
@@ -37,22 +85,23 @@ in
|
|||||||
"*.png"
|
"*.png"
|
||||||
"*.jpg"
|
"*.jpg"
|
||||||
];
|
];
|
||||||
"documents" = [ "*.pdf" ];
|
"documents" = {
|
||||||
|
createDirIfNotExists = false;
|
||||||
|
globs = [ "*.pdf" ];
|
||||||
|
};
|
||||||
"/home/<user>/archives" = [ "*.rar" ];
|
"/home/<user>/archives" = [ "*.rar" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
systemd.user.paths = lib.mapAttrs' (dir: globs: let
|
systemd.user.paths = lib.mapAttrs' (dir: mapping: {
|
||||||
unitName = "downloads-sorter@${dir}";
|
name = mapping.unitName;
|
||||||
in {
|
|
||||||
name = unitName;
|
|
||||||
value = {
|
value = {
|
||||||
Install.WantedBy = [ "paths.target" ];
|
Install.WantedBy = [ "paths.target" ];
|
||||||
Path = {
|
Path = {
|
||||||
PathExistsGlob = map (g: "${cfg.downloadsDir}/${g}") globs;
|
PathExistsGlob = mapping.globs;
|
||||||
Unit = "${unitName}.service";
|
Unit = "${mapping.unitName}.service";
|
||||||
TriggerLimitIntervalSec = "1s";
|
TriggerLimitIntervalSec = "1s";
|
||||||
TriggerLimitBurst = "1";
|
TriggerLimitBurst = "1";
|
||||||
};
|
};
|
||||||
@@ -60,29 +109,25 @@ in
|
|||||||
}) cfg.mappings;
|
}) cfg.mappings;
|
||||||
|
|
||||||
# TODO: deduplicate
|
# TODO: deduplicate
|
||||||
systemd.user.services = lib.mapAttrs' (dir: globs: let
|
systemd.user.services = lib.mapAttrs' (dir: mapping: {
|
||||||
unitName = "downloads-sorter@${dir}";
|
name = mapping.unitName;
|
||||||
in {
|
|
||||||
name = unitName;
|
|
||||||
value = {
|
value = {
|
||||||
Unit.Description = "Downloads directory watchdog, sorts the downloads directory";
|
Unit.Description = "Downloads directory watchdog, sorts the downloads directory";
|
||||||
Service = {
|
Service = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
SyslogIdentifier = unitName;
|
SyslogIdentifier = mapping.unitName;
|
||||||
ExecStart = let
|
ExecStart = let
|
||||||
absolutePath = if (builtins.substring 0 1 dir) == "/" then dir else "${cfg.downloadsDir}/${dir}";
|
|
||||||
|
|
||||||
script = pkgs.writeShellApplication {
|
script = pkgs.writeShellApplication {
|
||||||
name = "downloads-sorter-${dir}.sh";
|
name = "downloads-sorter-${dir}.sh";
|
||||||
runtimeInputs = [ pkgs.coreutils ];
|
runtimeInputs = [ pkgs.coreutils ];
|
||||||
text = ''
|
text = ''
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|
||||||
FILES=(${lib.concatMapStringsSep " " (g: "'${cfg.downloadsDir}'/${g}") globs})
|
FILES=(${builtins.concatStringsSep " " mapping.globs})
|
||||||
|
|
||||||
for file in "''${FILES[@]}"; do
|
for file in "''${FILES[@]}"; do
|
||||||
echo "$file -> ${absolutePath}"
|
echo "$file -> ${mapping.dir}"
|
||||||
mv "$file" '${absolutePath}'
|
mv "$file" '${mapping.dir}'
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -100,7 +145,11 @@ in
|
|||||||
}) cfg.mappings;
|
}) cfg.mappings;
|
||||||
|
|
||||||
systemd.user.tmpfiles.settings."10-downloads-sorter-service" = let
|
systemd.user.tmpfiles.settings."10-downloads-sorter-service" = let
|
||||||
absolutePaths = map (p: if (builtins.substring 0 1 p) == "/" then p else "${cfg.downloadsDir}/${p}") (builtins.attrNames cfg.mappings);
|
absolutePaths = lib.pipe cfg.mappings [
|
||||||
|
builtins.attrValues
|
||||||
|
(builtins.filter (m: m.createDirIfNotExists))
|
||||||
|
(map (m: m.dir))
|
||||||
|
];
|
||||||
in lib.genAttrs absolutePaths (_: {
|
in lib.genAttrs absolutePaths (_: {
|
||||||
d = {
|
d = {
|
||||||
user = config.home.username;
|
user = config.home.username;
|
||||||
|
Reference in New Issue
Block a user