Indsæt streamen, ikke siden. En stations rigtige lyd ligger på en adresse, der ender på .mp3, .aac eller .m3u8, og når en live-transskription har den, behøver ingen at lytte med.
En redaktion, der overvåger tre radiostationer, to tv-streams og en håndfuld YouTube-livesendinger, har først et bemandingsproblem og dernæst et transskriptionsproblem. Nogen skal lytte, og nogen skal trykke på optag. En live-transskription med en stream som kilde overtager begge opgaver: giv den streamens adresse, så åbner serveren den, afkoder den og transskriberer den, mens den sendes, så ingen behøver at sidde ved et skrivebord, når en pressekonference begynder tidligt. Det svære, viser det sig, er at finde adressen.
En live-transskription kan hente sin lyd fra en stream, som serveren selv henter: en internetradiostation, en HLS-playlist (.m3u8), et DASH-manifest (.mpd) eller en YouTube-live. I Dashboardet er det en ny live-transskription på Transcriptions → New → Live, hvis kilde er en stream i stedet for mikrofonen. Adressen sættes, når transskriptionen oprettes, og ændrer sig aldrig, så en overvågning sættes op én gang og startes, når stationen er værd at optage.
Adressen skal være selve streamen, ikke den webside, der afspiller den. En stations side er HTML; lyden, den spiller, kommer fra et andet sted, typisk en Icecast- eller Shoutcast-server, og sidens afspiller finder adressen med JavaScript, efter siden er indlæst. Se efter et link på sitet mærket "Listen", "Direct stream" eller "Other players": det ender på .mp3, .aac, .m3u, .m3u8 eller .pls. En .pls eller .m3u er heller ikke lyd. Det er en kort tekstfil på nogle få linjer, hver med adressen på en stream; den findes, så desktopafspillere kan få et enkelt link, og VocalStack transskriberer den stream, den peger på. Hvis du i stedet indsætter siden, henter VocalStack den, ser HTML komme tilbage og fortæller dig det. Alle stationer skjuler deres stream lidt forskelligt; de fleste gemmer den bag en knap mærket "Listen", og det er det eneste på siden, der ikke er værd at klikke på.
To slags links afvises med det samme. Links til sider, der afspiller medier, såsom Spotify, SoundCloud eller Twitch, afvises, før der oprettes forbindelse. Kopibeskyttede streams understøttes heller ikke: en playlist eller et manifest, der kræver en DRM-licens, fejler med en besked, der siger det. Nogle streams afviser også anmodninger udefra stationens land, så en adresse, der virker på kontoret, kan være umulig at afspille fra en server i udlandet. Hvis en stream afspilles i en browser, men ikke i VocalStack, afviser stationen andre afspillere; bed stationen om en adresse, andre apps må bruge. De fleste tilbyder en MP3-, AAC- eller HLS-stream. Når du har den rigtige adresse, er næste spørgsmål, om den nogensinde slutter.
En live-stream slutter aldrig, så den er ikke en fil. Forskellen betyder især noget for YouTube. En YouTube-live, der sendes nu, hører hjemme i en live-transskription, og Dashboardet følger den, mens den sendes. En YouTube-live, der er planlagt, men ikke er startet, vises som Not live yet og kan optages, når den sendes. En færdig video er noget andet: den har en længde, så den transskriberes som en fil: indsæt linket på Transcriptions → New.
Playlisten selv siger, hvilken den er, og det er sådan, enhver afspiller skelner. En færdig HLS-playlist indeholder en #EXT-X-ENDLIST-tag eller #EXT-X-PLAYLIST-TYPE:VOD; en live-playlist har ingen af dem og vokser hele tiden, så afspilleren henter den igen hvert par sekund for nye segmenter. Et DASH-manifest erklærer type="dynamic", mens udsendelsen sendes, og type="static", når det er en optagelse. En MP3- eller AAC-stream fra en Icecast-server har slet ingen længde; den stopper simpelthen aldrig.
Fra kode afgør GET /v1/transcriptions/probe?url= dette, før noget gemmes. Svarets kind er live for en YouTube-live, der sendes nu eller er planlagt, en stream uden ende, en HLS-playlist uden slutning eller et dynamisk DASH-manifest. Det er file for alt med en ende, og unknown, når proben ikke kunne afgøre det. Når linket ikke åbnede som medie, er ok false, og problem er en sætning, du kan vise. Proben gemmer intet og er begrænset til 60 kald i minuttet.
Filtranskriptioner kører mange gange hurtigere, end lyden spilles, så en færdig times tv tager ikke en time. En live-stream transskriberes i realtid, men "realtid" kører allerede bagud i forhold til udsendelsen, og det er værd at vide, hvor meget. En Icecast-stream når en afspiller nogle få sekunder efter mikrofonen. HLS og DASH er opdelt i segmenter, og en afspiller starter ikke, før den har to eller tre segmenter på omkring seks sekunder hver, så en HLS-stream ligger typisk 10 til 30 sekunder bag udsendelsen, før transskriptionen tilføjer sin egen forsinkelse. En linje, der ser ud til at komme for sent, skyldes oftest stationens forsinkelse, ikke transskriberens. En stream giver én optagelse; en redaktion kører flere, og de samles alle det samme sted.
Hver live-transskription lander i Transcriptions-biblioteket ved siden af hver fil. Hver optagelse gemmes sammen med sin transskription, og når ordene er endelige, skrives en titel og et resumé for den på alle sprog. Det talte sprog registreres automatisk, og talere kan skifte sprog når som helst. Hvis du kender sproget, forbedrer det transskriptionens kvalitet at angive det.
Biblioteket kan søges på tværs af sprog: søg i alle transskriptioner, uanset sprog, og få resultaterne på dit eget sprog. En reporter, der kun læser engelsk, kan søge i den rumænske stations morgenudsendelse efter en ministers navn og læse træffene på engelsk, hver med den transskription, de stammer fra, og tidspunktet for det, der blev sagt. Ministerens navn vil være stavet på tre måder på tre stationer; søgning efter det på engelsk sparer dig for at læse alle tre.
En transskription kan oversættes til op til 10 sprog på én gang, så den samme optagelse kan læses på alle de sprog, redaktionen arbejder med. Et søgetræf er først nyttigt, hvis du kan citere det.
En transskription er ikke en mur af tekst. API'ets GET /v1/recordings/{id}/transcript returnerer sentences, segments med ordtider og speakers, så et citat kan placeres til sekundet i forhold til optagelsen. Dashboardet afspiller en live-optagelse præcis, som den kom ind, med lyden, så en sætning kan høres, før den citeres.
Før du stoler på en transskription af radiolyd, fra hvilket system som helst, er to ting værd at vide. Talegenkendelse snubler mest over egennavne, steder og tal, netop det en reporter søger efter. Et navn, genkenderen aldrig har set, kommer ud som det nærmeste almindelige ord, og en søgning efter den korrekte stavning misser det. Søg i stedet efter et karakteristisk ord, der er sagt i samme åndedrag, og læs derefter omkring træffet. Og radio er ikke kun tale: musikunderlæg, jingles og reklamepauser giver linjer, der ligner sætninger, men ikke er det. Tjek lyden, før du citerer.
Speaker labels har tre indstillinger: fra, til eller When Needed, som viser mærkater, når en anden taler høres. Du kan navngive en taler i en optagelse (PUT /v1/recordings/{id}/speakers/{idx}), og når en talers stemme er indrulleret, genkender fremtidige optagelser dem ved navn. For en station, hvis værter vender tilbage hver morgen, er "Speaker 2" ikke længere et mærke efter den første uge. Når citatet er tjekket, skal det ud af huset.
Hvert format på en transskriptions Export-fane har en opgave i en redaktion. SRT går til videoredigereren, så et klip fra streamen får undertekster; en oversat SRT har dem på et andet sprog. Excel går til researcheren med én række pr. sætning og dens tidspunkt, til filtrering og annotering. PDF går til redaktøren, der vil læse hele timen i toget. JSON går til den, der skriver redaktionens egne værktøjer. Via API'et er eksporten GET /v1/recordings/{id}/export?format=srt|json|xlsx|pdf, med lang= for en oversat version. Download audio giver optagelsen som en MP3 til selve pakken.
At overvåge en udsendelse til reportering er almindelig redaktionspraksis. At genudgive et klip af den er noget andet. Det reguleres af fair dealing eller fair use, dér hvor du befinder dig, og af stationens egne vilkår, så redaktionens sædvanlige regler for at citere en konkurrent gælder også konkurrentens lyd. Alt ovenfor er nogle få klik i Dashboardet; for en redaktion med tyve stationer er det bedre at scripte det.
Hele stien findes i API'et, inkluderet i alle abonnementer og faktureret til samme takst som Dashboardet. En live-transskription med en stream som kilde oprettes inaktiv, startes af sin første optagelse og styres over HTTP. Fordi serveren henter lyden, returnerer start af en optagelse ingen stream, som du skal sende til: der er intet på din side, der sender lyd.
API=$VOCALSTACK_API_ORIGIN # the origin shown in the API reference
AUTH="Authorization: Bearer $VOCALSTACK_API_KEY"
# A live transcription whose source the server pulls itself.
TX=$(jq -n '{name: "Morning show", source: {kind: "stream", url: "https://example.org/live.mp3"},
settings: {speakerLabels: "auto"}}' \
| curl -sSf "$API/v1/transcriptions/live" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" -d @- | jq -r .id)
# Start recording: the server opens the stream now.
REC=$(curl -sSf -X POST "$API/v1/transcriptions/$TX/recordings" -H "$AUTH" \
-H "Content-Type: application/json" -d '{}' | jq -r .recording.id)
# Later: pause, then stop (a live recording must be paused before it is stopped).
curl -sSf -X POST "$API/v1/recordings/$REC/pause" -H "$AUTH"
curl -sSf -X POST "$API/v1/recordings/$REC/stop" -H "$AUTH" -H "Content-Type: application/json" -d '{}'
# Search everything that was said, in English.
curl -sSfG "$API/v1/search" -H "$AUTH" --data-urlencode "q=budget" --data-urlencode "lang=en"
Idempotency-Key gør, at et gentaget kald til oprettelse returnerer den første transskription i stedet for en anden. TypeScript- og Python-SDK'erne pakker de samme kald og downloades fra Developers-området; API-referencen har hver rute og dens frames.
En AI-agent kan arbejde med biblioteket uden kode overhovedet. MCP-serveren på /v1/mcp giver agenten et sæt værktøjer; blandt dem er fire, der betyder noget her. search_transcriptions søger i det, der blev sagt, på tværs af kontoens transskriptioner på alle sprog og returnerer de matchende linjer, hver med den transskription, den ligger i, og tidspunktet for det, der blev sagt. get_summary returnerer en optagelses resumé. transcribe_url og wait_for_transcription transskriberer en færdig video eller en optagelses link og venter på resultatet. Start af en live-stream-overvågning forbliver i Dashboardet eller i API'et ovenfor. Opsætningen til Claude Code, Cursor eller enhver Streamable HTTP-klient findes under Developers → Connect an AI agent; MCP-artiklen gennemgår den. Hvad scriptet koster at køre, er det sidste spørgsmål.
Live-transskription koster $0.60 per time lyd, faktureret pr. sekund ($0.01 i minuttet), med et minimum på 5 minutter pr. transskription. Live koster mere end en fil, fordi den transskriberes, mens det sker, så længe den kører. Filer koster $0.20 per time ($0.0033 i minuttet; en fil på 30 minutter koster $0.10). Priserne er de samme på alle abonnementer og via API'et, og oversættelser, resuméer, navngivning af talere, udsendelse og deling tilføjer aldrig til regningen.
En station, der overvåges otte timer om dagen, fem dage om ugen, er 40 live-timer: $24 om ugen, uanset hvilke sprog redaktionen læser den på.
Grænserne, der betyder noget for overvågning, varierer efter abonnement. Hver er listet i rækkefølgen Pay as you go, Premium, Enterprise:
En ny konto starter med en tilmeldingskredit på $5 og intet kort, hvilket er mere end otte timers live-overvågning at prøve det på. Den fulde tabel står på prissiden.