A monorepo of web apps that turn entertainment APIs into interactive dashboards -- analyze YouTube channels, rank TV show seasons, explore Spotify artist catalogs, and dissect a decade of Last.fm listening.
Built with Next.js 16, TypeScript, and a shared component library. Each app connects to a different public API (YouTube, OMDb/TMDB, Spotify, Deezer, Last.fm) and presents the data through searchable, sortable, and themeable interfaces.
The most complete app in this repo. A full-stack platform for deep YouTube channel analysis.
Sync an entire channel's video catalog into a Neon Postgres database, then explore it through a 5-tab dashboard: Overview (KPIs, score distribution, top performers), Videos (filterable table with engagement rates), Timeline (chronological trends), Sagas (AI-detected narrative arcs grouping related uploads), and Discover (AI-generated starter packs, evolution timelines, channel DNA profiles).
Every video gets a 0-100 score computed from five weighted components -- reach, engagement, momentum, efficiency, and community -- using sigmoid normalization and duration-aware bucketing so short and long videos are compared fairly.
The sync pipeline runs as Inngest background jobs with real-time SSE progress streaming. An AI chat drawer (Cmd+J) lets you ask natural language questions with full channel context. Channels can be compared side-by-side (2-5 at once), and reports can be shared via expiring snapshot links.
Stack: Neon Postgres, Drizzle ORM, NextAuth (Google OAuth), Inngest, Vercel AI SDK (Gemini 2.0 Flash / Groq), React Three Fiber, Vitest, PostHog.
Find out which season of any TV series is the best.
Search for a show and the app fetches every episode's rating from OMDb and (optionally) TMDB, computes each season's median score, and presents a ranked table from best to worst. Click any season to see the episode-by-episode breakdown with IMDb links and dual-source ratings. The median-based ranking reduces the impact of single outlier episodes on a season's overall standing.
Both data sources are cached in-memory (24h TTL) and rate-limited. The app includes Playwright E2E tests covering search, navigation, theme toggling, and accessibility.
Stack: OMDb API, TMDB API (optional), Playwright, PostHog. Libre Baskerville + JetBrains Mono typography with a gold accent theme.
Deep analysis of a Last.fm scrobble history -- the questions Last.fm's own charts cannot answer.
Last.fm's aggregate endpoints only tell you what you played most. This app imports the entire play-by-play record (225k scrobbles across a decade) into Postgres, then computes things that need the raw stream: an hour-by-weekday heatmap, a per-day calendar going back to the first scrobble, listening sessions detected from gaps between plays, and consecutive-day streaks.
The more interesting half is behavioural. Every artist is classified by how they moved through rotation -- companion, phase, abandoned, recent, or casual -- from their active span and how much of their listening collapsed into a single month. Alongside that: discovery rate over time, comeback detection (a long silence followed by a return that stuck), year-over-year rank movement, one-hit wonders, and concentration measured as both top-10 share and normalised Shannon entropy.
The import is one resumable mechanism for both the initial backfill and ongoing syncs. It pins its upper bound so page numbers stay stable mid-run, works in bounded chunks that save a cursor, and leans on a unique index so a retried page is harmless.
Stack: Last.fm API, Neon Postgres, Drizzle ORM, recharts, TanStack Table. Tables live in a dedicated lastfm Postgres schema so the database can be shared safely.
See every track an artist has ever released on Spotify, ranked by popularity score.
Unlike Spotify's own interface (which only shows the top 10), this app traverses the artist's full album catalog -- albums, singles, compilations -- deduplicates tracks, and ranks them all by Spotify's internal popularity metric (0-100). Each track has a 30-second audio preview powered by the Deezer API, with a built-in play/pause player that works directly from the table.
Stack: Spotify Web API (client credentials flow), Deezer Search API, PostHog. Geist typography with Spotify's green accent.
All four apps share the same core stack through the monorepo's shared packages. The YouTube app extends it significantly with database, auth, AI, and background job infrastructure; Listening Habits adds a database and charting.
| Layer | Technology | Used by |
|---|---|---|
| Monorepo | Turborepo + pnpm workspaces | All |
| Framework | Next.js 16 (App Router, Turbopack) | All |
| Language | TypeScript 5 | All |
| Styling | Tailwind CSS + CSS variables theming | All |
| UI Components | shadcn/ui + Radix UI + Lucide icons | All |
| Data Fetching | TanStack Query v5 | All |
| Tables | TanStack Table v8 | All |
| Theme | next-themes (dark/light + system) | All |
| Analytics | PostHog | All |
| Deployment | Vercel | All |
| Database | Neon Postgres + Drizzle ORM | YouTube, Listening Habits |
| Charts | recharts (heatmaps hand-rolled as CSS grids) | Listening Habits |
| Auth | NextAuth v5 + Google OAuth | YouTube |
| AI | Vercel AI SDK, Google Gemini 2.0 Flash, Groq Llama 3.1 | YouTube |
| Background Jobs | Inngest (sync pipeline, scheduled cleanup) | YouTube |
| 3D Graphics | React Three Fiber + Three.js | YouTube |
| Unit Tests | Vitest + @vitest/coverage-v8 | YouTube |
| E2E Tests | Playwright | IMDb |
Three internal packages keep the apps consistent and reduce duplication:
Utilities and hooks shared across all apps:
cn()-- class name utility (clsx + tailwind-merge)createCache()-- in-memory LRU cache with TTL, max size, and hit/miss statscheckRateLimit()/createRateLimitConfig()-- per-IP rate limiting with sliding windowsapiFetch()/ApiError-- fetch wrapper with structured error handlingcorsHeaders/optionsResponse/withRateLimitHeaders-- HTTP response helperscreateSuggestionsHook()-- factory for building TanStack Query-based autocomplete hookscreateValidator()/getSafeErrorMessage()-- input validation and safe error extraction
Shared React component library:
- Layout: Navbar, Providers (QueryClient + ThemeProvider + PostHog)
- Data: DataTable (TanStack Table wrapper with sorting, filtering, pagination, expandable rows, sticky headers)
- Forms: SearchAutocomplete (debounced search with keyboard navigation and custom renderers), SuggestionLinks
- Primitives: Button (7 variants), Card, Input, Dialog, Tooltip, Popover, Skeleton
- Theme: ThemeToggle (light/dark with MutationObserver sync), ThemeProvider
- Analytics: PostHogProvider (auto page-view tracking)
Shared Tailwind preset providing:
- CSS variable-based color system (background, foreground, primary, secondary, muted, accent, destructive, border, input, ring)
- Dark mode via
classstrategy - Border radius tokens (sm, md, lg from
--radius) - Custom keyframes and animations (fade-in, scale-in, shimmer, accordion-down/up)
tailwindcss-animateplugin
Each app extends this preset and defines the CSS variables in its own globals.css to create its unique visual identity (gold for IMDb, Spotify green, teal/violet for YouTube).
- Node.js 20+
- pnpm 9.15+
pnpm installpnpm dev # run all apps simultaneously
pnpm dev --filter youtube-analyzer # YouTube only (port 3003)
pnpm dev --filter imdb-best-season # IMDb only (port 3001)
pnpm dev --filter spotify-popularity # Spotify only (port 3002)pnpm build # build all apps (Turborepo-orchestrated)
pnpm lint # lint all apps and packages
pnpm clean # remove .next, dist, and node_modulesEach app requires its own .env.local. See the individual READMEs for the full list:
- YouTube Analyzer -- 12 variables (5 required)
- IMDb Best Season -- 3 variables (1 required)
- Spotify Popularity -- 3 variables (2 required)
data-projects/
├── apps/
│ ├── youtube/ # YouTube channel analyzer (port 3003)
│ │ ├── src/app/ # 42+ API routes, 6 pages
│ │ ├── src/components/ # 30+ React components
│ │ ├── src/db/ # Drizzle schema (13 tables)
│ │ └── src/lib/ # Scoring, sync, AI, auth
│ ├── imdb-best-season/ # TV show season ranker (port 3001)
│ │ ├── src/app/ # 2 API routes, 2 pages
│ │ ├── src/components/ # 6 React components
│ │ └── e2e/ # Playwright E2E tests
│ └── spotify-popularity/ # Spotify track explorer (port 3002)
│ ├── src/app/ # 3 API routes, 2 pages
│ ├── src/components/ # 8 React components
│ └── src/lib/spotify/ # Spotify API + auth layer
├── packages/
│ ├── shared/ # Utilities, hooks, API helpers
│ ├── ui/ # Shared component library
│ └── tailwind-config/ # Shared design tokens + preset
├── turbo.json # Turborepo task pipeline
├── pnpm-workspace.yaml # Workspace configuration
└── package.json # Root scripts (dev, build, lint, clean)