- 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
161 lines
5.5 KiB
Python
161 lines
5.5 KiB
Python
"""
|
|
Per-Show Setlist Importer
|
|
Fetches setlist for each show individually
|
|
"""
|
|
import requests
|
|
from sqlmodel import Session, select
|
|
from database import engine
|
|
from models import Show, Song, Performance
|
|
|
|
BASE_URL = "https://elgoose.net/api/v2"
|
|
|
|
def fetch_show_setlist(api_show_id):
|
|
"""Fetch setlist for a specific show"""
|
|
url = f"{BASE_URL}/setlists/showid/{api_show_id}.json"
|
|
try:
|
|
response = requests.get(url, timeout=30)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
if data.get('error') == 1:
|
|
return None
|
|
return data.get('data', [])
|
|
except Exception as e:
|
|
return None
|
|
|
|
def main():
|
|
print("PER-SHOW SETLIST IMPORTER")
|
|
print("=" * 40)
|
|
|
|
with Session(engine) as session:
|
|
# Get all shows
|
|
shows = session.exec(select(Show)).all()
|
|
print(f"Found {len(shows)} shows in database")
|
|
|
|
# Build song map by title
|
|
songs = session.exec(select(Song)).all()
|
|
song_map = {s.title.lower(): s.id for s in songs}
|
|
print(f"Mapped {len(song_map)} songs")
|
|
|
|
# Get existing performances
|
|
print("Loading existing performances...")
|
|
existing_map = {} # (show_id, song_id, position) -> Performance Object
|
|
perfs = session.exec(select(Performance)).all()
|
|
for p in perfs:
|
|
existing_map[(p.show_id, p.song_id, p.position)] = p
|
|
print(f"Found {len(existing_map)} existing performances")
|
|
|
|
# We need API show IDs. The ElGoose API shows endpoint returns show_id.
|
|
# Let's fetch and correlate by date
|
|
print("Fetching API shows to get API IDs...")
|
|
api_shows = {} # date_str -> api_show_id
|
|
|
|
page = 1
|
|
seen_ids = set()
|
|
while True:
|
|
url = f"{BASE_URL}/shows.json"
|
|
try:
|
|
resp = requests.get(url, params={"page": page}, timeout=30)
|
|
data = resp.json().get('data', [])
|
|
if not data:
|
|
break
|
|
|
|
# Loop detection
|
|
first_id = data[0].get('show_id') if data else None
|
|
if first_id in seen_ids:
|
|
print(f" Loop detected at page {page}")
|
|
break
|
|
if first_id:
|
|
seen_ids.add(first_id)
|
|
|
|
for s in data:
|
|
# CRITICAL: Only include Goose shows
|
|
if s.get('artist') != 'Goose':
|
|
continue
|
|
date_str = s['showdate']
|
|
api_shows[date_str] = s['show_id']
|
|
page += 1
|
|
except Exception as e:
|
|
print(f" Error on page {page}: {e}")
|
|
break
|
|
|
|
print(f"Got {len(api_shows)} API show IDs")
|
|
|
|
# Now import setlists for each show
|
|
total_added = 0
|
|
total_updated = 0
|
|
processed = 0
|
|
|
|
for show in shows:
|
|
date_str = show.date.strftime('%Y-%m-%d')
|
|
api_show_id = api_shows.get(date_str)
|
|
|
|
if not api_show_id:
|
|
continue
|
|
|
|
# REMOVED: Skipping logic. We verify everything.
|
|
# existing_for_show = ...
|
|
|
|
# Fetch setlist
|
|
setlist = fetch_show_setlist(api_show_id)
|
|
if not setlist:
|
|
continue
|
|
|
|
added = 0
|
|
updated = 0
|
|
|
|
for item in setlist:
|
|
song_title = item.get('songname', '').lower()
|
|
song_id = song_map.get(song_title)
|
|
|
|
if not song_id:
|
|
continue
|
|
|
|
position = item.get('position', 0)
|
|
key = (show.id, song_id, position)
|
|
|
|
# Resolve set name
|
|
set_val = str(item.get('setnumber', '1'))
|
|
if set_val.isdigit():
|
|
set_name = f"Set {set_val}"
|
|
elif set_val.lower() == 'e':
|
|
set_name = "Encore"
|
|
elif set_val.lower() == 'e2':
|
|
set_name = "Encore 2"
|
|
elif set_val.lower() == 's':
|
|
set_name = "Soundcheck"
|
|
else:
|
|
set_name = f"Set {set_val}"
|
|
|
|
if key in existing_map:
|
|
# Update Check
|
|
perf = existing_map[key]
|
|
if not perf.set_name or perf.set_name != set_name:
|
|
perf.set_name = set_name
|
|
session.add(perf)
|
|
updated += 1
|
|
total_updated += 1
|
|
continue
|
|
|
|
# Create New
|
|
perf = Performance(
|
|
show_id=show.id,
|
|
song_id=song_id,
|
|
position=position,
|
|
set_name=set_name,
|
|
segue=bool(item.get('segue', 0)),
|
|
notes=item.get('footnote')
|
|
)
|
|
session.add(perf)
|
|
existing_map[key] = perf # Add to map to prevent dupes in same run
|
|
added += 1
|
|
total_added += 1
|
|
|
|
if added > 0 or updated > 0:
|
|
session.commit()
|
|
processed += 1
|
|
print(f"Show {date_str}: +{added} new, ~{updated} updated")
|
|
|
|
print(f"\nImport Complete! Added: {total_added}, Updated: {total_updated}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|