Implement WebSocket support for real-time radio updates and enhance LRC fetching logic

This commit is contained in:
2025-09-26 11:36:13 -04:00
parent 9b32a8f984
commit 65e0d3ae7a
3 changed files with 270 additions and 12 deletions

View File

@@ -4,6 +4,7 @@ import time
import datetime
import os
import random
import asyncio
from uuid import uuid4 as uuid
from typing import Union, Optional, Iterable
from aiohttp import ClientSession, ClientTimeout
@@ -478,10 +479,11 @@ class RadioUtil:
playlist, len(self.active_playlist[playlist]),
)
"""Loading Complete"""
logging.info(f"Skipping: {playlist}")
await self._ls_skip(playlist) # Request skip from LS to bring streams current
"""Loading Complete"""
# Request skip from LS to bring streams current
for playlist in self.playlists:
logging.info("Skipping: %s", playlist)
await self._ls_skip(playlist)
self.playlists_loaded = True
except Exception as e:
logging.info("Playlist load failed: %s", str(e))

View File

@@ -22,12 +22,12 @@ class MetadataFetchError(Exception):
# Suppress all logging output from this module and its children
for name in [__name__, "utils.sr_wrapper"]:
logger = logging.getLogger(name)
logger.setLevel(logging.CRITICAL)
logger.setLevel(logging.INFO) # Temporarily set to INFO for debugging LRC
logger.propagate = False
for handler in logger.handlers:
handler.setLevel(logging.CRITICAL)
handler.setLevel(logging.INFO)
# Also set the root logger to CRITICAL as a last resort (may affect global logging)
logging.getLogger().setLevel(logging.CRITICAL)
# logging.getLogger().setLevel(logging.CRITICAL)
load_dotenv()
@@ -746,3 +746,55 @@ class SRUtil:
except Exception as e:
logging.critical("Error: %s", str(e))
return False
async def get_lrc_by_track_id(self, track_id: int) -> Optional[str]:
"""Get LRC lyrics by track ID."""
logging.info(f"SR: Fetching metadata for track ID {track_id}")
metadata = await self.get_metadata_by_track_id(track_id)
lrc = metadata.get('lyrics') if metadata else None
logging.info(f"SR: LRC {'found' if lrc else 'not found'}")
return lrc
async def get_lrc_by_artist_song(
self, artist: str, song: str, album: Optional[str] = None, duration: Optional[int] = None
) -> Optional[str]:
"""Get LRC lyrics by artist and song, optionally filtering by album and duration."""
logging.info(f"SR: Searching tracks for {artist} - {song}")
tracks = await self.get_tracks_by_artist_song(artist, song)
logging.info(f"SR: Found {len(tracks) if tracks else 0} tracks")
if not tracks:
return None
# Filter by album if provided
if album:
tracks = [
t for t in tracks
if t.get('album', {}).get('title', '').lower() == album.lower()
]
if not tracks:
return None
# If duration provided, select the track with closest duration match
if duration is not None:
tracks_with_diff = [
(t, abs(t.get('duration', 0) - duration)) for t in tracks
]
tracks_with_diff.sort(key=lambda x: x[1])
best_track, min_diff = tracks_with_diff[0]
logging.info(f"SR: Best match duration diff: {min_diff}s")
# If the closest match is more than 5 seconds off, consider no match
if min_diff > 5:
logging.info("SR: Duration diff too large, no match")
return None
else:
best_track = tracks[0]
track_id = best_track.get('id')
logging.info(f"SR: Using track ID {track_id}")
if not track_id:
return None
return await self.get_lrc_by_track_id(track_id)