added external service for meta retrieve

This commit is contained in:
Alexander Karpov 2024-12-02 03:04:18 +03:00
parent f320fa2d62
commit 1b9b1d2bb5
3 changed files with 115 additions and 17 deletions
akarpov/music/services
config/settings

View File

@ -0,0 +1,76 @@
import requests
from typing import Optional, Dict, Any
import structlog
from django.conf import settings
from functools import wraps
logger = structlog.get_logger(__name__)
class ExternalServiceClient:
def __init__(self):
self.base_url = getattr(settings, 'MUSIC_EXTERNAL_SERVICE_URL', None)
def _make_request(self, endpoint: str, params: Dict = None, **kwargs) -> Optional[Dict]:
if not self.base_url:
return None
url = f"{self.base_url.rstrip('/')}/{endpoint.lstrip('/')}"
try:
response = requests.post(url, params=params, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(
"External service request failed",
error=str(e),
endpoint=endpoint,
params=params
)
return None
def get_spotify_info(self, track_name: str) -> Optional[Dict[str, Any]]:
return self._make_request(
"/spotify/search",
params={"query": track_name}
)
def translate_text(self, text: str, source_lang: str = "auto", target_lang: str = "en") -> Optional[str]:
response = self._make_request(
"/translation/translate",
json={
"text": text,
"source_lang": source_lang,
"target_lang": target_lang
}
)
return response.get("translated_text") if response else None
def external_service_fallback(fallback_func):
"""Decorator to try external service first, then fall back to local implementation"""
@wraps(fallback_func)
def wrapper(*args, **kwargs):
if not hasattr(settings, 'MUSIC_EXTERNAL_SERVICE_URL') or not settings.MUSIC_EXTERNAL_SERVICE_URL:
return fallback_func(*args, **kwargs)
client = ExternalServiceClient()
try:
if fallback_func.__name__ == "get_spotify_info":
result = client.get_spotify_info(args[1]) # args[1] is track_name
elif fallback_func.__name__ == "safe_translate":
result = client.translate_text(args[0]) # args[0] is text
if result:
return result
except Exception as e:
logger.error(
"External service failed, falling back to local implementation",
error=str(e),
function=fallback_func.__name__
)
return fallback_func(*args, **kwargs)
return wrapper

View File

@ -4,6 +4,11 @@
import requests
import spotipy
from akarpov.music.services.external import (
ExternalServiceClient,
external_service_fallback,
)
try:
from deep_translator import GoogleTranslator
except requests.exceptions.JSONDecodeError:
@ -76,6 +81,7 @@ def spotify_search(name: str, session: spotipy.Spotify, search_type="track"):
return res
@external_service_fallback
def get_spotify_info(name: str, session: spotipy.Spotify) -> dict:
info = {
"album_name": "",
@ -379,29 +385,43 @@ def save_author_image(author, image_path):
print(f"Error saving author image: {str(e)}")
def safe_translate(text):
@external_service_fallback
def safe_translate(text: str) -> str:
try:
translated = GoogleTranslator(source="auto", target="en").translate(text)
return slugify(translated)
except Exception as e:
print(f"Error translating text: {str(e)}")
print(f"Translation failed: {str(e)}")
return slugify(text)
def search_all_platforms(track_name: str) -> dict:
print(track_name)
# session = spotipy.Spotify(
# auth_manager=spotipy.SpotifyClientCredentials(
# client_id=settings.MUSIC_SPOTIFY_ID,
# client_secret=settings.MUSIC_SPOTIFY_SECRET,
# )
# )
# spotify_info = get_spotify_info(track_name, session)
spotify_info = {} # TODO: add proxy for info retrieve
if settings.MUSIC_EXTERNAL_SERVICE_URL:
# Use external service if configured
client = ExternalServiceClient()
spotify_info = client.get_spotify_info(track_name) or {}
else:
# Local implementation fallback
try:
session = spotipy.Spotify(
auth_manager=SpotifyClientCredentials(
client_id=settings.MUSIC_SPOTIFY_ID,
client_secret=settings.MUSIC_SPOTIFY_SECRET,
)
)
spotify_info = get_spotify_info(track_name, session)
except Exception as e:
print("Local Spotify implementation failed", error=str(e))
spotify_info = {}
yandex_info = search_yandex(track_name)
if "album_image_path" in spotify_info and "album_image_path" in yandex_info:
os.remove(yandex_info["album_image_path"])
# Combine artist information
combined_artists = set()
for artist in spotify_info.get("artists", []) + yandex_info.get("artists", []):
normalized_artist = normalize_text(artist)
@ -410,12 +430,13 @@ def search_all_platforms(track_name: str) -> dict:
for existing_artist in combined_artists
):
combined_artists.add(normalized_artist)
genre = spotify_info.get("genre") or yandex_info.get("genre")
if type(genre) is list:
genre = sorted(genre, key=lambda x: len(x))
genre = genre[0]
track_info = {
# Process genre information
genre = spotify_info.get("genre") or yandex_info.get("genre")
if isinstance(genre, list) and genre:
genre = sorted(genre, key=len)[0]
return {
"album_name": spotify_info.get("album_name")
or yandex_info.get("album_name", ""),
"release": spotify_info.get("release") or yandex_info.get("release", ""),
@ -425,5 +446,3 @@ def search_all_platforms(track_name: str) -> dict:
"album_image": spotify_info.get("album_image_path")
or yandex_info.get("album_image_path", None),
}
return track_info

View File

@ -715,6 +715,9 @@
LAST_FM_API_KEY = env("LAST_FM_API_KET", default="")
LAST_FM_SECRET = env("LAST_FM_SECRET", default="")
# EXTERNAL
MUSIC_EXTERNAL_SERVICE_URL = env("MUSIC_EXTERNAL_SERVICE_URL", default="")
# ROBOTS
# ------------------------------------------------------------------------------
ROBOTS_USE_SITEMAP = True