Files
Lycostorrent/app/lastfm_api.py
2026-03-23 20:59:26 +01:00

212 lines
7.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import requests
import logging
import re
logger = logging.getLogger(__name__)
class LastFmAPI:
"""Classe pour interagir avec l'API Last.fm"""
def __init__(self, api_key=None):
self.api_key = api_key
self.base_url = "http://ws.audioscrobbler.com/2.0/"
self.session = requests.Session()
def search_album(self, query):
"""Recherche un album sur Last.fm"""
try:
clean_query = self._clean_music_title(query)
artist, album = self._extract_artist_album(clean_query)
logger.info(f"🎵 Recherche Last.fm: Artiste='{artist}' Album='{album}'")
if not album:
return None
params = {
'method': 'album.search',
'album': album,
'api_key': self.api_key,
'format': 'json',
'limit': 1
}
response = self.session.get(self.base_url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
results = data.get('results', {}).get('albummatches', {}).get('album', [])
if results:
album_data = results[0]
return self.get_album_info(album_data['artist'], album_data['name'])
return None
except Exception as e:
logger.error(f"Erreur recherche album Last.fm: {e}")
return None
def get_album_info(self, artist, album):
"""Récupère les infos complètes d'un album"""
try:
params = {
'method': 'album.getinfo',
'artist': artist,
'album': album,
'api_key': self.api_key,
'format': 'json'
}
response = self.session.get(self.base_url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
# Vérifier si erreur Last.fm
if 'error' in data:
logger.debug(f"Last.fm error: {data.get('message', 'Unknown error')}")
return None
album_info = data.get('album')
# Vérifier que album_info est un dict et non une string
if album_info and isinstance(album_info, dict):
return self._format_album(album_info)
return None
except Exception as e:
logger.error(f"Erreur info album: {e}")
return None
def _format_album(self, album):
"""Formate les données d'un album"""
try:
# Récupérer la plus grande image disponible
images = album.get('image', [])
cover_url = None
if isinstance(images, list):
for img in reversed(images):
if isinstance(img, dict) and img.get('size') in ['extralarge', 'large', 'medium']:
cover_url = img.get('#text')
if cover_url:
break
# Récupérer les tags
tags_data = album.get('tags', {})
tags = []
if isinstance(tags_data, dict):
tag_list = tags_data.get('tag', [])
if isinstance(tag_list, list):
tags = [tag.get('name') for tag in tag_list[:5] if isinstance(tag, dict)]
# Récupérer le wiki
wiki = album.get('wiki', {})
summary = ''
published = ''
if isinstance(wiki, dict):
summary = wiki.get('summary', '')
published = wiki.get('published', '')
return {
'artist': album.get('artist', ''),
'album': album.get('name', ''),
'cover_url': cover_url,
'summary': summary,
'published': published,
'listeners': album.get('listeners', 0),
'playcount': album.get('playcount', 0),
'tags': tags,
'url': album.get('url', ''),
'type': 'album'
}
except Exception as e:
logger.error(f"Erreur formatage album: {e}")
return None
def _clean_music_title(self, title):
"""Nettoie un titre de torrent musical"""
original = title
# Supprimer les préfixes comme [Request]
title = re.sub(r'^\s*\[.*?\]\s*', '', title)
# Remplacer les points et underscores par des espaces SAUF le tiret
title = title.replace('.', ' ').replace('_', ' ')
# Supprimer les tags de qualité
title = re.sub(r'\b(FLAC|MP3|AAC|WAV|OGG|ALAC|DSD|WEB|CD|VINYL)\b', '', title, flags=re.IGNORECASE)
title = re.sub(r'\b(320|256|192|128|24bit|16bit)\s*(kbps|khz)?\b', '', title, flags=re.IGNORECASE)
title = re.sub(r'\b(CBR|VBR|Lossless)\b', '', title, flags=re.IGNORECASE)
# Supprimer les infos de format entre parenthèses ou crochets à la fin
# mais garder le contenu principal
title = re.sub(r'\s*\([^)]*(?:FLAC|MP3|Lossless|kbps|Vinyl|CD|WEB)[^)]*\)\s*', '', title, flags=re.IGNORECASE)
title = re.sub(r'\s*\[[^\]]*(?:FLAC|MP3|Lossless|kbps|Vinyl|CD|WEB)[^\]]*\]\s*', '', title, flags=re.IGNORECASE)
# Supprimer les années entre parenthèses (2025) ou à la fin
title = re.sub(r'\s*\(\s*(19|20)\d{2}\s*\)\s*', ' ', title)
title = re.sub(r'\s+(19|20)\d{2}\s*$', '', title)
# Supprimer (EP), (Single), (Remaster), etc.
title = re.sub(r'\s*\(\s*(EP|Single|Remaster|Remastered|Deluxe|Edition|Upconvert)\s*\)\s*', '', title, flags=re.IGNORECASE)
# Supprimer Discography, Anthology, etc.
title = re.sub(r'\s*[-]\s*Discography.*$', '', title, flags=re.IGNORECASE)
title = re.sub(r'\s+Discography.*$', '', title, flags=re.IGNORECASE)
# Supprimer les groupes de release à la fin (-GROUPE)
title = re.sub(r'\s*-\s*[A-Z0-9]{2,}$', '', title)
# Supprimer les parenthèses/crochets incomplets (ex: "(20" ou "[FLA")
title = re.sub(r'\s*\([^)]*$', '', title) # Parenthèse ouvrante sans fermante
title = re.sub(r'\s*\[[^\]]*$', '', title) # Crochet ouvrant sans fermant
# Supprimer les "..." à la fin
title = re.sub(r'\s*\.{2,}\s*$', '', title)
# Nettoyer les espaces multiples
title = re.sub(r'\s+', ' ', title).strip()
# Supprimer les tirets en début ou fin
title = re.sub(r'^-+\s*|\s*-+$', '', title)
logger.debug(f"Music title cleaned: '{original[:50]}''{title}'")
return title
def _extract_artist_album(self, title):
"""Extrait l'artiste et l'album du titre"""
# Chercher le séparateur " - " (artiste - album)
if ' - ' in title:
parts = title.split(' - ', 1)
artist = parts[0].strip()
album = parts[1].strip()
# Si l'album est vide, utiliser le titre entier comme album
if not album:
return '', title.strip()
return artist, album
# Pas de séparateur trouvé - essayer de deviner
# Parfois le format est "Artiste Album" sans séparateur
return '', title.strip()
def enrich_torrent(self, torrent_title):
"""Enrichit un torrent musical avec les données Last.fm"""
try:
album_data = self.search_album(torrent_title)
if album_data:
logger.info(f"✅ Album trouvé: {album_data['artist']} - {album_data['album']}")
return album_data
else:
logger.warning(f"❌ Album non trouvé: {torrent_title[:60]}")
return None
except Exception as e:
logger.error(f"Erreur enrichissement musique: {e}")
return None