Skip to content
intermediateaudio

Transcript Format Ingestion

Six transcript formats rendered as the same speaker bubbles, with per-turn labels and spans that work regardless of where the transcript came from.

Get This Design

This design is available in our showcase. Copy the configuration below to get started.

Quick start:

bash
# Create your project folder
mkdir transcript-formats
cd transcript-formats
# Save config.yaml from this page into this folder
potato start config.yaml
Transcript Format Ingestion annotation interface preview

Configuration File

yaml
annotation_task_name: "Transcript Formats"
task_dir: "."
port: 8000

# Six instances, six different transcript formats. All of them render as the
# same speaker bubbles, because Potato normalizes every supported format to one
# turn model before display.
#
# Here the transcripts are inlined into the data file. They can equally be paths
# to sidecar files next to the media, which is usually what your ASR tool
# already produced:
#
#     "conversation": {"audio": "media/talk_01.mp3",
#                      "transcript": "media/talk_01.srt"}
#
# To build a data file from a folder of ASR output instead:
#
#     potato transcripts ./whisper_out --media-dir ./audio -o data/items.json
#     potato transcripts ./whisper_out --dry-run     # what did it detect?

data_files:
  - data/sample-data.json

item_properties:
  id_key: id
  text_key: title

instance_display:
  layout:
    direction: vertical
    gap: 16px
  fields:
    - key: title
      type: text
      label: "Format"

    - key: source_format
      type: text
      label: "Where this came from"

    - key: conversation
      type: audio_dialogue
      label: "Transcript"
      span_target: true
      display_options:
        audio_key: audio
        scroll_height: 420px
        show_timestamps: true
        # "auto" reads the field as a sidecar path when it looks like one.
        # These transcripts are inlined, so nothing is read from disk.
        transcript_is_path: auto
        # No fixed roster: these files use different speaker conventions
        # (Host/Guest, Interviewer/Participant, speaker_0/speaker_1, tier
        # names), so speakers get deterministic colors and alternating sides.
        allow_speaker_assignment: auto

annotation_schemes:
  # Whether the transcript survived ingestion intact, which is the judgment you
  # actually want a human to make before trusting a corpus.
  - annotation_type: radio
    name: ingestion_quality
    description: "Did this transcript come through correctly?"
    labels:
      - name: "clean"
        tooltip: "Timings, speakers, and text all look right"
      - name: "timing_off"
        tooltip: "Turns are misaligned with the audio"
      - name: "speakers_wrong"
        tooltip: "Turns are attributed to the wrong speaker, or not attributed"
      - name: "text_garbled"
        tooltip: "Text is missing, truncated, or mangled"

  # Per-turn labeling, bound to every turn of the conversation field.
  - annotation_type: radio
    name: turn_type
    description: "What is this turn doing?"
    labels:
      - question
      - answer
      - aside
    turn_level: true
    turn_binding:
      field: conversation

  # Spans work over normalized turns regardless of the source format.
  - annotation_type: span
    name: highlights
    description: "Highlight anything an annotator would need to check against the audio"
    target_field: conversation
    labels:
      - name: unclear
        key_value: "u"
      - name: likely_asr_error
        key_value: "e"

output_annotation_dir: "annotation_output/"
export_annotation_format: "jsonl"

Sample Data

json
[
  {
    "id": "talk_01",
    "title": "SubRip (.srt)",
    "source_format": "Exported from a captioning tool. Speakers come from the \"Host:\" / \"Guest:\" prefix on each cue.",
    "conversation": {
      "audio": "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
      "transcript": "1\n00:00:00,000 --> 00:00:06,500\nHost: Welcome back. Today we're talking about what happens to a transcript before anyone annotates it.\n\n2\n00:00:06,500 --> 00:00:12,000\nHost: You've argued that most of the errors people blame on annotators actually start upstream.\n\n3\n00:00:12,000 --> 00:00:21,400\nGuest: Right. If the diarization is wrong, every downstream label inherits that mistake, and nobody notices until analysis.\n\n4\n00:00:21,400 --> 00:00:28,900\nHost: So what would you check first, before recruiting a single annotator?\n"
    }
  },
  {
    "id": "talk_02",
    "title": "WebVTT (.vtt)",
    "source_format": "WebVTT. Speakers come from the <v Name> voice tags, the format's own way of marking who is talking.",
    "conversation": {
      "audio": "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3",
      "transcript": "WEBVTT\n\nNOTE\nExported from a captioning tool. Voice tags carry the speaker.\n\n1\n00:00:00.000 --> 00:00:07.200\n<v Interviewer>Can you describe what a normal week looked like before the change?\n\n2\n00:00:07.200 --> 00:00:16.800\n<v Participant>It was mostly reactive. You'd come in, see what broke overnight, and spend the day on that.\n\n3\n00:00:16.800 --> 00:00:24.000\n<v Interviewer>And afterwards?\n\n4\n00:00:24.000 --> 00:00:33.500\n<v Participant>Afterwards there was a plan. Not a good one at first, but the difference was having one at all.\n"
    }
  }
]

// ... and 4 more items

Details

Annotation Types

radiospan

Domain

AudioSpeech

Use Cases

transcript annotationASR ingestionspeaker diarizationsubtitle annotation

Tags

audiotranscriptwhispersubtitlessrtwebvttdeepgramtextgrid