Court Recording Transcriber Development Guide
An AI-powered application for transcribing court recordings with speaker identification, synchronized playback, search functionality, and professional legal document exports.
Live site: https://court-record-transcriber.casedev.app
Architecture
src/
├── app/
│ ├── api/recordings/ # API routes for recordings
│ │ ├── route.ts # List, create recordings
│ │ └── [id]/
│ │ ├── route.ts # Get, update, delete
│ │ ├── transcribe/ # Start transcription
│ │ └── export/ # Export endpoints
│ ├── upload/ # Upload page
│ └── recording/[id]/ # Transcript viewer page
├── components/
│ ├── ui/ # shadcn/ui components
│ ├── AudioPlayer.tsx # Waveform + playback
│ ├── TranscriptView.tsx # Transcript display
│ ├── SpeakerEditor.tsx # Label speakers
│ └── ExportDialog.tsx # Export options
└── lib/
├── db/
│ ├── index.ts # Database connection
│ └── schema.ts # Drizzle schema
├── casedev/ # Case.dev API client
└── legal-vocabulary.ts # Word boosting config
Core Workflow
Upload Audio → Transcribe → Identify Speakers → Review/Edit → Export
↓ ↓ ↓ ↓ ↓
MP3/WAV Case.dev API Auto-detect Sync playback PDF/Word/
M4A/etc with legal Judge, Atty, click-to-seek Plain text
vocabulary Witness, etc
Tech Stack
| Layer |
Technology |
| Frontend |
Next.js 16, React 19, Tailwind CSS |
| Backend |
Next.js API Routes |
| Database |
PostgreSQL + Drizzle ORM |
| Audio |
wavesurfer.js |
| Transcription |
Case.dev Speech-to-Text API |
| Export |
React PDF, docx library |
Key Features
| Feature |
Description |
| Audio Upload |
Drag-drop MP3, WAV, M4A, FLAC, OGG |
| AI Transcription |
Case.dev API with legal vocabulary boosting |
| Speaker ID |
Auto-detect speakers, customizable labels |
| Synced Playback |
Click transcript line to jump to timestamp |
| Search |
Find words/phrases with highlighting |
| Export |
PDF, Word (.docx), plain text with legal formatting |
Database Operations
PostgreSQL with Drizzle ORM. See references/database-schema.md.
Commands
npm run db:push # Push schema (dev)
npm run db:generate # Generate migrations
npm run db:studio # Open Drizzle Studio
Core Tables
- recordings: id, filename, duration, status, audioUrl
- transcripts: id, recordingId, content (JSON), speakerMap
- utterances: id, transcriptId, speaker, text, startTime, endTime
Case.dev Integration
See references/casedev-transcription-api.md for API patterns.
Transcription Flow
// 1. Upload audio to Case.dev
const { audioId } = await uploadAudio(file);
// 2. Start transcription with legal vocabulary
const { jobId } = await startTranscription(audioId, {
vocabulary: legalVocabulary,
speakerDiarization: true,
});
// 3. Poll for completion
const transcript = await pollTranscriptionStatus(jobId);
// 4. Store results
await saveTranscript(recordingId, transcript);
Audio Playback
See references/audio-playback.md for wavesurfer.js patterns.
Key Features
- Waveform visualization
- Click-to-seek from transcript
- Playback speed control
- Keyboard shortcuts (space, arrows)
Development
Setup
npm install
cp .env.example .env.local
# Add CASEDEV_API_KEY and DATABASE_URL
npm run db:push
npm run dev
Environment
CASEDEV_API_KEY=sk_case_... # Case.dev API key
DATABASE_URL=postgresql://... # PostgreSQL connection
NEXT_PUBLIC_APP_URL=http://localhost:3000
Common Tasks
Adding a New Export Format
- Create export function in
lib/export/
- Add endpoint in
app/api/recordings/[id]/export/
- Add option to
ExportDialog.tsx
Customizing Speaker Labels
// Default labels
const speakerLabels = ['Judge', 'Plaintiff Attorney', 'Defense Attorney',
'Witness', 'Clerk', 'Unknown'];
// In SpeakerEditor component, allow custom labels
Adding Legal Vocabulary
// lib/legal-vocabulary.ts
export const legalVocabulary = [
'objection', 'sustained', 'overruled', 'plaintiff', 'defendant',
'voir dire', 'habeas corpus', 'pro bono', 'amicus curiae',
// Add more terms
];
Export Formats
| Format |
Use Case |
| PDF |
Official court filing, archive |
| Word (.docx) |
Editing, annotations |
| Plain Text |
Processing, search indexing |
| SRT |
Subtitles for video recordings |
Troubleshooting
| Issue |
Solution |
| Transcription stuck |
Check Case.dev API status, verify audio format |
| Audio won't play |
Verify audio URL accessible, check CORS |
| Speaker labels wrong |
Use SpeakerEditor to reassign |
| Export fails |
Check transcript exists, verify format support |
| Waveform not showing |
Ensure wavesurfer.js loaded, check audio src |
1---2name: court-record-transcriber3description: Development skill for CaseMark's Court Recording Transcriber - an AI-powered application for transcribing court recordings with speaker identification, synchronized playback, search, and legal document exports. Built with Next.js 16, PostgreSQL, Drizzle ORM, wavesurfer.js, and Case.dev APIs. Use this skill when: (1) Working on or extending the court-record-transcriber codebase, (2) Integrating with Case.dev transcription APIs, (3) Working with audio playback/waveforms, (4) Building transcript export features, or (5) Adding speaker identification logic.4---5
6# Court Recording Transcriber Development Guide
7
8An AI-powered application for transcribing court recordings with speaker identification, synchronized playback, search functionality, and professional legal document exports.
9
10**Live site**: https://court-record-transcriber.casedev.app
11
12## Architecture
13
14```
15src/
16├── app/
17│ ├── api/recordings/ # API routes for recordings
18│ │ ├── route.ts # List, create recordings
19│ │ └── [id]/
20│ │ ├── route.ts # Get, update, delete
21│ │ ├── transcribe/ # Start transcription
22│ │ └── export/ # Export endpoints
23│ ├── upload/ # Upload page
24│ └── recording/[id]/ # Transcript viewer page
25├── components/
26│ ├── ui/ # shadcn/ui components
27│ ├── AudioPlayer.tsx # Waveform + playback
28│ ├── TranscriptView.tsx # Transcript display
29│ ├── SpeakerEditor.tsx # Label speakers
30│ └── ExportDialog.tsx # Export options
31└── lib/
32 ├── db/
33 │ ├── index.ts # Database connection
34 │ └── schema.ts # Drizzle schema
35 ├── casedev/ # Case.dev API client
36 └── legal-vocabulary.ts # Word boosting config
37```
38
39## Core Workflow
40
41```
42Upload Audio → Transcribe → Identify Speakers → Review/Edit → Export
43 ↓ ↓ ↓ ↓ ↓
44 MP3/WAV Case.dev API Auto-detect Sync playback PDF/Word/
45 M4A/etc with legal Judge, Atty, click-to-seek Plain text
46 vocabulary Witness, etc
47```
48
49## Tech Stack
50
51| Layer | Technology |
52|-------|-----------|
53| Frontend | Next.js 16, React 19, Tailwind CSS |
54| Backend | Next.js API Routes |
55| Database | PostgreSQL + Drizzle ORM |
56| Audio | wavesurfer.js |
57| Transcription | Case.dev Speech-to-Text API |
58| Export | React PDF, docx library |
59
60## Key Features
61
62| Feature | Description |
63|---------|-------------|
64| Audio Upload | Drag-drop MP3, WAV, M4A, FLAC, OGG |
65| AI Transcription | Case.dev API with legal vocabulary boosting |
66| Speaker ID | Auto-detect speakers, customizable labels |
67| Synced Playback | Click transcript line to jump to timestamp |
68| Search | Find words/phrases with highlighting |
69| Export | PDF, Word (.docx), plain text with legal formatting |
70
71## Database Operations
72
73PostgreSQL with Drizzle ORM. See [references/database-schema.md](references/database-schema.md).
74
75### Commands
76```bash
77npm run db:push # Push schema (dev)
78npm run db:generate # Generate migrations
79npm run db:studio # Open Drizzle Studio
80```
81
82### Core Tables
83- **recordings**: id, filename, duration, status, audioUrl
84- **transcripts**: id, recordingId, content (JSON), speakerMap
85- **utterances**: id, transcriptId, speaker, text, startTime, endTime
86
87## Case.dev Integration
88
89See [references/casedev-transcription-api.md](references/casedev-transcription-api.md) for API patterns.
90
91### Transcription Flow
92```typescript
93// 1. Upload audio to Case.dev
94const { audioId } = await uploadAudio(file);
95
96// 2. Start transcription with legal vocabulary
97const { jobId } = await startTranscription(audioId, {
98 vocabulary: legalVocabulary,
99 speakerDiarization: true,
100});
101
102// 3. Poll for completion
103const transcript = await pollTranscriptionStatus(jobId);
104
105// 4. Store results
106await saveTranscript(recordingId, transcript);
107```
108
109## Audio Playback
110
111See [references/audio-playback.md](references/audio-playback.md) for wavesurfer.js patterns.
112
113### Key Features
114- Waveform visualization
115- Click-to-seek from transcript
116- Playback speed control
117- Keyboard shortcuts (space, arrows)
118
119## Development
120
121### Setup
122```bash
123npm install
124cp .env.example .env.local
125# Add CASEDEV_API_KEY and DATABASE_URL
126npm run db:push
127npm run dev
128```
129
130### Environment
131```
132CASEDEV_API_KEY=sk_case_... # Case.dev API key
133DATABASE_URL=postgresql://... # PostgreSQL connection
134NEXT_PUBLIC_APP_URL=http://localhost:3000
135```
136
137## Common Tasks
138
139### Adding a New Export Format
1401. Create export function in `lib/export/`
1412. Add endpoint in `app/api/recordings/[id]/export/`
1423. Add option to `ExportDialog.tsx`
143
144### Customizing Speaker Labels
145```typescript
146// Default labels
147const speakerLabels = ['Judge', 'Plaintiff Attorney', 'Defense Attorney',
148 'Witness', 'Clerk', 'Unknown'];
149
150// In SpeakerEditor component, allow custom labels
151```
152
153### Adding Legal Vocabulary
154```typescript
155// lib/legal-vocabulary.ts
156export const legalVocabulary = [
157 'objection', 'sustained', 'overruled', 'plaintiff', 'defendant',
158 'voir dire', 'habeas corpus', 'pro bono', 'amicus curiae',
159 // Add more terms
160];
161```
162
163## Export Formats
164
165| Format | Use Case |
166|--------|----------|
167| PDF | Official court filing, archive |
168| Word (.docx) | Editing, annotations |
169| Plain Text | Processing, search indexing |
170| SRT | Subtitles for video recordings |
171
172## Troubleshooting
173
174| Issue | Solution |
175|-------|----------|
176| Transcription stuck | Check Case.dev API status, verify audio format |
177| Audio won't play | Verify audio URL accessible, check CORS |
178| Speaker labels wrong | Use SpeakerEditor to reassign |
179| Export fails | Check transcript exists, verify format support |
180| Waveform not showing | Ensure wavesurfer.js loaded, check audio src |