A concert calendar for venues across the Raleigh-Durham-Chapel Hill area.
Triangle Shows aggregates live music listings from 21+ venues across the Triangle and displays them on an interactive calendar. Every 6 hours, a background scraper pulls fresh event data from each venue. Visitors can search events, filter by city or venue, favorite shows, hide clutter, and export to their personal calendar via .ics download or live subscription feed.
| Venue | City |
|---|---|
| Boom Club | Durham |
| DPAC | Durham |
| Motorco Music Hall | Durham |
| Rubies on Five Points | Durham |
| Shadowbox Studio | Durham |
| Stancyks | Durham |
| The Pinhook | Durham |
| Cat's Cradle and Back Room | Carrboro |
| Local 506 | Chapel Hill |
| The Cave | Chapel Hill |
| Haw River Ballroom | Saxapahaw |
| Chapel of Bones | Raleigh |
| Kings | Raleigh |
| Koka Booth Amphitheatre | Cary |
| Lincoln Theatre | Raleigh |
| Neptune's Parlour | Raleigh |
| Pour House | Raleigh |
| Red Hat Amphitheater | Raleigh |
| Slim's | Raleigh |
| The Ritz | Raleigh |
- Full-month calendar — powered by FullCalendar v6 with month and list views
- Search & filters — filter by city, venue, or size; text search by artist/event name
- Event details — modal with doors/show time, price, description, and ticket link
- Favorites — heart events and export them as a
.icsfile - Hide shows — hide events cluttering your view; restore them any time
- Calendar subscription — add
https://triangle-shows.net/feeds/events.icsto Apple Calendar, Google Calendar, or Outlook for live updates - Color palettes — 5 themes (Amber, Phosphor, Midnight, Wisteria, Durham) with light/dark modes
- Durham subdomain —
durm.triangle-shows.netshows only Durham venues with the Durham Bulls palette
See [[SELF-HOSTING]] for setup instructions.
backend/
app/
api/ # FastAPI route handlers (v1 canonical surface, deprecated unversioned aliases, iCal feed, shared helpers)
services/ # Shared query + cross-venue de-duplication logic used by every read surface
scrapers/ # One scraper per venue/platform
models.py # SQLAlchemy ORM — Venue, Event, ScrapeLog
schemas.py # Pydantic response models
scheduler.py # APScheduler job config
seed.py # Venue seed data (names, URLs, colors, capacities)
config.py # Settings loaded from .env
alembic/ # Database migrations
tests/ # pytest suite (real-PostgreSQL harness; see backend/README.md)
frontend/
index.html
css/styles.css
js/
app.js # FullCalendar init, loading screen, hidden-show chips
fullcalendar-adapter.js # Maps neutral /api/v1 events → FullCalendar's event shape
filters.js # Search, city, venue, and size filter logic
config.js # Color palettes, API base URL, site config
modal.js # Event detail modal
favorites.js # Heart, hide, restore, and export logic
tools/ # Dev utilities (see below)
The frontend is buildless, but the adapter that maps the neutral /api/v1 feed into FullCalendar's event shape (js/fullcalendar-adapter.js) has unit tests on Node's built-in runner — no build step, no npm install:
node --test frontend/tests/*.test.jsThey lock the price/time string formatting and the extendedProps key set that modal.js/filters.js/favorites.js read by name. Backend tests live under backend/ — see backend/README.md.
Testing scripts are in tools/ for debugging scrapers and development. Fair warning — these are primarily tested on my machine and some may be outdated.
| Script | What it does |
|---|---|
run_scrape.py |
Calls the scrape API and prints a summary; logs to tools/scrape_results.log |
diagnose_scrapers.py |
Runs scrapers individually and reports output and errors |
inspect_html.py |
Fetches and prints raw HTML from a venue page for scraper debugging |
inspect_js_venues.py |
Inspects JS-heavy venue pages to find embedded API or widget patterns |
check_venue_urls.py |
Spot-checks that venue event page URLs resolve |
check_roots.py |
Verifies scraper URL roots are reachable |
wait_for_deploy.py |
Polls /api/health until a new Cloud Build deploy goes live |
import_submissions.py |
Imports approved event submissions from a Google Sheet into the DB |
Each venue is handled by one scraper type. Last updated 2026-06-29 — may drift as venues are added.
| Scraper | Venues |
|---|---|
ticketmaster |
Koka Booth Amphitheatre, Red Hat Amphitheater, DPAC, The Ritz |
rhp_events |
Lincoln Theatre, Cat's Cradle, Cat's Cradle Back Room, Local 506, The Pinhook |
motorco |
Motorco Music Hall |
eventprime |
Kings |
tribe_events |
The Cave |
venuepilot |
Haw River Ballroom, Rubies on Five Points, Stanczyks |
squarespace |
Neptune's Parlour, Boom Club |
mec |
Shadowbox Studio, Slim's |
tickpick_organizer |
Chapel of Bones |
webflow_cms |
Pour House |
The authoritative source is backend/app/seed.py — each venue dict has a scraper_type field.
| Layer | Technology |
|---|---|
| Backend | Python 3.12, FastAPI, SQLAlchemy (async), Alembic |
| Database | PostgreSQL (asyncpg driver) |
| Scraping | httpx, BeautifulSoup4, Ticketmaster Discovery API |
| Frontend | Vanilla JS, FullCalendar v6 |
| Scheduling | APScheduler (local) / Google Cloud Scheduler (production) |
| Deployment | Google Cloud Run + Neon PostgreSQL + Cloudflare |
This fork runs on Railway (project triangle-shows, production environment) rather than the upstream Cloud Run setup. The service builds from this repo, and pushes to main deploy automatically via .github/workflows/deploy.yml.
Because Railway's GitHub App is not installed on the fork, the workflow drives the deploy through the Railway CLI (railway redeploy --from-source) instead of a native Railway deployment trigger. It requires one repo secret:
RAILWAY_TOKEN— a Railway project token scoped to thetriangle-showsproduction environment (Railway dashboard → project → Settings → Tokens).
To deploy a specific commit out of band, run the workflow manually (workflow_dispatch) or railway redeploy --service triangle-shows --from-source --yes locally against a linked project.
Upcoming features and ideas are tracked in GitHub Issues. Some things I'm currently thinking about:
- Adding more venues (Sharp 9 Gallery, the Fruit, others)
- Handling custom/one-off events and form submission
- Performance improvements
- Possibly pulling in events from Instagram accounts like Fuzzy Needle
I'm looking for co-developers — this thing could be really cool and TBH I'm not a professional developer. Reach out to me if you want to help build it!
See GIT_CONVENTIONS.md for the branch/PR workflow if you want to contribute.
GNU General Public License v3. See LICENSE.
Contact me at @tyfi on Bluesky, or you can email mail@triangle-shows.net