Upphleðsla skilar verki, ekki uppskrift. Settu Idempotency-Key á hverja beiðni um nýtt verk svo endurtekin beiðni geti aldrei stofnað eða rukkað aðra umritun; kannaðu síðan verkið þar til það er tilbúið og lestu setningar, orð eða útflutning af sama auðkenni.
Skráarumritun er einfaldasta verkið sem VocalStack API gerir, og hún hefur lögun hvers ósamstillts verks: hlaða upp, bíða, lesa. Upphleðslan svarar strax með umritun og verki hennar, en engum orðum enn. Á meðan forritið þitt kannar verkið umritar þjónninn skrána margfalt hraðar en hljóðið spilast. Þegar staðan er done lesurðu uppskriftina sem setningar með mælendum og tímasetningum, eða flytur hana út sem skrá á einu af fjórum sniðum, annaðhvort á talaða tungumálinu eða þýddu.
Allt í þessari lögun flyst yfir í hvaða hópvinnslu-API sem er: stofnun sem skilar auðkenni, staða sem þú kannar, og niðurstaða sem þú sækir á eftir. Það eina sem er sérstakt fyrir VocalStack eru reitanöfnin, og í þessari kennslu förum við yfir þau með TypeScript- og Python-SDK og HTTP-köllunum þar sem þau hjálpa.
Opnaðu Developers → API keys í stjórnborðinu og búðu til lykil með heimildunum Read og Write. Lykillinn birtist aðeins einu sinni; afritaðu hann í umhverfisbreytu:
export VOCALSTACK_API_KEY="<your API key>"
Lykill er fyrir netþjóna og skriptur. Allt sem fer í vafra fer með lyklinum, svo hafðu hann utan síðukóðans og utan vefslóða, sem enda í skráningum. Til að streyma úr vafra býr netþjónninn þinn til rauntímasetu fyrir hann; það er önnur kennsla.
Hvert kall auðkennist með lyklinum sem bearer-tókeni:
Authorization: Bearer vs_key_...
Þegar lykillinn er kominn í umhverfið þitt er næst að búa til biðlara sem sendir hann.
Það eru tvö SDK. Hvert þeirra hefur einn biðlara, VocalStack, með aðferðum flokkuðum eftir auðlind: vs.transcriptions, vs.jobs, vs.recordings, vs.realtime og nokkur til. TypeScript-SDK keyrir á Node 20 eða nýrra og í vöfrum; Python-SDK byggist á asyncio og þarf Python 3.10 eða nýrra.
SDK-in eru hvorki á npm né PyPI. API-ið þjónar smíðunum til hvers reiknings. Uppsetningarskipunum, útfylltum fyrir þitt umhverfi, er lýst í uppsetningarkaflanum í skyndileiðbeiningunum í API-handbókinni, og þær senda lykilinn þinn aðeins á API-hýsilinn. Ef þú vilt ekki setja neitt upp duga HTTP-köllin hér að neðan með curl og jq.
Hvort sem þú valdir, er fyrsta kallið upphleðslan.
POST /v1/transcriptions tekur skrána sem multipart/form-data: file-hluta, og valfrjálsan options-hluta með JSON. Það svarar með 201 og umrituninni. job.recordingId er auðkenni upptökunnar sem varð til úr skránni, og öll köll sem fylgja beinast að því auðkenni.
Tvær stillingar skipta máli í fyrstu keyrslu, báðar innan options.settings:
spokenLanguage: kóði eins og en. Ef hann er ekki gefinn er tungumálið greint sjálfkrafa. Ef þú nefnir það bætir það nákvæmnina í hávaðasömum upptökum og við mikinn hreim.speakerLabels: auto, on eða off. Með on fylgir hverri setningu mælandanúmer; með auto birtast merkingarnar aðeins þegar annar mælandi hefur heyrst.Flestar hljóð- og myndskrár eru samþykktar: mp3, wav, flac, ogg, m4a, mp4 og fleiri. Í myndskeiði er aðeins hljóðið geymt. Sniðið skiptir litlu máli fyrir nákvæmnina, því talgreiningin vinnur á 16 kHz mónó og öllu er endurúrtakað í það við móttöku. Stereó-WAV með 48 kHz af einum manni sem talar fer mestum hluta bæta sinna í að lýsa þögn í hárri upplausn. Hún umritast jafn vel og 64 kbps mónó-MP3 eða Opus-skrá af sama erindi, en hún er um tuttugu sinnum stærri og tekur því tuttugu sinnum lengri tíma í upphleðslu. Ef þú umbreytir áður en þú hleður upp eru mónó og hóflegur bitahraði þær tvær stillingar sem vert er að breyta.
Sendu Idempotency-Key með hverri upphleðslu, og skildu af hverju. Tímamörk í netkerfinu segja þér ekkert um hvað þjónninn gerði: hann gæti aldrei hafa séð beiðnina, eða hann gæti hafa vistað skrána og sett verkið í biðröð áður en svarið týndist. Blind endurtekning gæti því stofnað aðra umritun og aðra rukkun. Lykillinn lagar það. Þegar beiðni er endurtekin með sama lykli og sama innihaldi innan 24 klukkustunda skilar þjónninn fyrsta svarinu í stað þess að stofna nokkuð, og það breytir „að minnsta kosti einu sinni“ í „í raun einu sinni“. Reglan er því einn lykill fyrir hverja rökrétta upphleðslu, búinn til áður en fyrsta tilraun fer af stað og endurnotaður við hverja endurtekningu. Nýr lykill fyrir hverja tilraun myndi gera hverja endurtekningu að nýrri upphleðslu, og hausinn myndi ekki verja neitt.
import { VocalStack } from '@vocalstack/sdk';
const vs = new VocalStack({ baseUrl: '{{api}}', apiKey: process.env.VOCALSTACK_API_KEY });
const uploadKey = crypto.randomUUID(); // one key for this upload, however many attempts it takes
const tx = await vs.transcriptions.create(
{ file: './talk.mp3', name: 'Talk', settings: { spokenLanguage: 'en', speakerLabels: 'on' } },
{ idempotencyKey: uploadKey },
);
const recordingId = tx.job!.recordingId;
import asyncio
import os
import uuid
from vocalstack import VocalStack
async def main() -> None:
upload_key = str(uuid.uuid4()) # one key for this upload, however many attempts it takes
async with VocalStack("{{api}}", api_key=os.environ["VOCALSTACK_API_KEY"]) as vs:
tx = await vs.transcriptions.create(
file="./talk.mp3",
name="Talk",
settings={"spokenLanguage": "en", "speakerLabels": "on"},
idempotency_key=upload_key,
)
assert tx.job is not None
recording_id = tx.job.recording_id
Sama kall með curl:
API={{api}}
AUTH="Authorization: Bearer $VOCALSTACK_API_KEY"
UPLOAD_KEY=$(uuidgen)
REC=$(curl -sSf "$API/v1/transcriptions" -H "$AUTH" -H "Idempotency-Key: $UPLOAD_KEY" \
-F file=@talk.mp3 \
-F 'options={"name":"Talk","settings":{"spokenLanguage":"en","speakerLabels":"on"}}' \
| jq -r .job.recordingId)
Svarið 201 kemur áður en eitt orð hefur verið umritað; það sem þú heldur á er auðkenni verks, svo næsta skref er að bíða eftir því.
Verk fer úr queued í transcribing í done, eða lýkur í failed með error-skilaboðum og, þegar orsökin er vélrænt læsileg, errorCode. Fjórða ástandið, cropping, þýðir að verkið bíður þar til einhver velur hvaða bút upptökunnar á að umrita, eins og klippiskrefið í stjórnborðinu gerir. Venjuleg upphleðsla fer aldrei þangað, þótt jobs.wait skili líka þegar verk nær því. Lestu GET /v1/recordings/{id}/job þar til staðan er done eða failed.
Það eru engir vefkrókar (webhooks), og það er vísvitandi val. Vefkrókur þarf opinn HTTPS-endapunkt hjá þér, athugun á undirskrift svo enginn annar geti kallað á hann, og endurtekningarstefnu fyrir þegar endapunkturinn þinn er niðri, sem er mikil pípulögn fyrir verk sem tekur mínútu. Fyrirspurnir þurfa ekkert af þessu. Skriftan þín spyr „er þetta búið?“ á tveggja sekúndna fresti, eins og barn í aftursætinu á löngu ferðalagi, en ólíkt barninu fær hún prósentu til baka.
Vefsíða sem er þegar opin getur fylgst með atburðastraumi umritunarinnar í staðinn (WS /v1/transcriptions/{id}/events, job.progress-rammi þegar verkið þokast áfram). Fyrir skriftu er fyrirspurn rétta verkfærið.
SDK-in sjá um fyrirspurnirnar: vs.jobs.wait les verkið á tveggja sekúndna fresti og sendir hverja niðurstöðu til onProgress (on_progress í Python). progress verksins inniheldur pct (0 til 100) og processedS, fjölda miðilssekúndna sem hafa verið umritaðar hingað til. Forritin hér að neðan prenta línu aðeins þegar staða eða prósenta breytist.
let last = '';
const job = await vs.jobs.wait(recordingId, {
onProgress: (j) => {
const line = `${j.state} ${j.progress.pct}%`;
if (line !== last) console.log((last = line));
},
});
if (job.state !== 'done') throw new Error(`transcription ${job.state}: ${job.error ?? ''}`);
last = ""
def on_progress(j) -> None:
nonlocal last
line = f"{j.state} {j.progress.pct}%"
if line != last:
last = line
print(line)
job = await vs.jobs.wait(recording_id, on_progress=on_progress)
if job.state != "done":
raise RuntimeError(f"transcription {job.state}: {job.error}")
Athugaðu að wait kastar ekki villu við failed; það skilar verkinu og lætur athugunina í þínar hendur. Tvær sekúndur henta fyrir fyrirlestur. Fyrir þriggja tíma upptöku, eða lotu af hundruðum, skaltu lengja bilið (intervalMs, interval_s) eða draga úr tíðninni þegar verkið keyrir: fyrstu svörin segja mest, og þau síðari staðfesta að mestu leyti að þjónninn sé enn upptekinn. Án SDK skrifarðu sömu lykkju sjálfur:
while :; do
JOB=$(curl -sSf "$API/v1/recordings/$REC/job" -H "$AUTH")
STATE=$(jq -r .state <<<"$JOB")
echo "$STATE $(jq -r .progress.pct <<<"$JOB")%"
case $STATE in
done) break ;;
failed) jq -r .error <<<"$JOB" >&2; exit 1 ;;
esac
sleep 2
done
Þegar staðan sýnir done er uppskriftin tilbúin.
GET /v1/recordings/{id}/transcript skilar allri uppskriftinni í einu skjali:
sentences: það sem á að sýna fólki. Hver hefur text, speaker, og start og end í sekúndum.segments: línur vélarinnar, hver með tímasetningum orða í words (hvert orð hefur text, start og end).speakers: ein færsla fyrir hvert mælandanúmer, með name.Mælendur eru númeraðir frá 0. speaker með gildið -1 þýðir að mælendamerkingar voru slökktar fyrir upptökuna. Bættu við ?lang=es til að lesa uppskriftina þýdda yfir á spænsku; setningarnar koma þá til baka á því tungumáli.
const { sentences } = await vs.recordings.transcript(recordingId);
for (const s of sentences) console.log(s.speaker >= 0 ? `[speaker ${s.speaker}] ${s.text}` : s.text);
snapshot = await vs.recordings.transcript(recording_id)
for s in snapshot.sentences:
print(f"[speaker {s.speaker}] {s.text}" if s.speaker >= 0 else s.text)
curl -sSf "$API/v1/recordings/$REC/transcript" -H "$AUTH" \
| jq -r '.sentences[] | if .speaker >= 0 then "[speaker \(.speaker)] \(.text)" else .text end'
Tímasetningarnar tvær þjóna tvennu. Setningar eru lestrareiningin, svo það eru þær sem texti eða uppskriftarsíða sýnir. Orð eru bendingareiningin, svo skjátextalína í karókí-stíl, leitarniðurstaða eða klipping á bút fer um segments[].words. Útflutningssniðin eru byggð úr setningum.
GET /v1/recordings/{id}/export skilar alvöru skrá. format er eitt af srt, json, xlsx eða pdf, og skráin inniheldur allar leiðréttingar. Bættu við lang= fyrir þýdda skrá: útflutningurinn er þá lesinn á því tungumáli, ein lína fyrir hverja setningu, og hver setning sem ekki er enn þýdd er þýdd fyrst.
SRT er gott að kannast við, því það er skjátextasniðið sem nánast hver spilari tekur við og það er ómerkt textaskrá. Skrá er listi af skjátextaeiningum aðskildum með auðum línum, og hver eining er teljari, tímabil og texti. Tímastimplarnir nota kommu fyrir millisekúndurnar, franskur siður sem sniðið hefur aldrei losnað við:
1
00:00:01,000 --> 00:00:04,200
Welcome, everyone. Let's get started.
Ein setning í hverri einingu er rétta kornastærðin: hver eining er lesin á þeim tíma sem hún er á skjánum, og engin skerst af í miðri hugsun. Þegar upptakan var með mælendamerkingar byrjar texti hverrar einingar á nafni mælandans og tvípunkti.
Í TypeScript-SDK skilar vs.recordings.export Response; lestu bætin hans með arrayBuffer(). Í Python skilar það bytes.
import { writeFile } from 'node:fs/promises';
const srt = await vs.recordings.export(recordingId, 'srt');
await writeFile('talk.srt', Buffer.from(await srt.arrayBuffer()));
const pdf = await vs.recordings.export(recordingId, 'pdf', { lang: 'es' });
await writeFile('talk.es.pdf', Buffer.from(await pdf.arrayBuffer()));
srt = await vs.recordings.export(recording_id, "srt")
with open("talk.srt", "wb") as f:
f.write(srt)
pdf = await vs.recordings.export(recording_id, "pdf", lang="es")
with open("talk.es.pdf", "wb") as f:
f.write(pdf)
asyncio.run(main())
curl -sSf "$API/v1/recordings/$REC/export?format=srt" -H "$AUTH" -o talk.srt
curl -sSf "$API/v1/recordings/$REC/export?format=pdf&lang=es" -H "$AUTH" -o talk.es.pdf
Uppskriftin er geymd, svo hún birtist undir Transcriptions í stjórnborðinu, er hægt að deila með tengli og lesa aftur hvenær sem er síðar. Það er kjarni lykkjunnar: stofna, bíða, lesa, flytja út. Restin af þessari kennslu fjallar um afbrigðin: miðla sem þegar eru á netinu, villurnar sem þú lendir í og hvað skrá kostar.
Ef miðillinn er þegar á netinu afhendirðu API-inu tengilinn og það sækir skrána sjálft: opinbera http(s)-slóð (mp3, myndskeið, hlaðvarpsþátt) eða YouTube-myndskeið. Kallið er það sama, POST /v1/transcriptions, með JSON-innihaldið { "url": "..." } í stað margþátta upphleðslunnar, og name og settings eins og áður. Í SDK-unum gera vs.transcriptions.createFromUrl(url) (TypeScript) og vs.transcriptions.create(url=...) (Python) sömu beiðni.
Áður en þú stofnar nokkuð segir GET /v1/transcriptions/probe?url= þér hvað tengill er. kind hans er eitt af þremur:
file: upptaka með endi; umritaðu hana hér.live: YouTube-útsending í beinni, endalaus útvarpsstraumur eða HLS-spilunarlisti án enda. Bein tengill endar aldrei, svo ekki er hægt að umrita hann sem skrá; notaðu hann sem uppruna umritunar í beinni í staðinn.unknown: þjónninn gat ekki greint það.Þegar tengillinn opnast alls ekki sem miðill (vefsíða, til dæmis) er ok false og problem útskýrir það í einni setningu. Könnunin er valfrjáls og takmörkuð við 60 köll á mínútu.
Verk úr tengli byrjar í fetching áður en það færist yfir í transcribing og done. Á meðan það sækir segir progress.phase hvað þjónninn gerir: fetching meðan bæti berast (með fetchedBytes og fetchTotalBytes þegar hinn endinn segir hversu mörg), eða extracting meðan hljóðið er tekið úr myndskeiði. progress.phasePct segir hversu langt er komið í þeim áfanga. Langt myndskeið tekur tíma, svo kannaðu á nokkurra sekúndna fresti; vs.jobs.wait tekur intervalMs (interval_s í Python) til þess.
const probe = await vs.transcriptions.probe(url);
if (!probe.ok) throw new Error(`that link did not open: ${probe.problem}`);
if (probe.kind === 'live') throw new Error('a live link: record it with a live transcription instead');
const linkKey = crypto.randomUUID();
const tx = await vs.transcriptions.createFromUrl(url, { name: 'From a link' }, { idempotencyKey: linkKey });
const job = await vs.jobs.wait(tx.job!.recordingId, {
intervalMs: 3000,
onProgress: (j) => {
const p = j.progress;
if (j.state === 'fetching') console.log(`${p.phase ?? 'fetching'} ${p.phasePct ?? 0}%`);
else console.log(`${j.state} ${p.pct}%`);
},
});
Í Umrita tengil leiðbeiningunum í API-handbókinni er allt forritið á báðum tungumálum og í HTTP. Að sækja af tenglum bætir fleiri leiðum til að mistakast, svo hér eru villurnar sem þú sérð í raun.
Hvert misheppnað kall svarar með einu JSON-umslagi. code er strengurinn sem þú greinir á og status er HTTP-stöðukóðinn; message er fyrir fólk og requestId fyrir þjónustuver. Undir data segir retryable hvort það hjálpi að senda sömu beiðni aftur, og details geymir það sem kóðinn þarfnast. SDK-in kasta einni villutegund, VocalStackError, með sömu reitum.
Þrír kóðar eru þess virði að meðhöndla strax á fyrsta degi.
402 insufficient_credit: inneign reikningsins er búin, og ekkert var rukkað. details.balance er stöðan í dollurum og details.topUpUrl er staðurinn þar sem þú bætir við inneign. Þegar kostnaðurinn var þekktur fyrirfram, eins og fyrir skrá með þekkta lengd, segir details.required hvað hann hefði verið.429 rate_limited: bíddu í Retry-After sekúndur. Þegar details.metric er creates_per_hour ertu kominn yfir hraðamörk áætlunarinnar fyrir ný verk (nýjar umritanir á klukkustund), ekki stutt álagshámark; limit og resetsAt segja hvert hámarkið er og hvenær það losnar.503 unavailable: þjónustan er tímabundið ekki tiltæk. Sendu beiðnina aftur eftir Retry-After.SDK-in endurreyna 429 og 503 eftir Retry-After, tvisvar sinnum sjálfgefið (maxRetries / max_retries á biðlaranum). 503 er þó aðeins endurreynt á kalli sem er óhætt að senda tvisvar: GET, eða stofnun með Idempotency-Key. Stofnun án hans er eftir þér, því SDK getur ekki vitað hvort fyrsta tilraunin tókst. Það er hagnýta ástæðan fyrir því að setja lykil á hverja upphleðslu.
Hvert svar ber líka RateLimit-Limit, RateLimit-Remaining og RateLimit-Reset, svo álagsmikil keðja getur hægt á sér áður en 429 kemur.
{
"code": "insufficient_credit",
"status": 402,
"message": "this account has no credit left; add credit to continue",
"data": {
"retryable": false,
"details": { "balance": -0.42, "required": 0.075, "currency": "usd", "plan": "payg", "topUpUrl": "https://…" }
},
"requestId": "01J…"
}
Heildartaflan yfir kóða er í Villur, endurtekningar og takmörk. Þessi 402 er sú eina villa sem tengist peningum, svo hér er hvað skrá kostar.
Skrár kosta $0.20 á klukkustund af hljóði, rukkað eftir sekúndu ($0.0033 á mínútu) með 5 mínútna lágmarki: 30 mínútna skrá kostar $0.10. Það eru tíu sent, minna en kaffið sem þú drekkur á meðan framvindustikan fyllist. Verðið er það sama á öllum áætlunum og í gegnum API eins og í stjórnborðinu. Nýr reikningur fær einskiptis $5 inneign við nýskráningu, og ekkert kreditkort er krafist.
Þrjú mörk áætlunar snerta þessa kennslu.
GET /v1/realtime/capabilities skráir sem limits.maxUploadBytes. Það lægra gildir, og skrá sem er yfir því er hafnað með 413 áður en nokkuð er umritað. Fyrir langa upptöku er þetta ein ástæða enn til að hlaða upp þjappaðri mónó-skrá en ekki WAV.GET /v1/billing skilar áætlun þinni, stöðu og mörkum hvenær sem er; heildartaflan er á verðsíðunni.
Næstu skref eru streymi hljóðs í rauntíma þegar þú getur ekki beðið eftir að skrá klárist, eða tenging gervigreindarfulltrúa við sama reikning í gegnum MCP-þjóninn, svo hann umriti og leiti fyrir þig án alls kóðans hér að ofan.