- 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
167 lines
5.8 KiB
Python
167 lines
5.8 KiB
Python
"""
|
|
Direct setlist import - bypasses broken show_map logic
|
|
Imports setlists by matching dates directly
|
|
"""
|
|
import requests
|
|
import time
|
|
from datetime import datetime
|
|
from sqlmodel import Session, select, func
|
|
from database import engine
|
|
from models import Show, Song, Performance
|
|
from slugify import generate_slug
|
|
|
|
BASE_URL = "https://elgoose.net/api/v2"
|
|
|
|
def fetch_json(endpoint, params=None):
|
|
"""Fetch JSON from El Goose API"""
|
|
url = f"{BASE_URL}/{endpoint}.json"
|
|
try:
|
|
response = requests.get(url, params=params, timeout=30)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
if data.get('error') == 1:
|
|
return None
|
|
return data.get('data', [])
|
|
except Exception as e:
|
|
print(f"Error fetching {endpoint}: {e}")
|
|
return None
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("DIRECT SETLIST IMPORTER")
|
|
print("=" * 60)
|
|
|
|
with Session(engine) as session:
|
|
# Build date -> show_id mapping from our database
|
|
print("\n1. Building date->show mapping from database...")
|
|
shows = session.exec(select(Show)).all()
|
|
date_to_show = {}
|
|
for show in shows:
|
|
date_str = show.date.strftime('%Y-%m-%d')
|
|
date_to_show[date_str] = show.id
|
|
print(f" Found {len(date_to_show)} shows in database")
|
|
|
|
# Build song title -> song_id mapping
|
|
print("\n2. Building song mappings from database...")
|
|
songs = session.exec(select(Song)).all()
|
|
song_title_to_id = {s.title.lower(): s.id for s in songs}
|
|
print(f" Found {len(song_title_to_id)} songs in database")
|
|
|
|
# Fetch setlists from ElGoose
|
|
print("\n3. Fetching setlists from ElGoose API...")
|
|
page = 1
|
|
total_added = 0
|
|
total_skipped = 0
|
|
|
|
while True:
|
|
print(f" Page {page}...", end="", flush=True)
|
|
data = fetch_json("setlists", {"page": page})
|
|
|
|
if not data:
|
|
print(" Done.")
|
|
break
|
|
|
|
added_this_page = 0
|
|
for perf in data:
|
|
# Get the show date from the setlist item
|
|
show_date = perf.get('showdate')
|
|
song_name = perf.get('songname', '').lower()
|
|
|
|
if not show_date or not song_name:
|
|
total_skipped += 1
|
|
continue
|
|
|
|
# Find our show by date
|
|
our_show_id = date_to_show.get(show_date)
|
|
if not our_show_id:
|
|
total_skipped += 1
|
|
continue
|
|
|
|
# Find our song by title
|
|
our_song_id = song_title_to_id.get(song_name)
|
|
if not our_song_id:
|
|
total_skipped += 1
|
|
continue
|
|
|
|
position = perf.get('position', 0)
|
|
|
|
# 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 == position
|
|
)
|
|
).first()
|
|
|
|
if existing:
|
|
continue
|
|
|
|
# Map setnumber
|
|
set_val = str(perf.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}"
|
|
|
|
# Create performance
|
|
new_perf = Performance(
|
|
show_id=our_show_id,
|
|
song_id=our_song_id,
|
|
position=position,
|
|
set_name=set_name,
|
|
segue=bool(perf.get('segue', 0)),
|
|
notes=perf.get('footnote')
|
|
)
|
|
session.add(new_perf)
|
|
added_this_page += 1
|
|
total_added += 1
|
|
|
|
session.commit()
|
|
print(f" added {added_this_page} performances")
|
|
|
|
if page > 500: # Safety limit
|
|
print(" Safety limit reached")
|
|
break
|
|
|
|
page += 1
|
|
time.sleep(0.1) # Be nice to the API
|
|
|
|
# Generate slugs for all performances
|
|
print("\n4. Generating slugs for performances...")
|
|
perfs_without_slugs = session.exec(
|
|
select(Performance).where(Performance.slug == None)
|
|
).all()
|
|
|
|
for perf in perfs_without_slugs:
|
|
if perf.song and perf.show:
|
|
song_slug = perf.song.slug or generate_slug(perf.song.title)
|
|
date_str = perf.show.date.strftime('%Y-%m-%d')
|
|
perf.slug = f"{song_slug}-{date_str}"
|
|
|
|
session.commit()
|
|
print(f" Generated {len(perfs_without_slugs)} slugs")
|
|
|
|
# Final count
|
|
total_perfs = session.exec(select(func.count(Performance.id))).one()
|
|
shows_with_perfs = session.exec(
|
|
select(func.count(func.distinct(Performance.show_id)))
|
|
).one()
|
|
|
|
print("\n" + "=" * 60)
|
|
print("IMPORT COMPLETE!")
|
|
print("=" * 60)
|
|
print(f"\nStats:")
|
|
print(f" • Added: {total_added} new performances")
|
|
print(f" • Skipped: {total_skipped} (no match)")
|
|
print(f" • Total performances: {total_perfs}")
|
|
print(f" • Shows with setlists: {shows_with_perfs}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|