Add NixOS module and deployment guide for lidarr-mb-gap
- Introduced a new NixOS module for the lidarr-mb-gap service, allowing users to configure and manage the report generation process through NixOS. - Added a comprehensive deployment guide in `nixos/DEPLOYMENT.md`, detailing setup instructions, configuration options, and troubleshooting tips for deploying the service on NixOS and serving reports via Caddy. - Updated `flake.nix` to export the new NixOS module. - Enhanced the report generation scripts to support customizable output paths for generated reports.
This commit is contained in:
12
flake.nix
12
flake.nix
@@ -7,7 +7,17 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
let
|
||||||
|
# NixOS module output (not system-specific)
|
||||||
|
# The module accepts a package option, which can be set from the flake's packages
|
||||||
|
nixosModules = {
|
||||||
|
lidarr-mb-gap = import ./nixos/lidarr-mb-gap.nix;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Export NixOS modules
|
||||||
|
nixosModules = nixosModules;
|
||||||
|
} // flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
lib = pkgs.lib;
|
lib = pkgs.lib;
|
||||||
|
|||||||
292
nixos/DEPLOYMENT.md
Normal file
292
nixos/DEPLOYMENT.md
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
# Deployment Guide
|
||||||
|
|
||||||
|
This guide explains how to deploy the Lidarr MusicBrainz gap reporter using NixOS on your server and serve it with Caddy on your VPS.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Server (NixOS)**: Runs the report generation script periodically via systemd timer
|
||||||
|
- **VPS**: Serves the generated HTML report via Caddy
|
||||||
|
|
||||||
|
## Setup on NixOS Server
|
||||||
|
|
||||||
|
### 1. Add the NixOS Module
|
||||||
|
|
||||||
|
Add the module to your NixOS configuration. You have two options:
|
||||||
|
|
||||||
|
#### Option A: Import directly with source path
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In your configuration.nix or flake.nix
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./path/to/lidarr-musicbrainz/nixos/lidarr-mb-gap.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.lidarr-mb-gap = {
|
||||||
|
enable = true;
|
||||||
|
src = /path/to/lidarr-musicbrainz/src; # Source path - package will be built from this
|
||||||
|
reportDir = "/var/lib/lidarr-mb-gap/reports";
|
||||||
|
envFile = "/var/lib/lidarr-mb-gap/.env";
|
||||||
|
runInterval = "daily"; # Or "hourly", or "*-*-* 02:00:00" for specific time
|
||||||
|
|
||||||
|
# Optional: Auto-sync to VPS
|
||||||
|
syncToVPS = true;
|
||||||
|
vpsHost = "user@vps"; # Your SSH host alias
|
||||||
|
vpsPath = "/var/www/html";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option B: Use as a flake input (Recommended)
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In your flake.nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
lidarr-mb-gap.url = "path:/path/to/lidarr-musicbrainz";
|
||||||
|
# or
|
||||||
|
# lidarr-mb-gap.url = "github:yourusername/lidarr-musicbrainz";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, lidarr-mb-gap, ... }: {
|
||||||
|
nixosConfigurations.your-server = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux"; # or your system
|
||||||
|
modules = [
|
||||||
|
lidarr-mb-gap.nixosModules.lidarr-mb-gap
|
||||||
|
{
|
||||||
|
# Reference the package from the flake
|
||||||
|
services.lidarr-mb-gap.package = lidarr-mb-gap.packages.${system}.lidarr-mb-gap;
|
||||||
|
services.lidarr-mb-gap.enable = true;
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: When using the flake module, you can reference the package directly from `lidarr-mb-gap.packages.${system}.lidarr-mb-gap`, which is more efficient than building from source.
|
||||||
|
|
||||||
|
### 2. Create Environment File
|
||||||
|
|
||||||
|
Create `/var/lib/lidarr-mb-gap/.env` with your Lidarr credentials:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /var/lib/lidarr-mb-gap
|
||||||
|
sudo nano /var/lib/lidarr-mb-gap/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Add:
|
||||||
|
```env
|
||||||
|
LIDARR_URL=http://your-lidarr-instance:8686
|
||||||
|
LIDARR_API_KEY=your-api-key-here
|
||||||
|
SAMBL_URL=https://sambl.lioncat6.com
|
||||||
|
MAX_ARTISTS=0 # 0 = no limit
|
||||||
|
OUTPUT_DIR=/var/lib/lidarr-mb-gap/reports # Optional: defaults to current directory
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: The `OUTPUT_DIR` is automatically set by the systemd service, but you can override it in the `.env` file if needed.
|
||||||
|
|
||||||
|
Set proper permissions:
|
||||||
|
```bash
|
||||||
|
sudo chown -R lidarr-mb-gap:lidarr-mb-gap /var/lib/lidarr-mb-gap
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On the server
|
||||||
|
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_lidarr
|
||||||
|
ssh-copy-id -i ~/.ssh/id_ed25519_lidarr.pub user@vps
|
||||||
|
|
||||||
|
# Add to ~/.ssh/config
|
||||||
|
Host vps
|
||||||
|
HostName your-vps-ip-or-domain
|
||||||
|
User your-username
|
||||||
|
IdentityFile ~/.ssh/id_ed25519_lidarr
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Build and Switch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nixos-rebuild switch
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Test the Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run manually once
|
||||||
|
sudo systemctl start lidarr-mb-gap
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
sudo systemctl status lidarr-mb-gap
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
sudo journalctl -u lidarr-mb-gap -f
|
||||||
|
|
||||||
|
# Check timer
|
||||||
|
sudo systemctl status lidarr-mb-gap.timer
|
||||||
|
sudo systemctl list-timers lidarr-mb-gap
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup on VPS (Caddy)
|
||||||
|
|
||||||
|
### Option 1: Manual Sync (Recommended for initial setup)
|
||||||
|
|
||||||
|
If you didn't enable auto-sync, manually copy files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On server
|
||||||
|
scp /var/lib/lidarr-mb-gap/reports/missing_albums.html user@vps:/var/www/html/
|
||||||
|
|
||||||
|
# Or use rsync
|
||||||
|
rsync -avz /var/lib/lidarr-mb-gap/reports/ user@vps:/var/www/html/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Auto-sync via rsync
|
||||||
|
|
||||||
|
If you enabled `syncToVPS` in the NixOS config, files will sync automatically after each report generation.
|
||||||
|
|
||||||
|
### Configure Caddy
|
||||||
|
|
||||||
|
1. **Install Caddy** (if not already installed):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On Debian/Ubuntu
|
||||||
|
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install caddy
|
||||||
|
|
||||||
|
# Or using Nix
|
||||||
|
nix-env -iA nixpkgs.caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create web directory**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /var/www/html
|
||||||
|
sudo chown -R www-data:www-data /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure Caddy**:
|
||||||
|
|
||||||
|
Copy the `Caddyfile` to your Caddy config location:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For systemd service
|
||||||
|
sudo cp Caddyfile /etc/caddy/Caddyfile
|
||||||
|
|
||||||
|
# Or for user service
|
||||||
|
mkdir -p ~/.config/caddy
|
||||||
|
cp Caddyfile ~/.config/caddy/
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Start Caddy**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Systemd service
|
||||||
|
sudo systemctl enable caddy
|
||||||
|
sudo systemctl start caddy
|
||||||
|
sudo systemctl status caddy
|
||||||
|
|
||||||
|
# Or run directly
|
||||||
|
caddy file-server --listen :80 --root /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Access the report**:
|
||||||
|
|
||||||
|
- If using a domain: `https://your-domain.com/missing_albums.html`
|
||||||
|
- If using IP: `http://your-vps-ip/missing_albums.html`
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Change Report Generation Frequency
|
||||||
|
|
||||||
|
Edit the `runInterval` option:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.lidarr-mb-gap.runInterval = "hourly"; # Every hour
|
||||||
|
services.lidarr-mb-gap.runInterval = "*-*-* 02:00:00"; # Daily at 2 AM
|
||||||
|
services.lidarr-mb-gap.runInterval = "Mon *-*-* 00:00:00"; # Weekly on Monday
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Authentication to Caddy
|
||||||
|
|
||||||
|
Uncomment the `basicauth` section in `Caddyfile` and generate a password:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
caddy hash-password
|
||||||
|
# Enter your password, copy the hash
|
||||||
|
```
|
||||||
|
|
||||||
|
Then update the Caddyfile with your username and hash.
|
||||||
|
|
||||||
|
### Use a Custom Domain with HTTPS
|
||||||
|
|
||||||
|
Update the Caddyfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
your-domain.com {
|
||||||
|
root * /var/www/html
|
||||||
|
file_server
|
||||||
|
encode gzip
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Caddy will automatically obtain and renew SSL certificates via Let's Encrypt.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Report not generating
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check service logs
|
||||||
|
sudo journalctl -u lidarr-mb-gap -n 50
|
||||||
|
|
||||||
|
# Check if .env file exists and has correct permissions
|
||||||
|
sudo ls -la /var/lib/lidarr-mb-gap/.env
|
||||||
|
|
||||||
|
# Test manually
|
||||||
|
sudo -u lidarr-mb-gap nix run /path/to/flake#lidarr-mb-gap -- --output-dir /var/lib/lidarr-mb-gap/reports
|
||||||
|
```
|
||||||
|
|
||||||
|
### Files not syncing to VPS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test SSH connection
|
||||||
|
ssh user@vps
|
||||||
|
|
||||||
|
# Test rsync manually
|
||||||
|
rsync -avz /var/lib/lidarr-mb-gap/reports/ user@vps:/var/www/html/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caddy not serving files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Caddy logs
|
||||||
|
sudo journalctl -u caddy -f
|
||||||
|
|
||||||
|
# Verify file permissions
|
||||||
|
ls -la /var/www/html/
|
||||||
|
|
||||||
|
# Test Caddy config
|
||||||
|
sudo caddy validate --config /etc/caddy/Caddyfile
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
- Reports are generated in `/var/lib/lidarr-mb-gap/reports/`
|
||||||
|
- Old reports are overwritten on each run
|
||||||
|
- To keep history, modify the service to add timestamps to filenames
|
||||||
|
- Monitor disk space if keeping history
|
||||||
|
|
||||||
119
nixos/lidarr-mb-gap.nix
Normal file
119
nixos/lidarr-mb-gap.nix
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
reportDir = "/var/lib/lidarr-mb-gap/reports";
|
||||||
|
envFile = "/var/lib/lidarr-mb-gap/.env";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.lidarr-mb-gap = {
|
||||||
|
enable = lib.mkEnableOption "Lidarr MusicBrainz Gap Reporter";
|
||||||
|
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.package;
|
||||||
|
default = null;
|
||||||
|
description = "The lidarr-mb-gap package to use. If null, will be built from src.";
|
||||||
|
};
|
||||||
|
|
||||||
|
src = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.path;
|
||||||
|
default = null;
|
||||||
|
description = "Source path for building the package. Required if package is not explicitly set.";
|
||||||
|
};
|
||||||
|
|
||||||
|
reportDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = reportDir;
|
||||||
|
description = "Directory where reports will be generated";
|
||||||
|
};
|
||||||
|
|
||||||
|
envFile = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = envFile;
|
||||||
|
description = "Path to .env file with LIDARR_URL and LIDARR_API_KEY";
|
||||||
|
};
|
||||||
|
|
||||||
|
runInterval = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "daily";
|
||||||
|
description = "systemd timer interval (e.g., 'daily', 'hourly', '*-*-* 02:00:00')";
|
||||||
|
};
|
||||||
|
|
||||||
|
syncToVPS = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Whether to sync reports to VPS via rsync";
|
||||||
|
};
|
||||||
|
|
||||||
|
vpsHost = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = "VPS hostname or IP for rsync (e.g., 'vps' or 'user@vps.example.com')";
|
||||||
|
};
|
||||||
|
|
||||||
|
vpsPath = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/var/www/html";
|
||||||
|
description = "Path on VPS where reports should be synced";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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 -"
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.lidarr-mb-gap = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "lidarr-mb-gap";
|
||||||
|
home = "/var/lib/lidarr-mb-gap";
|
||||||
|
createHome = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.lidarr-mb-gap = {};
|
||||||
|
|
||||||
|
systemd.services.lidarr-mb-gap = {
|
||||||
|
description = "Generate Lidarr MusicBrainz Gap Report";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "lidarr-mb-gap";
|
||||||
|
Group = "lidarr-mb-gap";
|
||||||
|
WorkingDirectory = config.services.lidarr-mb-gap.reportDir;
|
||||||
|
EnvironmentFile = config.services.lidarr-mb-gap.envFile;
|
||||||
|
ExecStart = pkgs.writeShellScript "lidarr-mb-gap-run" ''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Export OUTPUT_DIR to environment
|
||||||
|
export OUTPUT_DIR=${config.services.lidarr-mb-gap.reportDir}
|
||||||
|
|
||||||
|
# Run the binary
|
||||||
|
${lib.getExe lidarrMbGapPackage} || {
|
||||||
|
echo "Failed to generate report"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sync to VPS if enabled
|
||||||
|
${lib.optionalString (config.services.lidarr-mb-gap.syncToVPS && config.services.lidarr-mb-gap.vpsHost != null) ''
|
||||||
|
${pkgs.rsync}/bin/rsync -avz --delete \
|
||||||
|
${config.services.lidarr-mb-gap.reportDir}/ \
|
||||||
|
${config.services.lidarr-mb-gap.vpsHost}:${config.services.lidarr-mb-gap.vpsPath}/
|
||||||
|
''}
|
||||||
|
'';
|
||||||
|
StandardOutput = "journal";
|
||||||
|
StandardError = "journal";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.lidarr-mb-gap = {
|
||||||
|
description = "Timer for Lidarr MusicBrainz Gap Report";
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = config.services.lidarr-mb-gap.runInterval;
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from html import escape
|
from html import escape
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def generate_html_report(albums_to_add: List[Dict], albums_to_update: List[Dict]):
|
def generate_html_report(
|
||||||
|
albums_to_add: List[Dict], albums_to_update: List[Dict], output_path: Path = Path("missing_albums.html")
|
||||||
|
):
|
||||||
"""Generate an HTML report with clickable submission links"""
|
"""Generate an HTML report with clickable submission links"""
|
||||||
html_content = """<!DOCTYPE html>
|
html_content = """<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -355,6 +358,6 @@ def generate_html_report(albums_to_add: List[Dict], albums_to_update: List[Dict]
|
|||||||
html_footer = "\n</body>\n</html>\n"
|
html_footer = "\n</body>\n</html>\n"
|
||||||
html_content = html_header + albums_html + html_footer
|
html_content = html_header + albums_html + html_footer
|
||||||
|
|
||||||
with open("missing_albums.html", "w", encoding="utf-8") as f:
|
with open(output_path, "w", encoding="utf-8") as f:
|
||||||
f.write(html_content)
|
f.write(html_content)
|
||||||
logger.info("HTML report saved to missing_albums.html")
|
logger.info(f"HTML report saved to {output_path}")
|
||||||
|
|||||||
13
src/main.py
13
src/main.py
@@ -8,6 +8,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
@@ -260,6 +261,10 @@ def main():
|
|||||||
LIDARR_API_KEY = os.getenv("LIDARR_API_KEY")
|
LIDARR_API_KEY = os.getenv("LIDARR_API_KEY")
|
||||||
SAMBL_URL = os.getenv("SAMBL_URL") or None
|
SAMBL_URL = os.getenv("SAMBL_URL") or None
|
||||||
MAX_ARTISTS = int(os.getenv("MAX_ARTISTS", "5"))
|
MAX_ARTISTS = int(os.getenv("MAX_ARTISTS", "5"))
|
||||||
|
OUTPUT_DIR = os.getenv("OUTPUT_DIR", ".")
|
||||||
|
|
||||||
|
output_dir = Path(OUTPUT_DIR)
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if not LIDARR_URL:
|
if not LIDARR_URL:
|
||||||
logger.error("LIDARR_URL not set")
|
logger.error("LIDARR_URL not set")
|
||||||
@@ -347,11 +352,13 @@ def main():
|
|||||||
"total": len(all_albums),
|
"total": len(all_albums),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with open("missing_albums.json", "w", encoding="utf-8") as f:
|
json_path = output_dir / "missing_albums.json"
|
||||||
|
with open(json_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(output_data, f, indent=2, ensure_ascii=False)
|
json.dump(output_data, f, indent=2, ensure_ascii=False)
|
||||||
logger.info("Results saved to missing_albums.json")
|
logger.info(f"Results saved to {json_path}")
|
||||||
|
|
||||||
generate_html_report(all_albums_to_add, all_albums_to_update)
|
html_path = output_dir / "missing_albums.html"
|
||||||
|
generate_html_report(all_albums_to_add, all_albums_to_update, html_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user