fediversion/backend/import_songs_only.py
fullsizemalt b4cddf41ea feat: Initialize Fediversion multi-band platform
- Fork elmeg-demo codebase for multi-band support
- Add data importer infrastructure with base class
- Create band-specific importers:
  - phish.py: Phish.net API v5
  - grateful_dead.py: Grateful Stats API
  - setlistfm.py: Dead & Company, Billy Strings (Setlist.fm)
- Add spec-kit configuration for Gemini
- Update README with supported bands and architecture
2025-12-28 12:39:28 -08:00

199 lines
6.5 KiB
Python

"""
Import ONLY songs and setlists into existing demo database
Uses correct API field mappings based on actual El Goose API response
"""
import requests
from datetime import datetime
from sqlmodel import Session, select
from database import engine
from models import Vertical, Song, Performance, Show
BASE_URL = "https://elgoose.net/api/v2"
ARTIST_ID = 1 # Goose
def fetch_json(endpoint, params=None):
"""Fetch JSON from El Goose API with error handling"""
url = f"{BASE_URL}/{endpoint}.json"
try:
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
if data.get('error') == 1 or data.get('error') == True:
print(f"❌ API Error: {data.get('error_message')}")
return None
return data.get('data', [])
except Exception as e:
print(f"❌ Failed to fetch {endpoint}: {e}")
return None
def import_songs(session, vertical_id):
"""Import all songs using correct API field names"""
print("\n🎵 Importing songs...")
songs_data = fetch_json("songs")
if not songs_data:
print("❌ No song data received")
return {}
song_map = {}
for s in songs_data:
# Check if song exists
existing = session.exec(
select(Song).where(
Song.title == s['name'],
Song.vertical_id == vertical_id
)
).first()
if existing:
song_map[s['id']] = existing.id
else:
song = Song(
title=s['name'],
original_artist=s.get('original_artist'),
vertical_id=vertical_id
)
session.add(song)
session.commit()
session.refresh(song)
song_map[s['id']] = song.id
if len(song_map) % 100 == 0:
print(f" Progress: {len(song_map)} songs...")
print(f"✓ Imported {len(song_map)} songs")
return song_map
def import_setlists(session, vertical_id, song_map):
"""Import setlists for Goose shows only"""
print("\n📋 Importing setlists...")
# Get all our shows from the database
shows = session.exec(
select(Show).where(Show.vertical_id == vertical_id)
).all()
print(f" Found {len(shows)} shows in database")
# Fetch ALL setlists and filter for Goose
print(" Fetching setlists from API...")
setlists_data = fetch_json("setlists")
if not setlists_data:
print("❌ No setlist data found")
return
print(f" Received {len(setlists_data)} total performances")
# Create a map of El Goose show_id to our show id
show_map = {}
for show in shows:
# We need to find the matching El Goose show by date
# Since we imported shows with their original show_id stored... wait, we didn't
# We need to match by date instead
pass
# Actually, let's fetch shows again to get the mapping
shows_data = fetch_json("shows")
if shows_data:
goose_shows = [s for s in shows_data if s.get('artist_id') == ARTIST_ID]
print(f" Found {len(goose_shows)} Goose shows in API")
# Build mapping: El Goose show_id -> our database show id
for eg_show in goose_shows:
eg_date = datetime.strptime(eg_show['showdate'], '%Y-%m-%d').date()
# Find matching show in our DB by date
db_show = session.exec(
select(Show).where(
Show.vertical_id == vertical_id,
Show.date >= datetime.combine(eg_date, datetime.min.time()),
Show.date < datetime.combine(eg_date, datetime.max.time())
)
).first()
if db_show:
show_map[eg_show['show_id']] = db_show.id
print(f" Mapped {len(show_map)} shows")
# Now import performances
performance_count = 0
skipped = 0
for perf_data in setlists_data:
# Only import if this is a Goose show
our_show_id = show_map.get(perf_data.get('show_id'))
if not our_show_id:
skipped += 1
continue
our_song_id = song_map.get(perf_data.get('song_id'))
if not our_song_id:
# Song not found - might be from another artist
skipped += 1
continue
# Check if performance already exists
existing = session.exec(
select(Performance).where(
Performance.show_id == our_show_id,
Performance.song_id == our_song_id,
Performance.position == perf_data.get('position', 0)
)
).first()
if existing:
continue
perf = Performance(
show_id=our_show_id,
song_id=our_song_id,
position=perf_data.get('position', 0),
set_name=perf_data.get('set'),
segue=bool(perf_data.get('segue', 0)),
notes=perf_data.get('notes')
)
session.add(perf)
performance_count += 1
if performance_count % 100 == 0:
session.commit()
print(f" Progress: {performance_count} performances...")
session.commit()
print(f"✓ Imported {performance_count} performances (skipped {skipped} non-Goose)")
def main():
print("="*60)
print("EL GOOSE SONGS & SETLISTS IMPORTER")
print("="*60)
with Session(engine) as session:
# Get existing Goose vertical
vertical = session.exec(
select(Vertical).where(Vertical.slug == "goose")
).first()
if not vertical:
print("❌ Goose vertical not found! Run main import first.")
return
print(f"✓ Found Goose vertical (ID: {vertical.id})")
# Import songs and setlists
song_map = import_songs(session, vertical.id)
import_setlists(session, vertical.id, song_map)
print("\n" + "="*60)
print("✓ IMPORT COMPLETE!")
print("="*60)
print(f"\nImported:")
print(f"{len(song_map)} songs")
print(f"\nDemo environment is now fully populated!")
print(f"\nStart demo servers:")
print(f" Backend: DATABASE_URL='sqlite:///./elmeg-demo.db' uvicorn main:app --reload --port 8001")
print(f" Frontend: NEXT_PUBLIC_API_URL=http://localhost:8001 npm run dev -- -p 3001")
if __name__ == "__main__":
main()