Bodega FC

Bodega FC - Independent Record Label Platform

A Django platform for an independent record label that pulls artist catalogs from Spotify and videos from YouTube automatically, so the site stays current without anyone updating it

Year

2025

Role

Full-Stack Developer & Strategic Partner

Duration

12 weeks

Read Time

4 min read

strategydesignengineeringintegration
Bodega FC - Independent Record Label Platform - Bodega FC

Bodega FC: A Digital Home for Independent Music

A web platform for Bodega FC, an independent record label, live at bodegafclabel.com. The core idea: a label site rots the moment someone has to update it by hand, so this one pulls its content from the places it already lives — Spotify for releases, YouTube for videos — and reserves human effort for the editorial parts.

The project came through LTMA, the grant-writing consultancy I co-founded, which worked with the label on funding strategy while I handled design and engineering.

The Problem

Label websites go stale in a predictable way: releases come out, videos drop, shows get booked, and the site reflects none of it because the person with CMS access is busy running a label. Bodega FC needed a site where the default state is current, and where the manual work is limited to the things only a human can write — artist bios, editorial framing, event details.

How It Works

Releases from Spotify

Each artist profile is populated from the Spotify Web API — albums, singles, and top tracks, with artwork and links to streaming platforms:

class SpotifyIntegration:
    def get_artist_catalog(self, artist_id: str) -> ArtistCatalog:
        """Fetch an artist's releases and top tracks from the Spotify API."""
        albums = self.client.artist_albums(
            artist_id, include_groups='album,single'
        )
        top_tracks = self.client.artist_top_tracks(artist_id)
        return ArtistCatalog(albums=albums, top_tracks=top_tracks)

A new release appears on the site as soon as the next refresh runs, with no one at the label doing anything. API responses are cached in PostgreSQL and refreshed by background jobs, both to respect Spotify's rate limits and so the site degrades gracefully — if the API is down, the site serves the last known catalog rather than an error.

Videos from YouTube

The YouTube Data API does the same job for video: each artist's latest uploads — music videos, visualizers, live sessions — are syndicated onto their profile with thumbnails and metadata, on the same cached-refresh pattern as Spotify.

Events on a Map

Shows are the one content type that has to be entered by hand (there's no API for a label's gig calendar), so the payoff had to be worth the entry. Events render on an interactive Mapbox map with custom markers by event type and popups carrying details and ticket links:

map.on('load', () => {
  events.forEach(event => {
    new mapboxgl.Marker({
      element: createCustomMarker(event.type)
    })
    .setLngLat([event.longitude, event.latitude])
    .setPopup(createEventPopup(event))
    .addTo(map)
  })
})
Interactive event map with Mapbox

Live Streams from Twitch

For artists who stream DJ sets or sessions, profiles carry an embedded Twitch player with a live indicator and access to VODs.

Technology Choices

Django + HTMX rather than a SPA. The site is content-first and SEO matters for a label, so server-rendered pages were the right default. HTMX covers the interactive bits — filters, infinite scroll — without shipping a JavaScript framework, and Django's admin gave the label a real editorial interface for free: bios and events are managed there, and new content can be flagged for review so the label keeps editorial control.

PostgreSQL on Heroku. The data is relational in an obvious way — artists to releases, events to venues to artists — plus the cached API responses. Boring and appropriate.

Dark UI as a brand decision, not a theme toggle. The label's identity is underground; the design leans into it with deep blacks, high-contrast type, and restrained accent color. Album artwork does the visual heavy lifting, which is as it should be on a music site.

Lessons Learned

Automate the feeds, hand-craft the editorial. The split that made this project work: anything that exists in an external system of record gets pulled automatically; anything requiring judgment gets a good admin interface. Trying to automate the editorial (or hand-enter the feeds) would have failed in opposite ways.

Cache like the API is going away. Third-party APIs rate-limit, hiccup, and change. Treating the local cache as the source the site actually renders from — with the API as something that refreshes it — made the site resilient by construction.

Server-rendered is still the right call for content sites. This project would have been slower to build and worse at its job as a React SPA. Matching the architecture to the actual problem beat defaulting to the fashionable stack.

DjangoPythonPostgreSQLHerokuSpotify APIYouTube Data APIMapbox GL JSHTMXTwitch Embed

Interested in similar results?

Let's discuss how I can help bring your project to life with the same attention to detail.