mirror of
https://github.com/Dvorinka/SpotifyRecAlg.git
synced 2026-06-03 20:13:03 +00:00
175 lines
5.9 KiB
Python
175 lines
5.9 KiB
Python
import json
|
|
import logging
|
|
|
|
from flask_openapi3 import APIBlueprint, Tag
|
|
from pydantic import Field
|
|
|
|
from swingmusic.api.apischemas import TrackHashSchema
|
|
|
|
# DragonflyDB integration for lyrics caching
|
|
from swingmusic.db.dragonfly_client import get_dragonfly_client
|
|
from swingmusic.lib.lyrics import (
|
|
Lyrics as Lyrics_class,
|
|
)
|
|
from swingmusic.lib.lyrics import (
|
|
get_lyrics_file,
|
|
get_lyrics_from_duplicates,
|
|
get_lyrics_from_tags,
|
|
)
|
|
from swingmusic.plugins.lyrics import Lyrics
|
|
from swingmusic.store.tracks import TrackStore
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
bp_tag = Tag(name="Lyrics", description="Get lyrics")
|
|
api = APIBlueprint("lyrics", __name__, url_prefix="/lyrics", abp_tags=[bp_tag])
|
|
|
|
|
|
class SendLyricsBody(TrackHashSchema):
|
|
filepath: str = Field(description="The path to the file")
|
|
|
|
|
|
@api.post("")
|
|
def send_lyrics(body: SendLyricsBody):
|
|
"""
|
|
Returns the lyrics for a track
|
|
"""
|
|
# 1. try to get lyrics by .lrc / .elrc file
|
|
# 2. try to get lyrics by extra key
|
|
# 3. try to get by duplicates
|
|
# 4. iter plugins
|
|
|
|
filepath = body.filepath
|
|
trackhash = body.trackhash
|
|
|
|
# Try DragonflyDB cache first
|
|
cache = get_dragonfly_client()
|
|
cache_key = f"lyrics:{trackhash}"
|
|
|
|
if cache.is_available():
|
|
try:
|
|
cached = cache.get(cache_key)
|
|
if cached:
|
|
logger.debug(f"Cache hit for lyrics {trackhash}")
|
|
return json.loads(cached)
|
|
except Exception:
|
|
pass # Cache miss is fine
|
|
|
|
# get copyright first
|
|
copyright = ""
|
|
if entry := TrackStore.trackhashmap.get(trackhash, None):
|
|
for track in entry.tracks:
|
|
copyright = track.copyright
|
|
|
|
if copyright:
|
|
break
|
|
|
|
lyrics = get_lyrics_file(filepath)
|
|
|
|
if not lyrics:
|
|
lyrics = get_lyrics_from_tags(trackhash) # type: ignore
|
|
|
|
if not lyrics:
|
|
lyrics = get_lyrics_from_duplicates(filepath, trackhash)
|
|
|
|
# check lyrics plugins
|
|
if not lyrics:
|
|
try:
|
|
# Get track metadata for plugin search
|
|
entry = TrackStore.trackhashmap.get(trackhash, None)
|
|
if entry and len(entry.tracks) > 0:
|
|
track = entry.tracks[0] # Use first track for metadata
|
|
title = getattr(track, "title", "") or ""
|
|
artist = ""
|
|
if hasattr(track, "artists") and track.artists:
|
|
artist = (
|
|
track.artists[0].name
|
|
if hasattr(track.artists[0], "name")
|
|
else str(track.artists[0])
|
|
)
|
|
album = ""
|
|
if hasattr(track, "album") and track.album:
|
|
album = (
|
|
track.album.name
|
|
if hasattr(track.album, "name")
|
|
else str(track.album)
|
|
)
|
|
|
|
# Only proceed if we have basic metadata
|
|
if title and artist:
|
|
# Initialize lyrics plugin
|
|
lyrics_plugin = Lyrics()
|
|
if lyrics_plugin.enabled:
|
|
# LRCLIB-first metadata retrieval with provider fallback.
|
|
lrc_content = lyrics_plugin.download_lyrics_by_metadata(
|
|
title=title,
|
|
artist=artist,
|
|
path=filepath,
|
|
album=album,
|
|
)
|
|
|
|
# Fallback to provider search result track IDs when metadata fetch fails.
|
|
if not lrc_content:
|
|
search_results = (
|
|
lyrics_plugin.search_lyrics_by_title_and_artist(
|
|
title, artist
|
|
)
|
|
)
|
|
if search_results and len(search_results) > 0:
|
|
perfect_match = search_results[0]
|
|
if album:
|
|
for result in search_results:
|
|
result_title = result.get("title", "").lower()
|
|
result_album = result.get("album", "").lower()
|
|
if (
|
|
result_title == title.lower()
|
|
and result_album == album.lower()
|
|
):
|
|
perfect_match = result
|
|
break
|
|
|
|
track_id = perfect_match.get("track_id")
|
|
if track_id:
|
|
lrc_content = lyrics_plugin.download_lyrics(
|
|
track_id, filepath
|
|
)
|
|
|
|
if lrc_content and len(lrc_content.strip()) > 0:
|
|
lyrics = Lyrics_class(lrc_content)
|
|
except Exception:
|
|
# Log error but don't break the lyrics fetching process
|
|
# In production, you might want to log this error
|
|
pass
|
|
|
|
if not lyrics:
|
|
return {"error": "No lyrics found"}
|
|
|
|
if lyrics.is_synced:
|
|
text = lyrics.format_synced_lyrics()
|
|
else:
|
|
text = lyrics.format_unsynced_lyrics()
|
|
|
|
result = {"lyrics": text, "synced": lyrics.is_synced, "copyright": copyright}
|
|
|
|
# Cache lyrics for 24 hours (lyrics rarely change)
|
|
if cache.is_available():
|
|
import contextlib
|
|
|
|
with contextlib.suppress(Exception):
|
|
cache.set(cache_key, json.dumps(result), ex=86400)
|
|
|
|
return result, 200
|
|
|
|
|
|
@api.post("/check")
|
|
def check_lyrics(body: SendLyricsBody):
|
|
"""
|
|
Checks if lyrics file or tag exists for a track
|
|
"""
|
|
result = send_lyrics(body)
|
|
|
|
if "error" in result:
|
|
return {"exists": False}
|
|
else:
|
|
return {"exists": True}, 200
|