Add .gitignore to ignore logs directory
This commit is contained in:
2
.env.backup
Normal file
2
.env.backup
Normal file
@@ -0,0 +1,2 @@
|
||||
SOURCE_API_URL=https://maloja.kansaigaijin.com/apis/mlj_1/scrobbles
|
||||
SOURCE_API_KEY=MeEpNNVgLb9nB8OPbgtKjxn5jo1l8KLYTLyWvWBs48WLTij0BaYNSXtJY0UMa8
|
4
.env.template
Normal file
4
.env.template
Normal file
@@ -0,0 +1,4 @@
|
||||
# Maloja API details
|
||||
# Get these from your Maloja instance settings
|
||||
SOURCE_API_URL=
|
||||
SOURCE_API_KEY=
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
logs/
|
20
Dockerfile
Normal file
20
Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set working directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Create logs directory
|
||||
RUN mkdir -p /app/logs
|
||||
|
||||
# Copy requirements and install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy only the required script for this service
|
||||
COPY lidarr_api.py .
|
||||
|
||||
# Ensure logs directory is accessible
|
||||
VOLUME ["/app/logs"]
|
||||
|
||||
# The CMD to run the API service
|
||||
CMD ["python3", "lidarr_api.py"]
|
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
services:
|
||||
lidarr-importer-api:
|
||||
build: .
|
||||
container_name: maloja-lidarr-importer-api
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
ports:
|
||||
- "5110:5000"
|
||||
environment:
|
||||
- SOURCE_API_URL=${SOURCE_API_URL}
|
||||
- SOURCE_API_KEY=${SOURCE_API_KEY}
|
||||
command: ["python3", "lidarr_api.py"]
|
||||
restart: "unless-stopped"
|
128
lidarr_api.py
Normal file
128
lidarr_api.py
Normal file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import musicbrainzngs
|
||||
from typing import List, Dict, Optional
|
||||
from flask import Flask, jsonify
|
||||
import threading
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Logging
|
||||
# ----------------------------------------------------------------------
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/app/logs/lidarr_api.log'),
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Global state and Flask app
|
||||
# ----------------------------------------------------------------------
|
||||
app = Flask(__name__)
|
||||
artist_ids = []
|
||||
|
||||
# Configure MusicBrainzngs
|
||||
musicbrainzngs.set_useragent(
|
||||
"Maloja-Lidarr-Sync-API",
|
||||
"1.0",
|
||||
"your-email@example.com"
|
||||
)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# MusicBrainz API helpers
|
||||
# ----------------------------------------------------------------------
|
||||
def get_artist_id_from_recording(recording_mbid: str) -> Optional[str]:
|
||||
"""Finds the artist MBID for a given recording MBID."""
|
||||
try:
|
||||
result = musicbrainzngs.get_recording_by_id(recording_mbid, includes=["artist-credits"])
|
||||
if result.get('recording', {}).get('artist-credit', []):
|
||||
artist = result['recording']['artist-credit'][0]
|
||||
if artist.get('artist', {}).get('id'):
|
||||
return artist['artist']['id']
|
||||
except musicbrainzngs.MusicBrainzError as e:
|
||||
logger.error(f"MusicBrainz API error fetching recording {recording_mbid}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"An unexpected error occurred: {e}")
|
||||
return None
|
||||
|
||||
def get_artist_id_by_name(artist_name: str) -> Optional[str]:
|
||||
"""Finds the artist MBID by searching for their name."""
|
||||
try:
|
||||
result = musicbrainzngs.search_artists(artist=artist_name, limit=1)
|
||||
if result.get('artist-list', []):
|
||||
return result['artist-list'][0].get('id')
|
||||
except musicbrainzngs.MusicBrainzError as e:
|
||||
logger.error(f"MusicBrainz API error searching for artist '{artist_name}': {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"An unexpected error occurred: {e}")
|
||||
return None
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Main logic to fetch and process artists
|
||||
# ----------------------------------------------------------------------
|
||||
def fetch_and_process_artists():
|
||||
"""Fetches tracks from Maloja and populates the global artist_ids list."""
|
||||
global artist_ids
|
||||
while True:
|
||||
try:
|
||||
api_url = os.getenv("SOURCE_API_URL")
|
||||
api_key = os.getenv("SOURCE_API_KEY")
|
||||
|
||||
if not api_url or not api_key:
|
||||
logger.error("API URL or key not set. Exiting thread.")
|
||||
break
|
||||
|
||||
resp = requests.get(api_url, headers={'x-api-key': api_key}, params={'key': api_key}, timeout=15)
|
||||
resp.raise_for_status()
|
||||
tracks_data = resp.json().get('list', [])
|
||||
|
||||
unique_artist_ids = set()
|
||||
for track in tracks_data:
|
||||
if not isinstance(track, dict):
|
||||
continue
|
||||
|
||||
recording_mbid = track.get("track", {}).get("recording_mbid")
|
||||
artist_name = track.get("track", {}).get("artists", [""])[0]
|
||||
|
||||
if recording_mbid:
|
||||
artist_id = get_artist_id_from_recording(recording_mbid)
|
||||
if artist_id:
|
||||
unique_artist_ids.add(artist_id)
|
||||
elif artist_name:
|
||||
artist_id = get_artist_id_by_name(artist_name)
|
||||
if artist_id:
|
||||
unique_artist_ids.add(artist_id)
|
||||
|
||||
artist_ids = sorted(list(unique_artist_ids))
|
||||
logger.info(f"Artist list updated. Found {len(artist_ids)} unique artist IDs.")
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in the fetch thread: {e}")
|
||||
|
||||
time.sleep(3600)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Flask routes
|
||||
# ----------------------------------------------------------------------
|
||||
@app.route('/artists')
|
||||
def get_artists():
|
||||
if not artist_ids:
|
||||
return jsonify([])
|
||||
|
||||
lidarr_list = [{"foreignId": artist_id} for artist_id in artist_ids]
|
||||
return jsonify(lidarr_list)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Main execution
|
||||
# ----------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
thread = threading.Thread(target=fetch_and_process_artists, daemon=True)
|
||||
thread.start()
|
||||
app.run(host='0.0.0.0', port=5000)
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
requests==2.31.0
|
||||
musicbrainzngs==0.7.1
|
||||
Flask==2.3.3
|
Reference in New Issue
Block a user