This commit is contained in:
Lukas Wurzinger 2025-04-08 23:27:00 +02:00
parent b7e93fc970
commit ebf2035c54
No known key found for this signature in database
30 changed files with 305 additions and 462 deletions

View file

@ -35,11 +35,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1737621947,
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=",
"lastModified": 1742042642,
"narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
"owner": "cachix",
"repo": "cachix",
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5",
"rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
"type": "github"
},
"original": {
@ -95,11 +95,11 @@
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1743521698,
"narHash": "sha256-bvl7YmNZjGEZhyH86mGiJdhZGMaRot1TMM6cjwrwH6k=",
"lastModified": 1743783972,
"narHash": "sha256-5wPsNCnWmeLpLxavsftA9L7tnYgtlexV7FwLegxtpy4=",
"owner": "cachix",
"repo": "devenv",
"rev": "4917f16d045f12d938d0a29fb1cbfb9e24ae6d0d",
"rev": "2f53e2f867e0c2ba18b880e66169366e5f8ca554",
"type": "github"
},
"original": {
@ -211,11 +211,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1741352980,
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
"lastModified": 1743550720,
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
@ -245,6 +245,24 @@
"type": "github"
}
},
"flake-parts_4": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib_2"
},
"locked": {
"lastModified": 1743550720,
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flatpak": {
"locked": {
"lastModified": 1739444422,
@ -273,11 +291,11 @@
]
},
"locked": {
"lastModified": 1740849354,
"narHash": "sha256-oy33+t09FraucSZ2rZ6qnD1Y1c8azKKmQuCvF2ytUko=",
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "4a709a8ce9f8c08fa7ddb86761fe488ff7858a07",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
@ -406,6 +424,25 @@
"type": "github"
}
},
"musicomp": {
"inputs": {
"flake-parts": "flake-parts_4",
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1744068246,
"narHash": "sha256-ZrLuwXT0uRxa4hyMyCe/IG9lKZEqAM+lUgLKcCiZjbA=",
"ref": "refs/heads/main",
"rev": "66aa356585132605e8bd9cc630fab7416f3caf3d",
"revCount": 2,
"type": "git",
"url": "https://codeberg.org/helveticanonstandard/musicomp.git"
},
"original": {
"type": "git",
"url": "https://codeberg.org/helveticanonstandard/musicomp.git"
}
},
"nix": {
"inputs": {
"flake-compat": [
@ -446,11 +483,11 @@
]
},
"locked": {
"lastModified": 1743306489,
"narHash": "sha256-LROaIjSLo347cwcHRfSpqzEOa2FoLSeJwU4dOrGm55E=",
"lastModified": 1743911143,
"narHash": "sha256-4j4JPwr0TXHH4ZyorXN5yIcmqIQr0WYacsuPA4ktONo=",
"owner": "nix-community",
"repo": "nix-index-database",
"rev": "b3696bfb6c24aa61428839a99e8b40c53ac3a82d",
"rev": "a36f6a7148aec2c77d78e4466215cceb2f5f4bfb",
"type": "github"
},
"original": {
@ -462,16 +499,16 @@
"nixos-cosmic": {
"inputs": {
"flake-compat": "flake-compat_3",
"nixpkgs": "nixpkgs_6",
"nixpkgs": "nixpkgs_7",
"nixpkgs-stable": "nixpkgs-stable_2",
"rust-overlay": "rust-overlay_2"
},
"locked": {
"lastModified": 1743531084,
"narHash": "sha256-IOfLnebyXv2v8srLwKOx7TSi2NhHrvqL5AtpDEGVhJ4=",
"lastModified": 1744137608,
"narHash": "sha256-KEuKL7lM2ZqKzvaGIptVDAce29CAR4ZSgWtFD3PnpB0=",
"owner": "lilyinstarlight",
"repo": "nixos-cosmic",
"rev": "c231cbbfdd897b86efc9138a37ac302d5a29fb5d",
"rev": "d20b15f629985fe6900925bef462f947e4a75b2f",
"type": "github"
},
"original": {
@ -498,11 +535,26 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1740877520,
"narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=",
"lastModified": 1743296961,
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "147dee35aab2193b174e4c0868bd80ead5ce755c",
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs-lib_2": {
"locked": {
"lastModified": 1743296961,
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"type": "github"
},
"original": {
@ -529,11 +581,11 @@
},
"nixpkgs-stable_2": {
"locked": {
"lastModified": 1743367904,
"narHash": "sha256-sOos1jZGKmT6xxPvxGQyPTApOunXvScV4lNjBCXd/CI=",
"lastModified": 1743975612,
"narHash": "sha256-o4FjFOUmjSRMK7dn0TFdAT0RRWUWD+WsspPHa+qEQT8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7ffe0edc685f14b8c635e3d6591b0bbb97365e6c",
"rev": "a880f49904d68b5e53338d1e8c7bf80f59903928",
"type": "github"
},
"original": {
@ -609,11 +661,11 @@
},
"nixpkgs_6": {
"locked": {
"lastModified": 1743315132,
"narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=",
"lastModified": 1743964447,
"narHash": "sha256-nEo1t3Q0F+0jQ36HJfbJtiRU4OI+/0jX/iITURKe3EE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "52faf482a3889b7619003c0daec593a1912fddc1",
"rev": "063dece00c5a77e4a0ea24e5e5a5bd75232806f8",
"type": "github"
},
"original": {
@ -625,11 +677,27 @@
},
"nixpkgs_7": {
"locked": {
"lastModified": 1743076231,
"narHash": "sha256-yQugdVfi316qUfqzN8JMaA2vixl+45GxNm4oUfXlbgw=",
"lastModified": 1743964447,
"narHash": "sha256-nEo1t3Q0F+0jQ36HJfbJtiRU4OI+/0jX/iITURKe3EE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6c5963357f3c1c840201eda129a99d455074db04",
"rev": "063dece00c5a77e4a0ea24e5e5a5bd75232806f8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_8": {
"locked": {
"lastModified": 1743689281,
"narHash": "sha256-y7Hg5lwWhEOgflEHRfzSH96BOt26LaYfrYWzZ+VoVdg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2bfc080955153be0be56724be6fa5477b4eefabb",
"type": "github"
},
"original": {
@ -642,15 +710,15 @@
"phps": {
"inputs": {
"flake-compat": "flake-compat_4",
"nixpkgs": "nixpkgs_7",
"nixpkgs": "nixpkgs_8",
"utils": "utils"
},
"locked": {
"lastModified": 1743395855,
"narHash": "sha256-1OZa0T1PSRTHqQG2eiMDiqJPB8XSseoe7bKUCkG5C0U=",
"lastModified": 1744001863,
"narHash": "sha256-0pYw0Idtion++srUKsmGX7mq1weozdVE8gR+inoedUo=",
"owner": "fossar",
"repo": "nix-phps",
"rev": "87aa57df1dffc535756256efbd141c735852145f",
"rev": "220ed74315dc7cd64a6181efd3d583a3607ef01f",
"type": "github"
},
"original": {
@ -695,6 +763,7 @@
"flatpak": "flatpak",
"hardware": "hardware",
"lanzaboote": "lanzaboote",
"musicomp": "musicomp",
"nix-index-database": "nix-index-database",
"nixos-cosmic": "nixos-cosmic",
"nixpkgs": [
@ -733,11 +802,11 @@
]
},
"locked": {
"lastModified": 1743475035,
"narHash": "sha256-uLjVsb4Rxnp1zmFdPCDmdODd4RY6ETOeRj0IkC0ij/4=",
"lastModified": 1744079607,
"narHash": "sha256-5cog6Qd6w/bINdLO5mOysAHOHey8PwFXk4IWo+y+Czg=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "bee11c51c2cda3ac57c9e0149d94b86cc1b00d13",
"rev": "f6b62cc99c25e79a1c17e9fca91dc6b6faebec6c",
"type": "github"
},
"original": {

View file

@ -20,6 +20,7 @@
url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs";
};
musicomp.url = "git+https://codeberg.org/helveticanonstandard/musicomp.git";
};
nixConfig = {

View file

@ -1,5 +1,5 @@
{
services.flatpak.packages = [
"com.spotify.Client"
"io.github.dweymouth.supersonic"
];
}

View file

@ -1,5 +1,5 @@
{
services.flatpak.packages = [
"com.spotify.Client"
"io.github.dweymouth.supersonic"
];
}

View file

@ -0,0 +1,8 @@
{
users.users.futura = {
description = "Futura";
uid = 1001;
isNormalUser = true;
password = "futura";
};
}

View file

@ -1,5 +1,5 @@
{
services.flatpak.packages = [
"com.spotify.Client"
"io.github.dweymouth.supersonic"
];
}

View file

@ -25,6 +25,7 @@
xdebug.client_host = localhost
'';
# Wrap all PHP versions with the extensions I need and bundle composer
phps = lib.genAttrs supportedPhps (
phpName: let
phpBase = inputs.phps.packages.${pkgs.system}.${phpName};
@ -47,7 +48,6 @@
paths = [
phpWithEnv
phpWithEnv.packages.composer
pkgs.symfony-cli
];
};
in
@ -55,12 +55,32 @@
);
prefix = "/var/lib/phps";
# Tell Symfony's CLI where it can access the different PHP versions
symfony-cli = let
package = pkgs.symfony-cli;
in
pkgs.symlinkJoin {
inherit (package) pname version meta;
paths = [package];
buildInputs = [pkgs.makeWrapper];
postBuild = ''
wrapProgram $out/bin/${package.meta.mainProgram} \
--suffix PATH : ${pkgs.lib.makeBinPath (
builtins.attrValues phps
)}
'';
};
in {
nix.settings = {
substituters = ["https://fossar.cachix.org/"];
trusted-public-keys = ["fossar.cachix.org-1:Zv6FuqIboeHPWQS7ysLCJ7UT7xExb4OE8c4LyGb5AsE="];
};
# Link PHP installations so that PhpStorm knows about them
systemd.tmpfiles.settings =
builtins.mapAttrs (name: drv: {
"${prefix}/${name}"."L+".argument = drv.outPath;
@ -68,6 +88,8 @@ in {
phps;
environment.systemPackages = [
phps.${selectedPhp}.packages.composer
pkgs.jetbrains.phpstorm
phps.${selectedPhp}
symfony-cli
];
}

View file

@ -1,5 +0,0 @@
{pkgs, ...}: {
environment.systemPackages = [
pkgs.jetbrains.phpstorm
];
}

View file

@ -0,0 +1,35 @@
{
lib,
pkgs,
...
}: {
services = {
desktopManager = {
cosmic.enable = lib.mkForce false;
plasma6.enable = true;
};
displayManager = {
cosmic-greeter.enable = lib.mkForce false;
sddm = {
enable = true;
wayland.enable = true;
};
};
};
environment.systemPackages = [
pkgs.kdePackages.sddm-kcm
pkgs.kdePackages.discover
pkgs.kdePackages.kate
];
programs = {
kdeconnect.enable = true;
partition-manager.enable = true;
};
xdg.portal = {
xdgOpenUsePortal = true;
extraPortals = [pkgs.xdg-desktop-portal-gtk];
};
}

View file

@ -0,0 +1,5 @@
{
services.flatpak.packages = [
"io.github.dweymouth.supersonic"
];
}

View file

@ -0,0 +1,10 @@
{pkgs, ...}: {
environment.systemPackages = [
pkgs.gnumake
pkgs.unzip
pkgs.pv
pkgs.jq
pkgs.mariadb
pkgs.openssl
];
}

View file

@ -12,6 +12,12 @@
paths = [
config.services.vaultwarden.backupDir
config.services.syncthing.dataDir
config.services.forgejo.stateDir
config.services.postgresqlBackup.location
config.services.postgresqlBackup.location
# TODO: Add stateDir options for these
"/var/lib/headscale"
"/var/lib/navidrome"
];
passwordFile = config.age.secrets."restic-${attrName}".path;
pruneOpts = ["--keep-daily 7" "--keep-weekly 5" "--keep-monthly 12"];

View file

@ -0,0 +1,37 @@
# TODO: do this via tailscale?
# {
# config,
# lib,
# pkgs,
# ...
# }: let
# virtualHostName = "syncserver.helveticanonstandard.net";
# in {
# age.secrets = lib.mkSecrets {syncserver = {};};
#
# services.firefox-syncserver = {
# enable = true;
# secrets = config.age.secrets.syncserver.path;
# singleNode = {
# enable = true;
# hostname = virtualHostName;
# url = "https://${virtualHostName}";
# };
# settings = {
# port = 8070;
# };
# };
#
# services.nginx.virtualHosts.${config.services.firefox-syncserver.singleNode.hostname} = {
# enableACME = true;
# forceSSL = true;
#
# locations."/".proxyPass = let
# host = "127.0.0.1";
# port = builtins.toString config.services.firefox-syncserver.settings.port;
# in "http://${host}:${port}";
# };
#
# services.mysql.package = pkgs.mariadb;
# }
{}

View file

@ -1,6 +1,7 @@
{
config,
lib,
pkgs,
...
}: let
virtualHostName = "forgejo.helveticanonstandard.net";
@ -50,24 +51,26 @@ in {
secrets.mailer.PASSWD = config.age.secrets.forgejo-mailer.path;
};
systemd.services.forgejo.preStart = let
forgejo = lib.getExe config.services.forgejo.package;
passwordFile = config.age.secrets.forgejo-admin.path;
user = "helvetica";
email = "helvetica@helveticanonstandard.net";
in ''
if ! \
${forgejo} admin user change-password \
--username ${lib.escapeShellArg user} \
--password "$(cat -- ${lib.escapeShellArg passwordFile})"
then
${forgejo} admin user create \
--admin \
--email ${lib.escapeShellArg email} \
--username ${lib.escapeShellArg user} \
--password "$(cat -- ${lib.escapeShellArg passwordFile})"
fi
'';
systemd.services.forgejo.preStart = lib.getExe pkgs.writeShellApplication {
name = "forgejo-init-admin";
runtimeInputs = [
config.services.forgejo.package
];
text = let
passwordFile = config.age.secrets.forgejo-admin.path;
in ''
admins=$(admin user list --admin)
admins=$((admins - 1))
if ((admins < 1)); then
gitea admin user create \
--admin \
--email helvetica@helveticanonstandard.net \
--username helvetica \
--password "$(cat -- ${passwordFile})"
fi
'';
};
services.nginx.virtualHosts.${virtualHostName} = {
enableACME = true;

View file

@ -0,0 +1,10 @@
# {pkgs, ...}: {
# services.mysql.package = pkgs.mariadb;
#
# services.mysqlBackup = {
# enable = true;
# startAt = "*-*-* 02:00:00";
# location = "/srv/backup/postgresql";
# };
# }
{}

View file

@ -0,0 +1,23 @@
{config, ...}: let
virtualHostName = "mealie.helveticanonstandard.net";
in {
services.mealie = {
enable = true;
settings = {
BASE_URL = "https://${virtualHostName}";
ALLOW_SIGNUP = false;
};
listenAddress = "127.0.0.1";
port = 8040;
};
services.nginx.virtualHosts.${virtualHostName} = {
enableACME = true;
forceSSL = true;
locations."/".proxyPass = let
host = config.services.mealie.listenAddress;
port = builtins.toString config.services.mealie.port;
in "http://${host}:${port}";
};
}

View file

@ -0,0 +1,8 @@
{
services.postgresqlBackup = {
enable = true;
startAt = "*-*-* 02:00:00";
location = "/srv/backup/postgresql";
backupAll = true;
};
}

View file

@ -1,9 +1,14 @@
{
inputs,
self,
lib,
pkgs,
...
}: {
imports = [
inputs.musicomp.nixosModules.default
];
services.musicomp.jobs.main = {
music = "/srv/music";
comp = "/srv/compmusic";

View file

@ -1,116 +0,0 @@
{
self,
lib,
pkgs,
utils,
config,
...
}: let
inherit (lib) types;
inherit (utils.systemdUtils.unitOptions) unitOption;
in {
options.services.musicomp.jobs = lib.mkOption {
description = ''
Compression jobs to run with musicomp.
'';
default = {};
type = types.attrsOf (types.submodule {
options = {
music = lib.mkOption {
type = types.str;
description = ''
Source directory.
'';
example = "/srv/music";
};
comp = lib.mkOption {
type = types.str;
description = ''
Destination directory for compressed music.
'';
example = "/srv/comp";
};
post = lib.mkOption {
type = types.lines;
default = "";
description = ''
Shell commands that are run after compression has finished.
'';
};
workers = lib.mkOption {
type = lib.types.int;
default = 0;
description = ''
Number of workers.
'';
};
timerConfig = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf unitOption);
default = {
OnCalendar = "daily";
Persistent = true;
};
description = ''
When to run the job.
'';
};
package = lib.mkPackageOption self.packages.${pkgs.system} "musicomp" {};
inhibitsSleep = lib.mkOption {
default = false;
type = lib.types.bool;
example = true;
description = ''
Prevents the system from sleeping while running the job.
'';
};
};
});
};
config = {
systemd.services =
lib.mapAttrs'
(
name: job:
lib.nameValuePair "musicomp-jobs-${name}" {
wantedBy = ["multi-user.target"];
restartIfChanged = false;
script = ''
${lib.optionalString job.inhibitsSleep ''
${lib.getExe' pkgs.systemd "systemd-inhibit"} \
--mode block \
--who musicomp \
--what sleep \
--why ${lib.escapeShellArg "Scheduled musicomp ${name}"}
''}
${lib.getExe job.package} \
${lib.optionalString (job.workers > 0) "--workers ${job.workers}"} \
--verbose \
-- ${job.music} ${job.comp}
'';
postStart = job.post;
serviceConfig.Type = "oneshot";
}
)
config.services.musicomp.jobs;
systemd.timers =
lib.mapAttrs'
(name: job:
lib.nameValuePair "musicomp-jobs-${name}" {
wantedBy = ["timers.target"];
inherit (job) timerConfig;
})
(lib.filterAttrs (_: job: job.timerConfig != null) config.services.musicomp.jobs);
};
}

View file

@ -1,3 +0,0 @@
# musicomp
A music compression tool.

View file

@ -1,26 +0,0 @@
{
lib,
python3Packages,
opusTools,
}:
python3Packages.buildPythonApplication {
pname = "musicomp";
version = "0.1.0";
src = ./.;
pyproject = true;
doCheck = false;
build-system = [python3Packages.hatchling];
makeWrapperArgs = [
"--prefix"
"PATH"
":"
(lib.makeBinPath [opusTools])
];
meta.mainProgram = "musicomp";
}

View file

@ -1,17 +0,0 @@
[project]
name = "musicomp"
version = "0.1.0"
description = "Music compression tool"
authors = [{ name = "Lukas Wurzinger", email = "lukas@wrz.one" }]
readme = "README.md"
requires-python = ">=3.12"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project.scripts]
musicomp = "musicomp.cli:main"
[tool.hatch.build.targets.wheel]
packages = ["src/musicomp"]

View file

@ -1,3 +0,0 @@
from .cli import main
main()

View file

@ -1,6 +0,0 @@
from os import PathLike
from pathlib import Path
def clean(dst: str | PathLike[str]) -> None:
Path(dst).unlink(missing_ok=True)

View file

@ -1,117 +0,0 @@
import sys
from multiprocessing import Pool, cpu_count
from argparse import ArgumentParser, ArgumentTypeError, Namespace
from pathlib import Path
from .todo import Todo
args = Namespace()
def task(todo: Todo) -> None:
todo.run()
if args.verbose:
print("finished", todo, file=sys.stderr)
def main():
def workers_type_func(value: object) -> int:
try:
value = int(value) # pyright: ignore[reportArgumentType]
if value <= 0:
raise ArgumentTypeError(f"{value} is not a positive integer")
except ValueError:
raise ArgumentTypeError(f"{value} is not an integer")
return value
parser = ArgumentParser()
_ = parser.add_argument(
"-w",
"--workers",
default=cpu_count(),
type=workers_type_func,
help="amount of worker processes",
)
_ = parser.add_argument(
"-i",
"--interactive",
action="store_true",
help="prompt before running",
)
_ = parser.add_argument(
"-k",
"--keep",
action="store_true",
help="whether source files should be kept if both directories are the same",
)
_ = parser.add_argument(
"-r",
"--redo",
action="store_true",
help="whether everything should be re-encoded regardless of whether they have already been transcoded",
)
_ = parser.add_argument(
"-v",
"--verbose",
action="count",
default=0,
help="verbose output",
)
_ = parser.add_argument(
"music",
nargs=1,
type=Path,
help="the source directory",
)
_ = parser.add_argument(
"comp",
nargs=1,
type=Path,
help="the destination directory for compressed files",
)
global args
args = parser.parse_args(sys.argv[1:])
assert isinstance(args.workers, int) # pyright: ignore[reportAny]
assert isinstance(args.interactive, bool) # pyright: ignore[reportAny]
assert isinstance(args.keep, bool) # pyright: ignore[reportAny]
assert isinstance(args.redo, bool) # pyright: ignore[reportAny]
assert isinstance(args.verbose, int) # pyright: ignore[reportAny]
assert isinstance(args.music, list) # pyright: ignore[reportAny]
assert len(args.music) == 1 # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType]
assert isinstance(args.music[0], Path) # pyright: ignore[reportUnknownMemberType]
assert isinstance(args.comp, list) # pyright: ignore[reportAny]
assert len(args.comp) == 1 # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType]
assert isinstance(args.comp[0], Path) # pyright: ignore[reportUnknownMemberType]
src_dir = args.music[0] # pyright: ignore[reportUnknownMemberType]
dst_dir = args.comp[0] # pyright: ignore[reportUnknownMemberType]
plan = list(
Todo.plan(
src_dir,
dst_dir,
replace=src_dir.samefile(dst_dir) and not args.keep,
redo=args.redo,
)
)
if len(plan) == 0:
print("Nothing to do", file=sys.stderr)
sys.exit(0)
if args.verbose >= 1 or args.interactive:
print("Plan:", file=sys.stderr)
for todo in plan:
print(todo, file=sys.stderr)
if args.interactive:
result = input("Do you want to continue? [Y/n] ")
if result.lower() not in ("", "y", "yes"):
sys.exit(1)
with Pool(args.workers) as pool:
_ = pool.map(task, plan)

View file

@ -1,6 +0,0 @@
from os import PathLike
from pathlib import Path
def replace(src: str | PathLike[str]) -> None:
Path(src).unlink(missing_ok=True)

View file

@ -1,68 +0,0 @@
from pathlib import Path
from os import PathLike
from enum import StrEnum
from dataclasses import dataclass
from typing import Self, override
from collections.abc import Generator
from .transcode import transcode as todo_transcode
from .clean import clean as todo_clean
from .replace import replace as todo_replace
SRC_SUFFIX = ".flac"
DST_SUFFIX = ".opus"
class TodoAct(StrEnum):
TRANSCODE = "transcode"
CLEAN = "clean"
REPLACE = "replace"
@dataclass
class Todo:
act: TodoAct
src: str | PathLike[str]
dst: str | PathLike[str]
@classmethod
def plan(
cls: type[Self],
src_dir: str | PathLike[str],
dst_dir: str | PathLike[str],
replace: bool = False,
redo: bool = False,
) -> Generator[Self]:
def list_files(dir: str | PathLike[str], suffix: str) -> list[Path]:
files: list[Path] = []
for f in Path(dir).rglob("*"):
if f.is_file() and f.suffix == suffix:
files.append(f)
return files
src_files = list_files(src_dir, SRC_SUFFIX)
dst_files = list_files(dst_dir, DST_SUFFIX)
for f in src_files:
e = dst_dir / (f.relative_to(src_dir).with_suffix(DST_SUFFIX))
if redo or e not in dst_files:
yield cls(TodoAct.TRANSCODE, f, e)
if replace:
yield cls(TodoAct.REPLACE, f, e)
for f in dst_files:
e = src_dir / (f.relative_to(dst_dir).with_suffix(SRC_SUFFIX))
if e not in src_files:
yield cls(TodoAct.CLEAN, f, e)
def run(self) -> None:
match self.act:
case TodoAct.TRANSCODE:
todo_transcode(self.src, self.dst)
case TodoAct.CLEAN:
todo_clean(self.dst)
case TodoAct.REPLACE:
todo_replace(self.src)
@override
def __str__(self) -> str:
return f"{self.act} {self.src} -> {self.dst}"

View file

@ -1,34 +0,0 @@
from pathlib import Path
from subprocess import run
from os import PathLike
class TranscodingError(Exception):
pass
def transcode(src: str | PathLike[str], dst: str | PathLike[str]) -> None:
dst = Path(dst)
dst.parent.mkdir(parents=True, exist_ok=True)
if dst.is_file():
dst.unlink()
opusenc: tuple[str, ...] = (
"opusenc",
"--quiet",
"--bitrate",
"96.000",
"--music",
"--vbr",
"--comp",
"10",
"--",
str(src),
str(dst),
)
cp = run(opusenc)
if cp.returncode != 0:
raise TranscodingError(f"opusenc exited with code {cp.returncode}")

View file

@ -14,4 +14,6 @@ in {
"restic-vessel.age".publicKeys = (builtins.attrValues users) ++ [hosts.vessel];
"restic-abacus.age".publicKeys = (builtins.attrValues users) ++ [hosts.abacus];
"syncserver.age".publicKeys = (builtins.attrValues users) ++ [hosts.abacus];
}

BIN
secrets/syncserver.age Normal file

Binary file not shown.