Ericonaldo/podcast-transcript-forum

A modern podcast transcript forum with a tech-forward UI for browsing, searching, and reading podcast transcripts. Features category/host filtering, fuzzy search, multi-language transcript support, and responsive cross-device design.

0

stars

101

commits

JavaScript

primary language

Apr 26, 2026

updated

podcast.leoh.top/

README

PodScribe Logo

PodScribe

A modern podcast transcript forum for browsing, searching, and contributing podcast transcripts across multiple languages.

React 19 Vite 8 Express 5 SQLite FTS5 Tests License

Screenshots

Homepage

DarkLight
Homepage DarkHomepage Light

Episode View

Episode Page

Search

Features

  • Multi-language transcripts — View and switch between transcript languages; default display in the podcast's original language
  • Full-text search — Trigram-based search across podcast names, episodes, transcript content, and guest names
  • Reading mode — Distraction-free transcript reading with floating toggle
  • Embedded media player — Play episodes with synchronized transcript highlighting
  • Revision history — Git-like versioning for community-contributed transcript edits
  • Anonymous upload — Submit transcripts via web UI or Chrome extension
  • Speaker tags — Normalized speaker labels with visual differentiation
  • Light / dark theme — Toggleable with system-preference-aware defaults
  • Responsive design — Optimized for desktop and mobile
  • Category & host browsing — Sidebar navigation by podcast category

Tech Stack

LayerTech
FrontendReact 19, Vite 8, CSS Variables
BackendExpress 5, Node.js
DatabaseSQLite (better-sqlite3) with WAL mode, FTS5 trigram search
TestingJest, Supertest

Quick Start

# Install dependencies
npm install
cd client && npm install && cd ..

# Seed the database (optional)
npm run seed

# Development (two terminals)
npm run dev:server   # Express API on :4010
npm run dev:client   # Vite dev server on :5173

# Production build
npm run build
npm start            # Serves both API and built frontend on :4010

Project Structure

├── client/                 # React frontend
│   ├── src/
│   │   ├── components/     # Header, Sidebar, MediaPlayer, TranscriptEditor, ...
│   │   ├── pages/          # Home, Episode, Podcast, Search, Upload
│   │   └── styles/         # Global CSS, theme variables
│   └── vite.config.js
├── server/                 # Express backend
│   └── src/
│       ├── routes/         # podcasts, episodes, search, upload, revisions
│       ├── db.js           # SQLite setup + FTS5 trigram migration
│       └── index.js        # Express app
├── scripts/                # Utility scripts (seed, crawl, ASR, translation)
├── tests/                  # API test suite
└── data/                   # SQLite database (gitignored)

API

All endpoints are under /api.

MethodEndpointDescription
GET/api/healthHealth check
GET/api/podcastsList all podcasts
POST/api/podcastsCreate a podcast
GET/api/podcasts/:idGet podcast with episodes
GET/api/episodes/:idGet episode with transcripts
GET/api/search?q=...&type=...Full-text search
POST/api/uploadAnonymous upload (podcast + episode + transcript)
GET/api/episodes/:id/revisionsRevision history
POST/api/episodes/:id/revisions/:sha/restoreRestore a revision

Testing

npm test

Runs the full API test suite (~50 test cases) covering CRUD operations, search, multi-language transcripts, revisions, uploads, and cascade deletes.

Scripts

ScriptDescription
npm run seedSeed database with sample data
npm run crawlCrawl podcast sources for episode inventory and RSS transcripts; YouTube episodes are queued for ASR-first processing
node scripts/asr-zh.jsLocal ASR pipeline for Chinese podcasts
node scripts/translate-to-zh.jsTranslate English transcripts to Chinese from ASR/polished English sources
node scripts/batch-polish.jsLLM-powered transcript polishing
python3 scripts/batch_transcribe.py --podcast 'Planet Money' --limit=5Batch transcribe missing audio-only episodes with GPU-to-CPU OOM fallback
python3 scripts/batch_transcribe_youtube.py --podcast '不明白播客' --limit=5Batch transcribe missing YouTube episodes with ASR-first OOM-safe fallback
npm run audit:podcast -- --podcast-id=23Audit one podcast for duplicate translations, speaker-label drift, and source-language mismatches
node scripts/repair-podcast-23.js --episodes=...Repair Dwarkesh Podcast batches by recovering sources, canonicalizing speaker labels, and deduplicating translations
npm run audit:inline-speakersScan all polished transcripts for speaker tags that appear mid-paragraph instead of starting a new paragraph
npm run fix:inline-speakersSplit inline speaker tags into proper paragraph starts across the transcript database
node scripts/audit-english-transcripts.jsAudit all English podcasts for missing transcripts, asr-only episodes, malformed tags, and generic speaker labels
node scripts/fix-english-polish-format.js --podcast-id=25Normalize malformed English llm_polish formatting such as broken timestamp-speaker lines
node scripts/normalize-english-speakers.js --podcast-id=11Deterministically replace generic English speaker labels using host, title, and description hints

Codex Skills

This repo now includes repo-local Codex skills under .codex/skills for the two workflows that are most repetitive here:

  • podcast-transcript-pipeline for episode updates, ASR, repolish, postprocess, inline-speaker cleanup, and transcript QA.
  • podcast-forum-deploy for main-repo builds, newserver deployment, restart, and verification.
  • podcast-quality-repair for podcast-level audit and fast repair planning when speaker labels, transcript language, translation rows, or paragraph splits drift over time.

Transcript policy: for YouTube episodes, start from ASR as the default source of truth. Legacy youtube_auto and youtube_manual rows can exist in old data, but new repair and ingestion flows should replace them instead of building on them.

License

ISC

Contributors

Ericonaldo

101 commits

Ericonaldo/podcast-transcript-forum

