Who said what in thousands of hours of public meetings
Transcripts of city councils, school boards and courts that met on Zoom and posted the recordings to YouTube. Every turn is attributed to a named person, read from the name Zoom prints on the active speaker's tile, and linked to the same person across all of a body's meetings.
How the transcripts were made
Audio alone can tell when a different person starts talking, but not who they are. In Zoom's gallery view, the active speaker's tile is outlined in green and labelled with their name. The pipeline reads that name once a second and aligns it with a Whisper transcript. It runs as four scripts, all in the pipeline repository.
Read the name tile
Find the highlighted tile in each sampled frame and read its name label with PaddleOCR.
video → raw names, 1 per secondTranscribe the audio
Whisper large-v2 for the words, pyannote to group the speech into voices.
video → text + voice clustersTurn names into people
Strip titles, roles and OCR damage; link spellings of one person across every meeting of a body.
raw names → linked identitiesAttribute every turn
Name each stretch of speech from its voice and the tile on screen at that moment.
text + identities → transcriptOrder: ocr.py and transcribe.py run independently on each video. clean_ocr.py runs once over a whole collection, because it links people across meetings. merge.py runs last. Method and measurements: METHOD.md.
What a transcript contains
One JSON file per meeting: a list of turns. Each turn keeps the cleaned, linked speaker and the name exactly as it was read off the screen, so the cleaning can be checked or redone.
// data/hidalgo_ccl_9/-OPax3iQV50.json [ { "start": 3375.36, "end": 3384.72, "speaker": "patriciaocanaolivarez", "speaker_name": "Judge Patricia O'Caña-Olivarez", "ocr_label": "Judge Patricia O'Ca..", "text": "Okay. So let's come back on April the 5th at 930. ..." }, ... ]
| Field | Meaning |
|---|---|
| start, end | Seconds from the start of the YouTube video. |
| speaker | The linked identity: one key per person across all of a collection's meetings. Other means no name, or a room, device or organisation tile. |
| speaker_name | That person's display name. |
| ocr_label | The name as OCR read it during the turn, before any cleaning. |
| text | What was said (Whisper large-v2). |
Get the data
Everything the site shows is plain JSON in the site repository. Clone it, or fetch single files from this site.
# everything # download the repository from https://anonymous.4open.science/r/zoom-meeting-transcripts-E8E1 # layout data/collections.json # every collection, with statistics data/<collection>/meetings.json # its meetings: id, date, title, length data/<collection>/<video id>.json # one transcript data/<collection>/registry.json # who is who, and how names were linked
import json, pathlib root = pathlib.Path("zoom-meeting-transcripts/data") for c in json.loads((root / "collections.json").read_text()): for m in json.loads((root / c["slug"] / "meetings.json").read_text()): turns = json.loads((root / c["slug"] / f"{m['id']}.json").read_text()) # the video: https://www.youtube.com/watch?v={m['id']}
registry.json lists every person in a collection with each spelling that was linked to them, the rule that linked it, and every link the pipeline refused. Three older collections (Waipa District Council, DC Court of Appeals, Albemarle County School Board) predate the raw-OCR record. They keep their original speaker labels, with speaker_name and ocr_label empty.
Collections
Each collection is one public body's meetings, from the period when it met on Zoom in gallery view.
| Collection | Type | Region | Meetings | Hours | Dates |
|---|---|---|---|---|---|
| Loading… | |||||