Refactor flake.nix and enhance project structure
- Updated `flake.nix` to define a new Python application `lidarr-mb-gap` for identifying missing albums on MusicBrainz. - Improved development shell environment by including a Python environment with necessary packages. - Added new source files: `__init__.py`, `html_report.py`, and `main.py` to implement core functionality and HTML report generation. - Introduced `pyproject.toml` for better package management and project metadata. - Enhanced user instructions in the shell hook for running the application.
This commit is contained in:
38
flake.nix
38
flake.nix
@@ -10,30 +10,46 @@
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
|
||||
lib = pkgs.lib;
|
||||
lidarr-mb-gap = pkgs.python3Packages.buildPythonApplication {
|
||||
pname = "lidarr-mb-gap";
|
||||
version = "1.0.0";
|
||||
src = lib.cleanSource ./src;
|
||||
format = "pyproject";
|
||||
nativeBuildInputs = with pkgs.python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
propagatedBuildInputs = with pkgs.python3Packages; [
|
||||
requests
|
||||
python-dotenv
|
||||
]);
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pythonEnv
|
||||
(pkgs.python3.withPackages (ps: with ps; [
|
||||
requests
|
||||
python-dotenv
|
||||
]))
|
||||
pkgs.black
|
||||
];
|
||||
shellHook = ''
|
||||
echo "Python environment ready!"
|
||||
echo "Run: python main.py"
|
||||
echo "Format code with: black main.py"
|
||||
echo "Run: python src/main.py"
|
||||
echo "Format code with: black src/"
|
||||
'';
|
||||
};
|
||||
|
||||
packages.default = pkgs.writeShellApplication {
|
||||
name = "lidarr-musicbrainz";
|
||||
runtimeInputs = [ pythonEnv ];
|
||||
text = ''
|
||||
python ${./main.py} "$@"
|
||||
'';
|
||||
packages.default = lidarr-mb-gap;
|
||||
packages.lidarr-mb-gap = lidarr-mb-gap;
|
||||
apps.default = {
|
||||
type = "app";
|
||||
program = "${lidarr-mb-gap}/bin/lidarr-mb-gap";
|
||||
};
|
||||
apps.lidarr-mb-gap = {
|
||||
type = "app";
|
||||
program = "${lidarr-mb-gap}/bin/lidarr-mb-gap";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
2
src/__init__.py
Normal file
2
src/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Lidarr to MusicBrainz Missing Albums Finder"""
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""HTML report generation for missing albums"""
|
||||
|
||||
import logging
|
||||
from html import escape
|
||||
from typing import Dict, List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate_html_report(albums_to_add: List[Dict], albums_to_update: List[Dict]):
|
||||
"""Generate an HTML report with clickable submission links"""
|
||||
@@ -354,4 +357,4 @@ def generate_html_report(albums_to_add: List[Dict], albums_to_update: List[Dict]
|
||||
|
||||
with open("missing_albums.html", "w", encoding="utf-8") as f:
|
||||
f.write(html_content)
|
||||
print(f"📄 HTML report saved to missing_albums.html")
|
||||
logger.info("HTML report saved to missing_albums.html")
|
||||
@@ -5,6 +5,7 @@ for artists monitored in Lidarr, and generate submission links.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
@@ -17,6 +18,13 @@ from html_report import generate_html_report
|
||||
|
||||
load_dotenv()
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="[%(levelname)s] %(message)s",
|
||||
handlers=[logging.StreamHandler(sys.stdout)],
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LidarrClient:
|
||||
"""Client for interacting with Lidarr API"""
|
||||
@@ -33,7 +41,7 @@ class LidarrClient:
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error fetching artists from Lidarr: {e}", file=sys.stderr)
|
||||
logger.error(f"Error fetching artists from Lidarr: {e}")
|
||||
return []
|
||||
|
||||
def get_monitored_artists(
|
||||
@@ -254,32 +262,32 @@ def main():
|
||||
MAX_ARTISTS = int(os.getenv("MAX_ARTISTS", "5"))
|
||||
|
||||
if not LIDARR_URL:
|
||||
print("Error: LIDARR_URL not set.", file=sys.stderr)
|
||||
logger.error("LIDARR_URL not set")
|
||||
sys.exit(1)
|
||||
|
||||
if not LIDARR_API_KEY:
|
||||
print("Error: LIDARR_API_KEY not set.", file=sys.stderr)
|
||||
logger.error("LIDARR_API_KEY not set")
|
||||
sys.exit(1)
|
||||
|
||||
lidarr = LidarrClient(LIDARR_URL, LIDARR_API_KEY)
|
||||
sambl = SamblClient(SAMBL_URL)
|
||||
|
||||
print("Fetching monitored artists from Lidarr...")
|
||||
logger.info("Fetching monitored artists from Lidarr...")
|
||||
artists = lidarr.get_monitored_artists(["new", "all"])
|
||||
|
||||
if not artists:
|
||||
print("No artists found with monitorNewItems set to 'new' or 'all'")
|
||||
logger.warning("No artists found with monitorNewItems set to 'new' or 'all'")
|
||||
return
|
||||
|
||||
total_artists = len(artists)
|
||||
if MAX_ARTISTS > 0 and total_artists > MAX_ARTISTS:
|
||||
print(
|
||||
logger.info(
|
||||
f"Found {total_artists} monitored artists (limiting to {MAX_ARTISTS} for testing)"
|
||||
)
|
||||
artists = artists[:MAX_ARTISTS]
|
||||
else:
|
||||
print(f"Found {total_artists} monitored artists")
|
||||
print("\n" + "=" * 80)
|
||||
logger.info(f"Found {total_artists} monitored artists")
|
||||
logger.info("=" * 80)
|
||||
|
||||
all_albums_to_add = []
|
||||
all_albums_to_update = []
|
||||
@@ -289,43 +297,45 @@ def main():
|
||||
artist_mbid = artist.get("foreignArtistId") or artist.get("mbid")
|
||||
|
||||
if not artist_mbid:
|
||||
print(f"\n⚠️ Skipping {artist_name} - no MusicBrainz ID found")
|
||||
logger.warning(f"Skipping {artist_name} - no MusicBrainz ID found")
|
||||
continue
|
||||
|
||||
print(f"\n🎵 Artist: {artist_name}")
|
||||
print(f" MusicBrainz ID: {artist_mbid}")
|
||||
logger.info(f"Artist: {artist_name}")
|
||||
logger.info(f"MusicBrainz ID: {artist_mbid}")
|
||||
|
||||
albums_to_add, albums_to_update = sambl.find_missing_albums(
|
||||
artist_mbid, artist_name
|
||||
)
|
||||
|
||||
if albums_to_add:
|
||||
print(f"\n 📥 Albums to ADD ({len(albums_to_add)}):")
|
||||
logger.info(f"Albums to ADD ({len(albums_to_add)}):")
|
||||
processed = _process_albums(albums_to_add, "add")
|
||||
all_albums_to_add.extend(processed)
|
||||
print("\n".join(map(_format_album_output, processed)))
|
||||
for album_output in map(_format_album_output, processed):
|
||||
logger.info(album_output)
|
||||
|
||||
if albums_to_update:
|
||||
print(f"\n 🔄 Albums to UPDATE ({len(albums_to_update)}):")
|
||||
logger.info(f"Albums to UPDATE ({len(albums_to_update)}):")
|
||||
processed = _process_albums(albums_to_update, "update")
|
||||
all_albums_to_update.extend(processed)
|
||||
print("\n".join(map(_format_album_output, processed)))
|
||||
for album_output in map(_format_album_output, processed):
|
||||
logger.info(album_output)
|
||||
|
||||
if not albums_to_add and not albums_to_update:
|
||||
print(f" ✓ All albums are properly linked!")
|
||||
logger.info("All albums are properly linked!")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print(f"\n📊 Summary:")
|
||||
logger.info("=" * 80)
|
||||
logger.info("Summary:")
|
||||
artists_info = f"Artists processed: {len(artists)}"
|
||||
if MAX_ARTISTS > 0 and total_artists > MAX_ARTISTS:
|
||||
artists_info += f" (of {total_artists} total)"
|
||||
print(artists_info)
|
||||
print(f" Albums to ADD: {len(all_albums_to_add)}")
|
||||
print(f" Albums to UPDATE: {len(all_albums_to_update)}")
|
||||
logger.info(artists_info)
|
||||
logger.info(f"Albums to ADD: {len(all_albums_to_add)}")
|
||||
logger.info(f"Albums to UPDATE: {len(all_albums_to_update)}")
|
||||
|
||||
all_albums = all_albums_to_add + all_albums_to_update
|
||||
if not all_albums:
|
||||
print("\n✨ All albums are already on MusicBrainz!")
|
||||
logger.info("All albums are already on MusicBrainz!")
|
||||
return
|
||||
|
||||
output_data = {
|
||||
@@ -339,7 +349,7 @@ def main():
|
||||
}
|
||||
with open("missing_albums.json", "w", encoding="utf-8") as f:
|
||||
json.dump(output_data, f, indent=2, ensure_ascii=False)
|
||||
print(f"\n💾 Results saved to missing_albums.json")
|
||||
logger.info("Results saved to missing_albums.json")
|
||||
|
||||
generate_html_report(all_albums_to_add, all_albums_to_update)
|
||||
|
||||
20
src/pyproject.toml
Normal file
20
src/pyproject.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
py-modules = ["main", "html_report"]
|
||||
|
||||
[project]
|
||||
name = "lidarr-mb-gap"
|
||||
version = "1.0.0"
|
||||
description = "Lidarr to MusicBrainz Missing Albums Finder"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = [
|
||||
"requests",
|
||||
"python-dotenv",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
lidarr-mb-gap = "main:main"
|
||||
|
||||
Reference in New Issue
Block a user