A modern podcast transcript forum with a tech-forward UI for browsing, searching, and reading podcast transcripts. Features category/host filtering, fuzzy search, multi-language transcript support, and responsive cross-device design.

0

stars

101

commits

JavaScript

primary language

Apr 26, 2026

updated

podcast.leoh.top/

README

PodScribe Logo

PodScribe

A modern podcast transcript forum for browsing, searching, and contributing podcast transcripts across multiple languages.

React 19 Vite 8 Express 5 SQLite FTS5 Tests License

Screenshots

Homepage

DarkLight
Homepage DarkHomepage Light

Episode View

Episode Page

Search

Features

  • Multi-language transcripts — View and switch between transcript languages; default display in the podcast's original language
  • Full-text search — Trigram-based search across podcast names, episodes, transcript content, and guest names
  • Reading mode — Distraction-free transcript reading with floating toggle
  • Embedded media player — Play episodes with synchronized transcript highlighting
  • Revision history — Git-like versioning for community-contributed transcript edits
  • Anonymous upload — Submit transcripts via web UI or Chrome extension
  • Speaker tags — Normalized speaker labels with visual differentiation
  • Light / dark theme — Toggleable with system-preference-aware defaults
  • Responsive design — Optimized for desktop and mobile
  • Category & host browsing — Sidebar navigation by podcast category

Tech Stack

LayerTech
FrontendReact 19, Vite 8, CSS Variables
BackendExpress 5, Node.js
DatabaseSQLite (better-sqlite3) with WAL mode, FTS5 trigram search
TestingJest, Supertest

Quick Start

# Install dependencies
npm install
cd client && npm install && cd ..

# Seed the database (optional)
npm run seed

# Development (two terminals)
npm run dev:server   # Express API on :4010
npm run dev:client   # Vite dev server on :5173

# Production build
npm run build
npm start            # Serves both API and built frontend on :4010

Project Structure

├── client/                 # React frontend
│   ├── src/
│   │   ├── components/     # Header, Sidebar, MediaPlayer, TranscriptEditor, ...
│   │   ├── pages/          # Home, Episode, Podcast, Search, Upload
│   │   └── styles/         # Global CSS, theme variables
│   └── vite.config.js
├── server/                 # Express backend
│   └── src/
│       ├── routes/         # podcasts, episodes, search, upload, revisions
│       ├── db.js           # SQLite setup + FTS5 trigram migration
│       └── index.js        # Express app
├── scripts/                # Utility scripts (seed, crawl, ASR, translation)
├── tests/                  # API test suite
└── data/                   # SQLite database (gitignored)

API

All endpoints are under /api.

MethodEndpointDescription
GET/api/healthHealth check
GET/api/podcastsList all podcasts
POST/api/podcastsCreate a podcast
GET/api/podcasts/:idGet podcast with episodes
GET/api/episodes/:idGet episode with transcripts
GET/api/search?q=...&type=...Full-text search
POST/api/uploadAnonymous upload (podcast + episode + transcript)
GET/api/episodes/:id/revisionsRevision history
POST/api/episodes/:id/revisions/:sha/restoreRestore a revision

Testing

npm test

Runs the full API test suite (~50 test cases) covering CRUD operations, search, multi-language transcripts, revisions, uploads, and cascade deletes.

Scripts

ScriptDescription
npm run seedSeed database with sample data
npm run crawlCrawl podcast sources for episode inventory and RSS transcripts; YouTube episodes are queued for ASR-first processing
node scripts/asr-zh.jsLocal ASR pipeline for Chinese podcasts
node scripts/translate-to-zh.jsTranslate English transcripts to Chinese from ASR/polished English sources
node scripts/batch-polish.jsLLM-powered transcript polishing
python3 scripts/batch_transcribe.py --podcast 'Planet Money' --limit=5Batch transcribe missing audio-only episodes with GPU-to-CPU OOM fallback
python3 scripts/batch_transcribe_youtube.py --podcast '不明白播客' --limit=5Batch transcribe missing YouTube episodes with ASR-first OOM-safe fallback
npm run audit:podcast -- --podcast-id=23Audit one podcast for duplicate translations, speaker-label drift, and source-language mismatches
node scripts/repair-podcast-23.js --episodes=...Repair Dwarkesh Podcast batches by recovering sources, canonicalizing speaker labels, and deduplicating translations
npm run audit:inline-speakersScan all polished transcripts for speaker tags that appear mid-paragraph instead of starting a new paragraph
npm run fix:inline-speakersSplit inline speaker tags into proper paragraph starts across the transcript database
node scripts/audit-english-transcripts.jsAudit all English podcasts for missing transcripts, asr-only episodes, malformed tags, and generic speaker labels
node scripts/fix-english-polish-format.js --podcast-id=25Normalize malformed English llm_polish formatting such as broken timestamp-speaker lines
node scripts/normalize-english-speakers.js --podcast-id=11Deterministically replace generic English speaker labels using host, title, and description hints

Codex Skills

This repo now includes repo-local Codex skills under .codex/skills for the two workflows that are most repetitive here:

  • podcast-transcript-pipeline for episode updates, ASR, repolish, postprocess, inline-speaker cleanup, and transcript QA.
  • podcast-forum-deploy for main-repo builds, newserver deployment, restart, and verification.
  • podcast-quality-repair for podcast-level audit and fast repair planning when speaker labels, transcript language, translation rows, or paragraph splits drift over time.

Transcript policy: for YouTube episodes, start from ASR as the default source of truth. Legacy youtube_auto and youtube_manual rows can exist in old data, but new repair and ingestion flows should replace them instead of building on them.

License

ISC

Contributors

Ericonaldo

101 commits

Languages

JavaScript

80.2%

CSS

13.0%

Python

6.0%