diff --git a/README.md b/README.md index 66bb38c..7bb75ee 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ A tool to identify missing albums on MusicBrainz from Deezer releases for artist - 🎨 **Interactive HTML Report**: Filter by type (add/update) and by artist - 🐍 **Python-based**: Clean, functional codebase with proper logging - ❄️ **Nix Support**: Fully packaged with Nix flake for reproducible builds +- 🚀 **NixOS Module**: Automated deployment with systemd service and timer +- 🌐 **Web Serving**: Ready for deployment with Caddy or any web server ## Requirements @@ -58,6 +60,7 @@ LIDARR_API_KEY=your-api-key-here # Optional SAMBL_URL=https://sambl.lioncat6.com MAX_ARTISTS=5 # Limit number of artists processed (0 = no limit) +OUTPUT_DIR=. # Directory where output files will be written (default: current directory) ``` ### Getting Your Lidarr API Key @@ -82,7 +85,7 @@ The script will: 1. Fetch all artists from Lidarr with `monitorNewItems` set to "new" or "all" 2. For each artist, query SAMBL to find missing albums on MusicBrainz 3. Generate submission links (a-tisket and Harmony) -4. Create two output files: +4. Create two output files in the directory specified by `OUTPUT_DIR` (or current directory): - `missing_albums.json` - Machine-readable JSON report - `missing_albums.html` - Interactive HTML report with filters @@ -110,7 +113,13 @@ Interactive HTML report with: │ ├── main.py # Main application logic │ ├── html_report.py # HTML report generation │ └── pyproject.toml # Python package configuration +├── nix/ +│ └── package.nix # Shared Nix package definition +├── nixos/ +│ ├── lidarr-mb-gap.nix # NixOS module for automated deployment +│ └── DEPLOYMENT.md # Detailed deployment guide ├── flake.nix # Nix flake definition +├── Caddyfile # Caddy configuration for serving reports ├── .env # Environment variables (create this) └── README.md ``` @@ -136,6 +145,40 @@ The application uses Python's logging module with systemd-friendly output: Perfect for running as a systemd service. +## Deployment + +### NixOS Automated Deployment + +This project includes a NixOS module for automated deployment with: +- Systemd service for report generation +- Systemd timer for periodic execution (configurable) +- Optional automatic rsync sync to VPS +- SSH key management for secure VPS access +- Caddy configuration for serving the HTML report + +See [nixos/DEPLOYMENT.md](nixos/DEPLOYMENT.md) for detailed deployment instructions. + +### Quick Start with NixOS + +```nix +# In your NixOS configuration +services.lidarr-mb-gap = { + enable = true; + package = lidarr-mb-gap.packages.${system}.lidarr-mb-gap; # From flake + reportDir = "/var/lib/lidarr-mb-gap/reports"; + envFile = "/var/lib/lidarr-mb-gap/.env"; + runInterval = "daily"; + + # Optional: Auto-sync to VPS + syncToVPS = true; + vpsHost = "user@vps"; + vpsPath = "/var/www/html"; + sshKeyFile = "/var/lib/lidarr-mb-gap/.ssh/id_ed25519"; +}; +``` + +For complete setup instructions, including SSH key configuration and Caddy setup, see the [deployment guide](nixos/DEPLOYMENT.md). + ## Example Output ``` @@ -166,6 +209,11 @@ Perfect for running as a systemd service. - Make sure you're running from the project root - If using Nix, ensure the flake is properly built +### Output files not appearing +- Check that `OUTPUT_DIR` environment variable points to a writable directory +- Ensure the directory exists and has proper permissions +- Check logs for any permission errors + ## Contributing 1. Format code with `black src/` diff --git a/flake.nix b/flake.nix index f6e9053..d8e7908 100644 --- a/flake.nix +++ b/flake.nix @@ -21,18 +21,9 @@ let pkgs = import nixpkgs { inherit system; }; lib = pkgs.lib; - lidarr-mb-gap = pkgs.python3Packages.buildPythonApplication { - pname = "lidarr-mb-gap"; - version = "1.0.0"; + lidarr-mb-gap = import ./nix/package.nix { + inherit pkgs lib; src = lib.cleanSource ./src; - format = "pyproject"; - nativeBuildInputs = with pkgs.python3Packages; [ - setuptools - ]; - propagatedBuildInputs = with pkgs.python3Packages; [ - requests - python-dotenv - ]; }; in { diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..5305c59 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, src }: + +pkgs.python3Packages.buildPythonApplication { + pname = "lidarr-mb-gap"; + version = "1.0.0"; + inherit src; + format = "pyproject"; + nativeBuildInputs = with pkgs.python3Packages; [ + setuptools + ]; + propagatedBuildInputs = with pkgs.python3Packages; [ + requests + python-dotenv + ]; +} + diff --git a/nixos/DEPLOYMENT.md b/nixos/DEPLOYMENT.md index e9c67ed..d0dbd92 100644 --- a/nixos/DEPLOYMENT.md +++ b/nixos/DEPLOYMENT.md @@ -34,6 +34,13 @@ Add the module to your NixOS configuration. You have two options: syncToVPS = true; vpsHost = "user@vps"; # Your SSH host alias vpsPath = "/var/www/html"; + sshKeyFile = "/var/lib/lidarr-mb-gap/.ssh/id_ed25519"; + sshKnownHosts = { + vps = { + hostNames = [ "vps" "vps.example.com" ]; + publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; # Get from ssh-keyscan + }; + }; }; } ``` @@ -62,6 +69,18 @@ Add the module to your NixOS configuration. You have two options: services.lidarr-mb-gap.reportDir = "/var/lib/lidarr-mb-gap/reports"; services.lidarr-mb-gap.envFile = "/var/lib/lidarr-mb-gap/.env"; services.lidarr-mb-gap.runInterval = "daily"; + + # Optional: SSH configuration for VPS sync + services.lidarr-mb-gap.syncToVPS = true; + services.lidarr-mb-gap.vpsHost = "user@vps"; + services.lidarr-mb-gap.vpsPath = "/var/www/html"; + services.lidarr-mb-gap.sshKeyFile = "/var/lib/lidarr-mb-gap/.ssh/id_ed25519"; + services.lidarr-mb-gap.sshKnownHosts = { + vps = { + hostNames = [ "vps" "vps.example.com" ]; + publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; + }; + }; } ./configuration.nix ]; @@ -100,20 +119,96 @@ sudo chmod 600 /var/lib/lidarr-mb-gap/.env ### 3. Configure SSH for rsync (if using auto-sync) -If you enabled `syncToVPS`, set up SSH key authentication: +If you enabled `syncToVPS`, you need to set up SSH key authentication for the `lidarr-mb-gap` user. + +#### Step 1: Generate SSH Key Pair ```bash -# On the server -ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_lidarr -ssh-copy-id -i ~/.ssh/id_ed25519_lidarr.pub user@vps +# Generate a dedicated SSH key for the service +sudo -u lidarr-mb-gap ssh-keygen -t ed25519 -f /var/lib/lidarr-mb-gap/.ssh/id_ed25519 -N "" -# Add to ~/.ssh/config -Host vps - HostName your-vps-ip-or-domain - User your-username - IdentityFile ~/.ssh/id_ed25519_lidarr +# Or generate as your user and copy it +ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_lidarr -N "" +sudo mkdir -p /var/lib/lidarr-mb-gap/.ssh +sudo cp ~/.ssh/id_ed25519_lidarr /var/lib/lidarr-mb-gap/.ssh/id_ed25519 +sudo cp ~/.ssh/id_ed25519_lidarr.pub /var/lib/lidarr-mb-gap/.ssh/id_ed25519.pub +sudo chown -R lidarr-mb-gap:lidarr-mb-gap /var/lib/lidarr-mb-gap/.ssh +sudo chmod 600 /var/lib/lidarr-mb-gap/.ssh/id_ed25519 +sudo chmod 644 /var/lib/lidarr-mb-gap/.ssh/id_ed25519.pub ``` +#### Step 2: Add Public Key to VPS + +```bash +# Copy the public key to your VPS +sudo -u lidarr-mb-gap cat /var/lib/lidarr-mb-gap/.ssh/id_ed25519.pub | ssh user@vps "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" + +# Or manually: +# 1. Display the public key: +sudo -u lidarr-mb-gap cat /var/lib/lidarr-mb-gap/.ssh/id_ed25519.pub + +# 2. On your VPS, add it to ~/.ssh/authorized_keys (or the target user's authorized_keys) +``` + +#### Step 3: Get VPS Host Key (for known_hosts) + +```bash +# Get the VPS host key fingerprint +ssh-keyscan -t ed25519 your-vps-hostname-or-ip + +# This will output something like: +# your-vps-hostname ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... +``` + +#### Step 4: Configure in NixOS + +Add SSH configuration to your NixOS config: + +```nix +services.lidarr-mb-gap = { + enable = true; + # ... other options ... + + syncToVPS = true; + vpsHost = "user@vps-hostname"; # or "user@vps.example.com" + vpsPath = "/var/www/html"; + + # Path to SSH private key (optional, defaults to ~/.ssh/id_ed25519) + sshKeyFile = "/var/lib/lidarr-mb-gap/.ssh/id_ed25519"; + + # SSH known hosts (prevents host key verification prompts) + sshKnownHosts = { + vps = { + hostNames = [ "vps-hostname" "vps.example.com" "1.2.3.4" ]; # All possible hostnames/IPs + publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; # From ssh-keyscan output + }; + }; +}; +``` + +**Alternative: Manual Setup (if not using NixOS config)** + +If you prefer to set up SSH keys manually without NixOS configuration: + +```bash +# The service will use default SSH key location: /var/lib/lidarr-mb-gap/.ssh/id_ed25519 +# Just ensure the key exists and is properly configured +``` + +#### Step 5: Test SSH Connection + +Before enabling the service, test that SSH works: + +```bash +# Test SSH connection as the service user +sudo -u lidarr-mb-gap ssh -i /var/lib/lidarr-mb-gap/.ssh/id_ed25519 user@vps "echo 'SSH connection successful'" + +# Or if using default key location: +sudo -u lidarr-mb-gap ssh user@vps "echo 'SSH connection successful'" +``` + +If this works, rsync should work too. If you get host key verification errors, make sure you've configured `sshKnownHosts` in your NixOS config. + ### 4. Build and Switch ```bash diff --git a/nixos/lidarr-mb-gap.nix b/nixos/lidarr-mb-gap.nix index 2059d51..6afb97a 100644 --- a/nixos/lidarr-mb-gap.nix +++ b/nixos/lidarr-mb-gap.nix @@ -3,6 +3,17 @@ let reportDir = "/var/lib/lidarr-mb-gap/reports"; envFile = "/var/lib/lidarr-mb-gap/.env"; + + # Determine which package to use + lidarrMbGapPackage = if config.services.lidarr-mb-gap.package != null + then config.services.lidarr-mb-gap.package + else if config.services.lidarr-mb-gap.src != null + then import ../nix/package.nix { + inherit pkgs; + lib = pkgs.lib; + src = config.services.lidarr-mb-gap.src; + } + else throw "services.lidarr-mb-gap: Either 'package' or 'src' must be set."; in { options.services.lidarr-mb-gap = { @@ -55,11 +66,35 @@ in default = "/var/www/html"; description = "Path on VPS where reports should be synced"; }; + + sshKeyFile = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Path to SSH private key file for rsync. If null, uses default SSH key location."; + }; + + sshKnownHosts = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule { + options = { + hostNames = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "List of hostnames/IPs for this known host"; + }; + publicKey = lib.mkOption { + type = lib.types.str; + description = "SSH public key for the host"; + }; + }; + }); + default = {}; + description = "SSH known hosts configuration for the VPS (same format as programs.ssh.knownHosts)"; + }; }; config = lib.mkIf config.services.lidarr-mb-gap.enable { systemd.tmpfiles.rules = [ "d ${config.services.lidarr-mb-gap.reportDir} 0755 lidarr-mb-gap lidarr-mb-gap -" + "d /var/lib/lidarr-mb-gap/.ssh 0700 lidarr-mb-gap lidarr-mb-gap -" ]; users.users.lidarr-mb-gap = { @@ -70,6 +105,8 @@ in }; users.groups.lidarr-mb-gap = {}; + + programs.ssh.knownHosts = config.services.lidarr-mb-gap.sshKnownHosts; systemd.services.lidarr-mb-gap = { description = "Generate Lidarr MusicBrainz Gap Report"; @@ -96,7 +133,15 @@ in # Sync to VPS if enabled ${lib.optionalString (config.services.lidarr-mb-gap.syncToVPS && config.services.lidarr-mb-gap.vpsHost != null) '' + # Set up SSH options + SSH_OPTS="" + ${lib.optionalString (config.services.lidarr-mb-gap.sshKeyFile != null) '' + SSH_OPTS="-i ${config.services.lidarr-mb-gap.sshKeyFile}" + ''} + + # Use SSH options with rsync ${pkgs.rsync}/bin/rsync -avz --delete \ + -e "ssh $SSH_OPTS -o StrictHostKeyChecking=yes" \ ${config.services.lidarr-mb-gap.reportDir}/ \ ${config.services.lidarr-mb-gap.vpsHost}:${config.services.lidarr-mb-gap.vpsPath}/ ''}