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