Files
eventlens/backend/app/providers/bandsintown.py
T
2026-04-17 16:39:13 +02:00

135 lines
4.4 KiB
Python

from datetime import datetime
from urllib.parse import quote
import requests
from app.config import settings
from app.models import RegionScope, WatchType
from app.providers.utils import normalize_search_text
class BandsintownProvider:
base_url = "https://rest.bandsintown.com"
source_name = "bandsintown"
def is_configured(self) -> bool:
return bool(settings.bandsintown_app_id)
def search_events(
self,
term: str,
watch_type: WatchType,
region_scope: RegionScope,
) -> list[dict]:
if not self.is_configured():
setattr(self, "last_status", "ok")
setattr(
self,
"last_message",
"Bandsintown ist nicht konfiguriert. Bitte eine gueltige BANDSINTOWN_APP_ID setzen.",
)
return []
if watch_type != WatchType.artist:
setattr(self, "last_status", "ok")
setattr(
self,
"last_message",
f"Bandsintown skipped non-artist watch item '{term}'.",
)
return []
artist_name = quote(term, safe="")
url = f"{self.base_url}/artists/{artist_name}/events"
response = requests.get(
url,
params={"app_id": settings.bandsintown_app_id, "date": "upcoming"},
timeout=30,
)
if response.status_code in {401, 403, 429}:
setattr(self, "last_status", "blocked")
setattr(
self,
"last_message",
(
f"Bandsintown rejected app_id for '{term}' "
f"with status {response.status_code}."
),
)
return []
if response.status_code == 404:
setattr(self, "last_status", "ok")
setattr(
self,
"last_message",
f"Bandsintown found no artist match for '{term}'.",
)
return []
response.raise_for_status()
payload = response.json()
if not isinstance(payload, list):
setattr(self, "last_status", "error")
setattr(
self,
"last_message",
f"Bandsintown returned an unexpected payload for '{term}'.",
)
return []
setattr(self, "last_status", "ok")
setattr(
self,
"last_message",
f"Bandsintown returned {len(payload)} raw events for term '{term}'.",
)
results: list[dict] = []
normalized_term = normalize_search_text(term)
for item in payload:
venue = item.get("venue") or {}
city = venue.get("city")
country = venue.get("country")
if region_scope == RegionScope.hamburg and (city or "").casefold() != "hamburg":
continue
if region_scope == RegionScope.germany and (country or "").casefold() != "germany":
continue
datetime_value = item.get("datetime")
event_date = None
if datetime_value:
try:
event_date = datetime.fromisoformat(
datetime_value.replace("Z", "+00:00")
).replace(tzinfo=None)
except ValueError:
event_date = None
offers = item.get("offers") or []
ticket_url = next((offer.get("url") for offer in offers if offer.get("url")), None)
title = item.get("title") or f"{term} live"
artist_name = item.get("artist", {}).get("name", term)
haystack = normalize_search_text(f"{artist_name} {title}")
if normalized_term not in haystack:
continue
results.append(
{
"external_id": str(item.get("id")),
"title": title,
"matched_term": term,
"venue_name": venue.get("name"),
"city": city,
"country_code": "DE" if country == "Germany" else country,
"event_date": event_date,
"ticket_url": ticket_url or item.get("url"),
"image_url": item.get("artist", {}).get("thumb_url"),
"raw_payload": item,
}
)
return results