Files
spotify-cantina/app/history_poller.py
T
2026-04-23 00:39:58 -04:00

97 lines
2.7 KiB
Python

"""Background task que consulta recently_played cada 30s y persiste el historial."""
import asyncio
import logging
from datetime import datetime, timezone
from app.database import SessionLocal
from app.models import PlayHistory
from app import spotify
logger = logging.getLogger(__name__)
POLL_INTERVAL = 30 # segundos
async def start_poller():
while True:
try:
await asyncio.to_thread(_fetch_and_save)
except Exception as exc:
logger.warning("history_poller error: %s", exc)
await asyncio.sleep(POLL_INTERVAL)
def _fetch_and_save():
if not spotify.is_authenticated():
return
sp = spotify.get_client()
result = sp.current_user_recently_played(limit=50)
items = result.get("items", [])
if not items:
return
# Recolectar IDs de artistas únicos para lookup de géneros en batch
artist_ids: set[str] = set()
for item in items:
for artist in item["track"]["artists"]:
artist_ids.add(artist["id"])
genre_map: dict[str, list[str]] = {}
artist_id_list = list(artist_ids)
for i in range(0, len(artist_id_list), 50):
batch = artist_id_list[i : i + 50]
try:
data = sp.artists(batch)
for a in data.get("artists") or []:
if a:
genre_map[a["id"]] = a.get("genres", [])
except Exception:
pass
db = SessionLocal()
try:
saved = 0
for item in items:
track = item["track"]
played_at = datetime.fromisoformat(
item["played_at"].replace("Z", "+00:00")
)
exists = (
db.query(PlayHistory)
.filter(
PlayHistory.track_id == track["id"],
PlayHistory.played_at == played_at,
)
.first()
)
if exists:
continue
track_artists = track.get("artists", [])
artist_names = [a["name"] for a in track_artists]
genres: set[str] = set()
for a in track_artists:
genres.update(genre_map.get(a["id"], []))
db.add(
PlayHistory(
track_id=track["id"],
track_name=track["name"],
artists=", ".join(artist_names),
genres=", ".join(sorted(genres)),
played_at=played_at,
)
)
saved += 1
if saved:
db.commit()
logger.info("history_poller: guardadas %d reproducciones", saved)
except Exception:
db.rollback()
raise
finally:
db.close()