diff --git a/flake.lock b/flake.lock index 1f4c50c..2cd2a2b 100644 --- a/flake.lock +++ b/flake.lock @@ -35,11 +35,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1742042642, - "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=", + "lastModified": 1737621947, + "narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=", "owner": "cachix", "repo": "cachix", - "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203", + "rev": "f65a3cd5e339c223471e64c051434616e18cc4f5", "type": "github" }, "original": { @@ -95,11 +95,11 @@ "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1743783972, - "narHash": "sha256-5wPsNCnWmeLpLxavsftA9L7tnYgtlexV7FwLegxtpy4=", + "lastModified": 1743521698, + "narHash": "sha256-bvl7YmNZjGEZhyH86mGiJdhZGMaRot1TMM6cjwrwH6k=", "owner": "cachix", "repo": "devenv", - "rev": "2f53e2f867e0c2ba18b880e66169366e5f8ca554", + "rev": "4917f16d045f12d938d0a29fb1cbfb9e24ae6d0d", "type": "github" }, "original": { @@ -211,11 +211,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1741352980, + "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", "type": "github" }, "original": { @@ -245,24 +245,6 @@ "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, @@ -291,11 +273,11 @@ ] }, "locked": { - "lastModified": 1742649964, - "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", + "lastModified": 1740849354, + "narHash": "sha256-oy33+t09FraucSZ2rZ6qnD1Y1c8azKKmQuCvF2ytUko=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "rev": "4a709a8ce9f8c08fa7ddb86761fe488ff7858a07", "type": "github" }, "original": { @@ -424,25 +406,6 @@ "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": [ @@ -483,11 +446,11 @@ ] }, "locked": { - "lastModified": 1743911143, - "narHash": "sha256-4j4JPwr0TXHH4ZyorXN5yIcmqIQr0WYacsuPA4ktONo=", + "lastModified": 1743306489, + "narHash": "sha256-LROaIjSLo347cwcHRfSpqzEOa2FoLSeJwU4dOrGm55E=", "owner": "nix-community", "repo": "nix-index-database", - "rev": "a36f6a7148aec2c77d78e4466215cceb2f5f4bfb", + "rev": "b3696bfb6c24aa61428839a99e8b40c53ac3a82d", "type": "github" }, "original": { @@ -499,16 +462,16 @@ "nixos-cosmic": { "inputs": { "flake-compat": "flake-compat_3", - "nixpkgs": "nixpkgs_7", + "nixpkgs": "nixpkgs_6", "nixpkgs-stable": "nixpkgs-stable_2", "rust-overlay": "rust-overlay_2" }, "locked": { - "lastModified": 1744137608, - "narHash": "sha256-KEuKL7lM2ZqKzvaGIptVDAce29CAR4ZSgWtFD3PnpB0=", + "lastModified": 1743531084, + "narHash": "sha256-IOfLnebyXv2v8srLwKOx7TSi2NhHrvqL5AtpDEGVhJ4=", "owner": "lilyinstarlight", "repo": "nixos-cosmic", - "rev": "d20b15f629985fe6900925bef462f947e4a75b2f", + "rev": "c231cbbfdd897b86efc9138a37ac302d5a29fb5d", "type": "github" }, "original": { @@ -535,26 +498,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "lastModified": 1740877520, + "narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=", "owner": "nix-community", "repo": "nixpkgs.lib", - "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", + "rev": "147dee35aab2193b174e4c0868bd80ead5ce755c", "type": "github" }, "original": { @@ -581,11 +529,11 @@ }, "nixpkgs-stable_2": { "locked": { - "lastModified": 1743975612, - "narHash": "sha256-o4FjFOUmjSRMK7dn0TFdAT0RRWUWD+WsspPHa+qEQT8=", + "lastModified": 1743367904, + "narHash": "sha256-sOos1jZGKmT6xxPvxGQyPTApOunXvScV4lNjBCXd/CI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a880f49904d68b5e53338d1e8c7bf80f59903928", + "rev": "7ffe0edc685f14b8c635e3d6591b0bbb97365e6c", "type": "github" }, "original": { @@ -661,11 +609,11 @@ }, "nixpkgs_6": { "locked": { - "lastModified": 1743964447, - "narHash": "sha256-nEo1t3Q0F+0jQ36HJfbJtiRU4OI+/0jX/iITURKe3EE=", + "lastModified": 1743315132, + "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "063dece00c5a77e4a0ea24e5e5a5bd75232806f8", + "rev": "52faf482a3889b7619003c0daec593a1912fddc1", "type": "github" }, "original": { @@ -677,27 +625,11 @@ }, "nixpkgs_7": { "locked": { - "lastModified": 1743964447, - "narHash": "sha256-nEo1t3Q0F+0jQ36HJfbJtiRU4OI+/0jX/iITURKe3EE=", + "lastModified": 1743076231, + "narHash": "sha256-yQugdVfi316qUfqzN8JMaA2vixl+45GxNm4oUfXlbgw=", "owner": "NixOS", "repo": "nixpkgs", - "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", + "rev": "6c5963357f3c1c840201eda129a99d455074db04", "type": "github" }, "original": { @@ -710,15 +642,15 @@ "phps": { "inputs": { "flake-compat": "flake-compat_4", - "nixpkgs": "nixpkgs_8", + "nixpkgs": "nixpkgs_7", "utils": "utils" }, "locked": { - "lastModified": 1744001863, - "narHash": "sha256-0pYw0Idtion++srUKsmGX7mq1weozdVE8gR+inoedUo=", + "lastModified": 1743395855, + "narHash": "sha256-1OZa0T1PSRTHqQG2eiMDiqJPB8XSseoe7bKUCkG5C0U=", "owner": "fossar", "repo": "nix-phps", - "rev": "220ed74315dc7cd64a6181efd3d583a3607ef01f", + "rev": "87aa57df1dffc535756256efbd141c735852145f", "type": "github" }, "original": { @@ -763,7 +695,6 @@ "flatpak": "flatpak", "hardware": "hardware", "lanzaboote": "lanzaboote", - "musicomp": "musicomp", "nix-index-database": "nix-index-database", "nixos-cosmic": "nixos-cosmic", "nixpkgs": [ @@ -802,11 +733,11 @@ ] }, "locked": { - "lastModified": 1744079607, - "narHash": "sha256-5cog6Qd6w/bINdLO5mOysAHOHey8PwFXk4IWo+y+Czg=", + "lastModified": 1743475035, + "narHash": "sha256-uLjVsb4Rxnp1zmFdPCDmdODd4RY6ETOeRj0IkC0ij/4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "f6b62cc99c25e79a1c17e9fca91dc6b6faebec6c", + "rev": "bee11c51c2cda3ac57c9e0149d94b86cc1b00d13", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3ff0627..f527322 100644 --- a/flake.nix +++ b/flake.nix @@ -20,7 +20,6 @@ url = "github:nix-community/nix-index-database"; inputs.nixpkgs.follows = "nixpkgs"; }; - musicomp.url = "git+https://codeberg.org/helveticanonstandard/musicomp.git"; }; nixConfig = { diff --git a/hosts/headful/flamingo/supersonic.nix b/hosts/headful/flamingo/spotify.nix similarity index 52% rename from hosts/headful/flamingo/supersonic.nix rename to hosts/headful/flamingo/spotify.nix index 8b4dba9..79817b7 100644 --- a/hosts/headful/flamingo/supersonic.nix +++ b/hosts/headful/flamingo/spotify.nix @@ -1,5 +1,5 @@ { services.flatpak.packages = [ - "io.github.dweymouth.supersonic" + "com.spotify.Client" ]; } diff --git a/hosts/headful/glacier/users.nix b/hosts/headful/glacier/users.nix deleted file mode 100644 index 2ad58bc..0000000 --- a/hosts/headful/glacier/users.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - users.users.futura = { - description = "Futura"; - uid = 1001; - isNormalUser = true; - password = "futura"; - }; -} diff --git a/hosts/headful/glacier/supersonic.nix b/hosts/headful/insomniac/spotify.nix similarity index 52% rename from hosts/headful/glacier/supersonic.nix rename to hosts/headful/insomniac/spotify.nix index 8b4dba9..79817b7 100644 --- a/hosts/headful/glacier/supersonic.nix +++ b/hosts/headful/insomniac/spotify.nix @@ -1,5 +1,5 @@ { services.flatpak.packages = [ - "io.github.dweymouth.supersonic" + "com.spotify.Client" ]; } diff --git a/hosts/headful/insomniac/supersonic.nix b/hosts/headful/insomniac/supersonic.nix deleted file mode 100644 index 8b4dba9..0000000 --- a/hosts/headful/insomniac/supersonic.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ - services.flatpak.packages = [ - "io.github.dweymouth.supersonic" - ]; -} diff --git a/hosts/headful/work/php.nix b/hosts/headful/work/phps.nix similarity index 68% rename from hosts/headful/work/php.nix rename to hosts/headful/work/phps.nix index 7e8e644..be4b232 100644 --- a/hosts/headful/work/php.nix +++ b/hosts/headful/work/phps.nix @@ -25,7 +25,6 @@ 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}; @@ -48,6 +47,7 @@ paths = [ phpWithEnv phpWithEnv.packages.composer + pkgs.symfony-cli ]; }; in @@ -55,32 +55,12 @@ ); 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; @@ -88,8 +68,6 @@ in { phps; environment.systemPackages = [ - pkgs.jetbrains.phpstorm - phps.${selectedPhp} - symfony-cli + phps.${selectedPhp}.packages.composer ]; } diff --git a/hosts/headful/work/phpstorm.nix b/hosts/headful/work/phpstorm.nix new file mode 100644 index 0000000..0796603 --- /dev/null +++ b/hosts/headful/work/phpstorm.nix @@ -0,0 +1,5 @@ +{pkgs, ...}: { + environment.systemPackages = [ + pkgs.jetbrains.phpstorm + ]; +} diff --git a/hosts/headful/work/plasma.nix b/hosts/headful/work/plasma.nix deleted file mode 100644 index 03996b4..0000000 --- a/hosts/headful/work/plasma.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ - 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]; - }; -} diff --git a/hosts/headful/work/supersonic.nix b/hosts/headful/work/spotify.nix similarity index 52% rename from hosts/headful/work/supersonic.nix rename to hosts/headful/work/spotify.nix index 8b4dba9..79817b7 100644 --- a/hosts/headful/work/supersonic.nix +++ b/hosts/headful/work/spotify.nix @@ -1,5 +1,5 @@ { services.flatpak.packages = [ - "io.github.dweymouth.supersonic" + "com.spotify.Client" ]; } diff --git a/hosts/headful/work/tools.nix b/hosts/headful/work/tools.nix deleted file mode 100644 index 2c2281e..0000000 --- a/hosts/headful/work/tools.nix +++ /dev/null @@ -1,10 +0,0 @@ -{pkgs, ...}: { - environment.systemPackages = [ - pkgs.gnumake - pkgs.unzip - pkgs.pv - pkgs.jq - pkgs.mariadb - pkgs.openssl - ]; -} diff --git a/hosts/headless/abacus/backup.nix b/hosts/headless/abacus/backup.nix index d6cef2f..9156ae9 100644 --- a/hosts/headless/abacus/backup.nix +++ b/hosts/headless/abacus/backup.nix @@ -12,12 +12,6 @@ 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"]; diff --git a/hosts/headless/abacus/firefox-syncserver.nix b/hosts/headless/abacus/firefox-syncserver.nix deleted file mode 100644 index 40e274a..0000000 --- a/hosts/headless/abacus/firefox-syncserver.nix +++ /dev/null @@ -1,37 +0,0 @@ -# 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; -# } -{} diff --git a/hosts/headless/abacus/forgejo.nix b/hosts/headless/abacus/forgejo.nix index 4cc5186..c52d7d2 100644 --- a/hosts/headless/abacus/forgejo.nix +++ b/hosts/headless/abacus/forgejo.nix @@ -1,10 +1,9 @@ { config, lib, - pkgs, ... }: let - virtualHostName = "forgejo.helveticanonstandard.net"; + virtualHostName = "tea.wrz.one"; in { age.secrets = lib.mkSecrets { forgejo-mailer = { @@ -47,30 +46,28 @@ in { USER = "lukas@wrz.one"; }; }; - secrets.mailer.PASSWD = config.age.secrets.forgejo-mailer.path; }; - 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 - ''; - }; + systemd.services.forgejo.preStart = let + forgejo = lib.getExe config.services.forgejo.package; + passwordFile = config.age.secrets.forgejo-admin.path; + # TODO + user = "lukas"; + email = "lukas@wrz.one"; + 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 + ''; services.nginx.virtualHosts.${virtualHostName} = { enableACME = true; diff --git a/hosts/headless/abacus/mariadb.nix b/hosts/headless/abacus/mariadb.nix deleted file mode 100644 index 6f40a90..0000000 --- a/hosts/headless/abacus/mariadb.nix +++ /dev/null @@ -1,10 +0,0 @@ -# {pkgs, ...}: { -# services.mysql.package = pkgs.mariadb; -# -# services.mysqlBackup = { -# enable = true; -# startAt = "*-*-* 02:00:00"; -# location = "/srv/backup/postgresql"; -# }; -# } -{} diff --git a/hosts/headless/abacus/mealie.nix b/hosts/headless/abacus/mealie.nix deleted file mode 100644 index 7601750..0000000 --- a/hosts/headless/abacus/mealie.nix +++ /dev/null @@ -1,23 +0,0 @@ -{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}"; - }; -} diff --git a/hosts/headless/abacus/navidrome.nix b/hosts/headless/abacus/navidrome.nix index 3470813..6c4b1e5 100644 --- a/hosts/headless/abacus/navidrome.nix +++ b/hosts/headless/abacus/navidrome.nix @@ -7,7 +7,6 @@ in { Address = "localhost"; Port = 8050; MusicFolder = "/srv/music"; - EnableSharing = true; }; }; diff --git a/hosts/headless/abacus/postgresql.nix b/hosts/headless/abacus/postgresql.nix deleted file mode 100644 index 7bff95f..0000000 --- a/hosts/headless/abacus/postgresql.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - services.postgresqlBackup = { - enable = true; - startAt = "*-*-* 02:00:00"; - location = "/srv/backup/postgresql"; - backupAll = true; - }; -} diff --git a/hosts/headless/vessel/musicomp.nix b/hosts/headless/vessel/musicomp.nix index 56b75b2..ed4c090 100644 --- a/hosts/headless/vessel/musicomp.nix +++ b/hosts/headless/vessel/musicomp.nix @@ -1,14 +1,9 @@ { - inputs, self, lib, pkgs, ... }: { - imports = [ - inputs.musicomp.nixosModules.default - ]; - services.musicomp.jobs.main = { music = "/srv/music"; comp = "/srv/compmusic"; diff --git a/modules/musicomp.nix b/modules/musicomp.nix new file mode 100644 index 0000000..e78ed31 --- /dev/null +++ b/modules/musicomp.nix @@ -0,0 +1,116 @@ +{ + 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); + }; +} diff --git a/packages/musicomp/README.md b/packages/musicomp/README.md new file mode 100644 index 0000000..fcec2e7 --- /dev/null +++ b/packages/musicomp/README.md @@ -0,0 +1,3 @@ +# musicomp + +A music compression tool. diff --git a/packages/musicomp/package.nix b/packages/musicomp/package.nix new file mode 100644 index 0000000..28f7348 --- /dev/null +++ b/packages/musicomp/package.nix @@ -0,0 +1,26 @@ +{ + 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"; +} diff --git a/packages/musicomp/pyproject.toml b/packages/musicomp/pyproject.toml new file mode 100644 index 0000000..4a0d811 --- /dev/null +++ b/packages/musicomp/pyproject.toml @@ -0,0 +1,17 @@ +[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"] diff --git a/packages/musicomp/src/musicomp/__main__.py b/packages/musicomp/src/musicomp/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/packages/musicomp/src/musicomp/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/packages/musicomp/src/musicomp/clean.py b/packages/musicomp/src/musicomp/clean.py new file mode 100644 index 0000000..2e2107d --- /dev/null +++ b/packages/musicomp/src/musicomp/clean.py @@ -0,0 +1,6 @@ +from os import PathLike +from pathlib import Path + + +def clean(dst: str | PathLike[str]) -> None: + Path(dst).unlink(missing_ok=True) diff --git a/packages/musicomp/src/musicomp/cli.py b/packages/musicomp/src/musicomp/cli.py new file mode 100644 index 0000000..c9fc7aa --- /dev/null +++ b/packages/musicomp/src/musicomp/cli.py @@ -0,0 +1,117 @@ +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) diff --git a/packages/musicomp/src/musicomp/replace.py b/packages/musicomp/src/musicomp/replace.py new file mode 100644 index 0000000..097f5f3 --- /dev/null +++ b/packages/musicomp/src/musicomp/replace.py @@ -0,0 +1,6 @@ +from os import PathLike +from pathlib import Path + + +def replace(src: str | PathLike[str]) -> None: + Path(src).unlink(missing_ok=True) diff --git a/packages/musicomp/src/musicomp/todo.py b/packages/musicomp/src/musicomp/todo.py new file mode 100644 index 0000000..b9e2142 --- /dev/null +++ b/packages/musicomp/src/musicomp/todo.py @@ -0,0 +1,68 @@ +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}" diff --git a/packages/musicomp/src/musicomp/transcode.py b/packages/musicomp/src/musicomp/transcode.py new file mode 100644 index 0000000..7ccd74b --- /dev/null +++ b/packages/musicomp/src/musicomp/transcode.py @@ -0,0 +1,34 @@ +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}") diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 0a37768..adda8aa 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -14,6 +14,4 @@ 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]; } diff --git a/secrets/syncserver.age b/secrets/syncserver.age deleted file mode 100644 index 6256bf7..0000000 Binary files a/secrets/syncserver.age and /dev/null differ