mirror of
https://github.com/bnair123/MusicAnalyser.git
synced 2026-02-25 11:46:07 +00:00
feat: migrate to PostgreSQL and enhance playlist curation
- Migrate database from SQLite to PostgreSQL (100.91.248.114:5433) - Fix playlist curation to use actual top tracks instead of AI name matching - Add /playlists/history endpoint for historical playlist viewing - Add Playlist Archives section to frontend with expandable history - Add playlist-modify-* scopes to Spotify OAuth for playlist creation - Rewrite Genius client to use official API (fixes 403 scraping blocks) - Ensure playlists are created on Spotify before curation attempts - Add DATABASE.md documentation for PostgreSQL schema - Add migrations for PlaylistConfig and composition storage
This commit is contained in:
@@ -10,6 +10,7 @@ from .models import (
|
||||
PlayHistory as PlayHistoryModel,
|
||||
Track as TrackModel,
|
||||
AnalysisSnapshot,
|
||||
PlaylistConfig,
|
||||
)
|
||||
from . import schemas
|
||||
from .ingest import (
|
||||
@@ -220,13 +221,18 @@ async def refresh_six_hour_playlist(db: Session = Depends(get_db)):
|
||||
end_date = datetime.utcnow()
|
||||
start_date = end_date - timedelta(hours=6)
|
||||
|
||||
spotify_client = get_spotify_client()
|
||||
playlist_service = PlaylistService(
|
||||
db=db,
|
||||
spotify_client=get_spotify_client(),
|
||||
spotify_client=spotify_client,
|
||||
recco_client=get_reccobeats_client(),
|
||||
narrative_service=NarrativeService(),
|
||||
)
|
||||
|
||||
# Ensure playlists exist (creates on Spotify if needed)
|
||||
user_id = await spotify_client.get_current_user_id()
|
||||
await playlist_service.ensure_playlists_exist(user_id)
|
||||
|
||||
result = await playlist_service.curate_six_hour_playlist(start_date, end_date)
|
||||
|
||||
snapshot = AnalysisSnapshot(
|
||||
@@ -239,6 +245,7 @@ async def refresh_six_hour_playlist(db: Session = Depends(get_db)):
|
||||
playlist_theme=result.get("theme_name"),
|
||||
playlist_theme_reasoning=result.get("description"),
|
||||
six_hour_playlist_id=result.get("playlist_id"),
|
||||
playlist_composition=result.get("composition"),
|
||||
)
|
||||
db.add(snapshot)
|
||||
db.commit()
|
||||
@@ -256,13 +263,18 @@ async def refresh_daily_playlist(db: Session = Depends(get_db)):
|
||||
end_date = datetime.utcnow()
|
||||
start_date = end_date - timedelta(days=1)
|
||||
|
||||
spotify_client = get_spotify_client()
|
||||
playlist_service = PlaylistService(
|
||||
db=db,
|
||||
spotify_client=get_spotify_client(),
|
||||
spotify_client=spotify_client,
|
||||
recco_client=get_reccobeats_client(),
|
||||
narrative_service=NarrativeService(),
|
||||
)
|
||||
|
||||
# Ensure playlists exist (creates on Spotify if needed)
|
||||
user_id = await spotify_client.get_current_user_id()
|
||||
await playlist_service.ensure_playlists_exist(user_id)
|
||||
|
||||
result = await playlist_service.curate_daily_playlist(start_date, end_date)
|
||||
|
||||
snapshot = AnalysisSnapshot(
|
||||
@@ -273,6 +285,7 @@ async def refresh_daily_playlist(db: Session = Depends(get_db)):
|
||||
metrics_payload={},
|
||||
narrative_report={},
|
||||
daily_playlist_id=result.get("playlist_id"),
|
||||
playlist_composition=result.get("composition"),
|
||||
)
|
||||
db.add(snapshot)
|
||||
db.commit()
|
||||
@@ -286,32 +299,71 @@ async def refresh_daily_playlist(db: Session = Depends(get_db)):
|
||||
@app.get("/playlists")
|
||||
async def get_playlists_metadata(db: Session = Depends(get_db)):
|
||||
"""Returns metadata for the managed playlists."""
|
||||
latest_snapshot = (
|
||||
db.query(AnalysisSnapshot)
|
||||
.filter(AnalysisSnapshot.six_hour_playlist_id != None)
|
||||
.order_by(AnalysisSnapshot.date.desc())
|
||||
.first()
|
||||
|
||||
six_hour_config = (
|
||||
db.query(PlaylistConfig).filter(PlaylistConfig.key == "six_hour").first()
|
||||
)
|
||||
daily_config = (
|
||||
db.query(PlaylistConfig).filter(PlaylistConfig.key == "daily").first()
|
||||
)
|
||||
|
||||
return {
|
||||
"six_hour": {
|
||||
"id": latest_snapshot.six_hour_playlist_id
|
||||
if latest_snapshot
|
||||
"id": six_hour_config.spotify_id
|
||||
if six_hour_config
|
||||
else os.getenv("SIX_HOUR_PLAYLIST_ID"),
|
||||
"theme": latest_snapshot.playlist_theme if latest_snapshot else "N/A",
|
||||
"reasoning": latest_snapshot.playlist_theme_reasoning
|
||||
if latest_snapshot
|
||||
else "N/A",
|
||||
"last_refresh": latest_snapshot.date.isoformat()
|
||||
if latest_snapshot
|
||||
"theme": six_hour_config.current_theme if six_hour_config else "N/A",
|
||||
"reasoning": six_hour_config.description if six_hour_config else "N/A",
|
||||
"last_refresh": six_hour_config.last_updated.isoformat()
|
||||
if six_hour_config
|
||||
else None,
|
||||
"composition": six_hour_config.composition if six_hour_config else [],
|
||||
},
|
||||
"daily": {
|
||||
"id": latest_snapshot.daily_playlist_id
|
||||
if latest_snapshot
|
||||
"id": daily_config.spotify_id
|
||||
if daily_config
|
||||
else os.getenv("DAILY_PLAYLIST_ID"),
|
||||
"last_refresh": latest_snapshot.date.isoformat()
|
||||
if latest_snapshot
|
||||
"theme": daily_config.current_theme if daily_config else "N/A",
|
||||
"reasoning": daily_config.description if daily_config else "N/A",
|
||||
"last_refresh": daily_config.last_updated.isoformat()
|
||||
if daily_config
|
||||
else None,
|
||||
"composition": daily_config.composition if daily_config else [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/playlists/history")
|
||||
def get_playlist_history(
|
||||
limit: int = Query(default=20, ge=1, le=100),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Returns historical playlist snapshots."""
|
||||
snapshots = (
|
||||
db.query(AnalysisSnapshot)
|
||||
.filter(
|
||||
(AnalysisSnapshot.playlist_theme.isnot(None))
|
||||
| (AnalysisSnapshot.six_hour_playlist_id.isnot(None))
|
||||
| (AnalysisSnapshot.daily_playlist_id.isnot(None))
|
||||
)
|
||||
.order_by(AnalysisSnapshot.date.desc())
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
result = []
|
||||
for snap in snapshots:
|
||||
result.append(
|
||||
{
|
||||
"id": snap.id,
|
||||
"date": snap.date.isoformat() if snap.date else None,
|
||||
"period_label": snap.period_label,
|
||||
"theme": snap.playlist_theme,
|
||||
"reasoning": snap.playlist_theme_reasoning,
|
||||
"six_hour_id": snap.six_hour_playlist_id,
|
||||
"daily_id": snap.daily_playlist_id,
|
||||
"composition": snap.playlist_composition or [],
|
||||
}
|
||||
)
|
||||
|
||||
return {"history": result}
|
||||
|
||||
Reference in New Issue
Block a user