Wklej strumień, nie stronę. Prawdziwy dźwięk stacji znajduje się pod adresem kończącym się na .mp3, .aac lub .m3u8, a gdy transkrypcja na żywo go ma, nikt nie musi słuchać.
Redakcja, która monitoruje trzy stacje radiowe, dwa strumienie telewizyjne i kilka transmisji na żywo z YouTube, ma najpierw problem kadrowy, a dopiero potem problem z transkrypcją. Ktoś musi słuchać i ktoś musi nacisnąć nagrywanie. Transkrypcja na żywo ze strumieniem jako źródłem przejmuje obie te role: podajesz adres strumienia, serwer go otwiera, dekoduje i transkrybuje na bieżąco, w miarę emisji, więc nikt nie musi siedzieć przy biurku, gdy konferencja prasowa zacznie się wcześniej. Trudna część, jak się okazuje, to znalezienie tego adresu.
Transkrypcja na żywo może pobierać dźwięk ze strumienia, który serwer pobiera sam: z radia internetowego, z playlisty HLS (.m3u8), z manifestu DASH (.mpd) lub z transmisji na żywo z YouTube. W panelu robi się to tak: Transcriptions → New → Live tworzy nową transkrypcję na żywo, której źródłem jest strumień, a nie mikrofon. Adres ustawia się przy tworzeniu transkrypcji i nigdy się nie zmienia, więc monitor konfiguruje się raz, a uruchamia za każdym razem, gdy stacja jest warta nagrania.
Adres musi wskazywać sam strumień, a nie stronę, która go odtwarza. Strona stacji to HTML; dźwięk pochodzi zwykle z innego miejsca, najczęściej z serwera Icecast lub Shoutcast, a odtwarzacz na stronie znajduje ten adres za pomocą JavaScriptu już po załadowaniu strony. Szukaj na stronie linku w stylu „Listen”, „Direct stream” albo „Other players”: kończy się na .mp3, .aac, .m3u, .m3u8 lub .pls. Plik .pls ani .m3u nie jest dźwiękiem. To krótki plik tekstowy, kilka linijek, z których każda podaje adres strumienia; istnieje po to, by programy odtwarzające na komputerze mogły dostać jeden link, a VocalStack transkrybuje strumień, który w nim wskazano. Jeśli wkleisz zamiast tego stronę, VocalStack pobierze ją, zobaczy w odpowiedzi HTML i poinformuje cię o tym. Każda stacja ukrywa strumień nieco inaczej; większość chowa go za przyciskiem z napisem „Listen”, który jest jedyną rzeczą na stronie niewartą kliknięcia.
Dwa rodzaje linków są odrzucane od razu. Linki do stron odtwarzających media, takich jak Spotify, SoundCloud czy Twitch, zostają odrzucone, zanim cokolwiek się połączy. Strumienie chronione kopiowaniem także nie są obsługiwane: playlista lub manifest wymagający licencji DRM kończy się komunikatem, który to mówi. Niektóre strumienie odrzucają też żądania spoza kraju stacji, więc adres, który działa w biurze, może nie działać z serwera za granicą. Jeśli strumień odtwarza się w przeglądarce, ale nie w VocalStack, stacja odrzuca inne odtwarzacze; poproś ją o adres, z którego mogą korzystać inne aplikacje. Większość stacji oferuje strumień MP3, AAC lub HLS. Gdy masz już właściwy adres, pojawia się kolejne pytanie: czy kiedykolwiek się on kończy.
Strumień na żywo nigdy się nie kończy, więc nie jest plikiem. Rozróżnienie ma znaczenie zwłaszcza w przypadku YouTube. Transmisja na żywo z YouTube, która jest w eterze, należy do transkrypcji na żywo, a panel śledzi ją na bieżąco. Transmisja zaplanowana, która jeszcze się nie zaczęła, jest pokazywana jako Not live yet i można ją nagrać, gdy wejdzie na antenę. Zakończone wideo to co innego: ma długość, więc transkrybuje się je jako plik. Wklej link w Transcriptions → New.
Sama playlista mówi, czym jest, i tak odróżnia ją każdy odtwarzacz. Zakończona playlista HLS zawiera tag #EXT-X-ENDLIST albo #EXT-X-PLAYLIST-TYPE:VOD; playlista na żywo nie ma żadnego z nich i stale rośnie, więc odtwarzacz co kilka sekund pobiera ją ponownie w poszukiwaniu nowych segmentów. Manifest DASH deklaruje type="dynamic", gdy transmisja jest w eterze, i type="static", gdy jest już nagraniem. Strumień MP3 lub AAC z serwera Icecast nie ma żadnej długości; po prostu nigdy się nie kończy.
Z kodu GET /v1/transcriptions/probe?url= rozstrzyga to, zanim cokolwiek zostanie zapisane. Odpowiedź ma pole kind: live dla transmisji z YouTube, która jest w eterze lub zaplanowana, dla strumienia HTTP bez końca, dla playlisty HLS bez zakończenia albo dynamicznego manifestu DASH. Ma wartość file dla wszystkiego, co ma koniec, a unknown, gdy probe nie mógł tego ustalić. Gdy link nie otworzył się jako media, ok ma wartość false, a problem to zdanie, które można pokazać. Probe niczego nie zachowuje i jest ograniczony do 60 wywołań na minutę.
Transkrypcje plików przebiegają wielokrotnie szybciej niż odtwarzany dźwięk, więc godzina zakończonego programu telewizyjnego nie zajmuje godziny. Strumień na żywo transkrybuje się w czasie rzeczywistym, ale „czas rzeczywisty” i tak jest opóźniony względem emisji, i warto wiedzieć, o ile. Strumień Icecast dociera do odtwarzacza kilka sekund po mikrofonie. HLS i DASH są segmentowane, a odtwarzacz nie ruszy, dopóki nie zgromadzi dwóch–trzech segmentów po około sześć sekund każdy, więc strumień HLS jest zwykle o 10 do 30 sekund za emisją, zanim transkrypcja doda własne opóźnienie. Linia, która wydaje się przychodzić późno, zwykle jest opóźnieniem stacji, a nie transkrybera. Jeden strumień daje jedno nagranie; redakcja uruchamia ich kilka naraz, a wszystkie trafiają w to samo miejsce.
Każda transkrypcja na żywo trafia do biblioteki Transcriptions obok każdego pliku. Każde nagranie jest przechowywane razem ze swoją transkrypcją, a gdy słowa są ostateczne, dla nagrania powstają tytuł i podsumowanie we wszystkich językach. Język mowy wykrywany jest automatycznie, a mówcy mogą zmieniać język w dowolnej chwili. Gdy znasz język, jego podanie poprawia jakość transkrypcji.
Biblioteka przeszukuje się ponad językami: szukaj we wszystkich transkrypcjach, niezależnie od języka, a wyniki otrzymuj w swoim. Reporter, który czyta tylko po angielsku, może przeszukać poranny program rumuńskiej stacji pod kątem nazwiska ministra i przeczytać trafienia po angielsku, każde z transkrypcją, z której pochodzi, i czasem, w którym padło. Nazwisko ministra na trzech stacjach będzie zapisane na trzy sposoby; wyszukiwanie go po angielsku oszczędza czytania wszystkich trzech wersji.
Transkrypcję można przetłumaczyć nawet na 10 języków jednocześnie, więc to samo nagranie czyta się we wszystkich językach, w których pracuje redakcja. Trafienie wyszukiwania jest użyteczne tylko wtedy, gdy można je zacytować.
Transkrypcja to nie ściana tekstu. GET /v1/recordings/{id}/transcript w API zwraca sentences, segments z czasami słów oraz speakers, więc cytat można umieścić co do sekundy w nagraniu. Panel odtwarza nagranie na żywo dokładnie tak, jak wpłynęło, razem z dźwiękiem, więc zdanie można odsłuchać, zanim je zacytujesz.
Zanim zaufasz jakiejkolwiek transkrypcji audycji radiowej, z dowolnego systemu, warto wiedzieć o dwóch rzeczach. Rozpoznawanie mowy najbardziej się potyka na nazwach własnych, miejscach i liczbach, czyli na dokładnie tym, czego szuka reporter. Nazwa, której rozpoznawacz nigdy wcześniej nie widział, trafia jako najbliższe pospolite słowo, a wyszukiwanie poprawnej pisowni jej nie znajdzie. Zamiast tego szukaj wyróżniającego się słowa wypowiedzianego w tym samym tchu, a potem przeczytaj fragment wokół trafienia. Radio to też nie tylko mowa: podkłady muzyczne, dżingle i bloki reklamowe dają linie, które wyglądają jak zdania, ale nimi nie są. Sprawdź dźwięk, zanim zacytujesz.
Etykiety mówców mają trzy ustawienia: wyłączone, włączone albo When Needed, które pokazuje etykiety, gdy usłyszany zostanie drugi mówca. Mówcę można nazwać w nagraniu (PUT /v1/recordings/{id}/speakers/{idx}), a gdy głos mówcy zostanie zarejestrowany, przyszłe nagrania rozpoznają go po imieniu. W przypadku stacji, której prezenterzy wracają każdego ranka, etykiety przestają brzmieć „Speaker 2” już po pierwszym tygodniu. Gdy cytat zostanie sprawdzony, musi opuścić redakcję.
Każdy format na karcie Export transkrypcji ma w redakcji swoje zadanie. SRT trafia do montażysty wideo, więc klip ze strumienia ma napisy; przetłumaczony SRT ma je w innym języku. Excel trafia do badacza, jeden wiersz na zdanie z jego czasem, do filtrowania i adnotowania. PDF trafia do redaktora, który chce przeczytać całą godzinę w pociągu. JSON trafia do tego, kto pisze własne narzędzia redakcji. Przez API eksport to GET /v1/recordings/{id}/export?format=srt|json|xlsx|pdf, z lang= dla wersji przetłumaczonej. Download audio daje nagranie jako MP3 do samego materiału.
Monitorowanie audycji do celów relacjonowania to normalna praktyka redakcyjna. Ponowne publikowanie fragmentu to coś innego. Regulują to dozwolony użytek (fair dealing lub fair use) w miejscu, w którym jesteś, oraz warunki samej stacji, więc zwyczajne redakcyjne zasady cytowania konkurencji dotyczą także dźwięku konkurencji. Wszystko powyżej to kilka kliknięć w panelu; dla redakcji z dwudziestoma stacjami lepiej to zautomatyzować skryptem.
Cała ścieżka jest dostępna w API, włączona w każdym planie i rozliczana po tej samej stawce co panel. Transkrypcja na żywo ze źródłem strumieniowym tworzy się w stanie bezczynnym, uruchamia ją dopiero pierwsze nagranie i steruje się nią przez HTTP. Ponieważ serwer sam pobiera dźwięk, rozpoczęcie nagrywania nie zwraca strumienia, do którego masz publikować: po twojej stronie nic nie wysyła dźwięku.
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"
Klucz Idempotency-Key sprawia, że ponowione utworzenie zwraca pierwszą transkrypcję zamiast drugiej. Zestawy SDK w TypeScript i Pythonie opakowują te same wywołania i są pobierane z obszaru dla deweloperów; dokumentacja API zawiera każdą trasę i jej ramki.
Agent AI może obsługiwać bibliotekę bez żadnego kodu. Serwer MCP pod adresem /v1/mcp daje agentowi zestaw narzędzi; cztery z nich mają tu znaczenie. search_transcriptions przeszukuje to, co zostało powiedziane, we wszystkich transkrypcjach konta, w dowolnym języku, i zwraca pasujące linie, każdą z transkrypcją, w której się znajduje, i czasem, w którym padła. get_summary zwraca podsumowanie nagrania. transcribe_url i wait_for_transcription transkrybują zakończone wideo lub link do nagrania i czekają na wynik. Uruchamianie monitora strumienia na żywo pozostaje w panelu lub w API opisanym powyżej. Konfiguracja dla Claude Code, Cursor czy dowolnego klienta Streamable HTTP znajduje się w Developers → Connect an AI agent; artykuł o MCP prowadzi przez nią krok po kroku. Ostatnie pytanie dotyczy kosztu takiego skryptu.
Transkrypcja na żywo kosztuje $0.60 za godzinę dźwięku, rozliczana co sekundę ($0.01 za minutę), z minimum 5 minut na transkrypcję. Na żywo kosztuje więcej niż plik, bo jest transkrybowana w miarę trwania wydarzenia, tak długo, jak trwa. Pliki kosztują $0.20 za godzinę ($0.0033 za minutę; 30-minutowy plik kosztuje $0.10). Stawki są takie same w każdym planie i w API, a tłumaczenia, podsumowania, nazywanie mówców, transmitowanie i udostępnianie nigdy nie zwiększają rachunku.
Stacja monitorowana osiem godzin dziennie, pięć dni w tygodniu, to 40 godzin na żywo: $24 tygodniowo, bez względu na to, w ilu językach redakcja ją czyta.
Limity istotne przy monitorowaniu zależą od planu. Każdy podany jest w kolejności Pay as you go, Premium, Enterprise:
Nowe konto zaczyna się od $5 środków startowych bez karty, co wystarcza na ponad osiem godzin monitorowania na żywo, by to wypróbować. Pełna tabela jest na stronie cennika.