Mae uwchlwytho yn dychwelyd swydd, nid trawsgrifiad. Rhowch Idempotency-Key ar bob creu fel na all ailgais byth greu na bilio ail drawsgrifiad; yna holwch y swydd nes ei bod wedi'i gorffen, a darllenwch frawddegau, geiriau neu allforiad o'r un id.
Y peth symlaf y mae API VocalStack yn ei wneud yw trawsgrifio ffeil, ac mae ganddo siâp unrhyw swydd asyncronaidd: uwchlwytho, aros, darllen. Mae'r uwchlwytho yn ateb ar unwaith gyda thrawsgrifiad a'i swydd, ond heb eiriau eto. Tra bo eich rhaglen yn holi'r swydd, mae'r gweinydd yn trawsgrifio'r ffeil sawl gwaith yn gyflymach na chyflymder chwarae'r sain. Pan fo'r cyflwr yn done, rydych yn darllen y trawsgrifiad fel brawddegau gyda siaradwyr ac amseriadau, neu'n ei allforio fel ffeil mewn un o bedair fformat, naill ai yn yr iaith lafar neu wedi'i chyfieithu.
Mae popeth yn y siâp hwn yn trosglwyddo i unrhyw API swp: creu sy'n dychwelyd id, statws rydych yn ei holi, a chanlyniad rydych yn ei nôl wedyn. Enwau'r meysydd yn unig sy'n benodol i VocalStack, ac mae'r tiwtorial hwn yn cerdded drwy'r rheini gyda'r SDKs TypeScript a Python, a'r galwadau HTTP lle maent yn help.
Agorwch Developers → API keys yn y Dashboard a chreu allwedd gyda'r caniatâd Read a Write. Dangosir yr allwedd unwaith yn unig; copïwch hi i newidyn amgylchedd:
export VOCALSTACK_API_KEY="<your API key>"
Allwedd ar gyfer gweinyddion a sgriptiau yw hon. Mae unrhyw beth sy'n cael ei anfon i borwr yn anfon yr allwedd gydag ef, felly cadwch hi allan o god y dudalen ac allan o URLau, sy'n cyrraedd logiau. I ffrydio o borwr, mae eich gweinydd yn creu sesiwn amser real ar ei gyfer; mae hynny yn diwtorial gwahanol.
Mae pob galwad yn dilysu gyda'r allwedd fel tocyn bearer:
Authorization: Bearer vs_key_...
Gyda'r allwedd yn eich amgylchedd, y peth nesaf yw cleient i'w hanfon.
Mae dau SDK. Mae gan bob un un cleient, VocalStack, gyda'i ddulliau wedi'u grwpio yn ôl adnodd: vs.transcriptions, vs.jobs, vs.recordings, vs.realtime a rhai eraill. Mae'r SDK TypeScript yn rhedeg ar Node 20 neu'n ddiweddarach ac mewn porwyr; mae'r SDK Python wedi'i adeiladu ar asyncio ac mae angen Python 3.10 neu'n ddiweddarach arno.
Nid yw'r SDKs ar npm na PyPI. Mae'r API yn gweini'r adeiladau i bob cyfrif. Mae'r gorchmynion gosod, wedi'u llenwi ar gyfer eich amgylchedd, yn yr adran Install yn y cychwyn cyflym yn y cyfeirnod API, ac maent yn anfon eich allwedd i'r gwesteiwr API yn unig. Os hoffech osod dim, mae'r galwadau HTTP isod ond angen curl a jq.
Pa bynnag un a ddewisoch, yr alwad gyntaf yw'r uwchlwytho.
Mae POST /v1/transcriptions yn derbyn y ffeil fel multipart/form-data: rhan file, ynghyd â rhan options ddewisol sy'n dal JSON. Mae'n ateb 201 gyda'r trawsgrifiad. Y job.recordingId yw id y recordiad a wnaed o'r ffeil, ac mae pob galwad sy'n dilyn yn cyfeirio at yr id hwnnw.
Mae dau osodiad yn bwysig ar gyfer rhediad cyntaf, y ddau o fewn options.settings:
spokenLanguage: cod fel en. Gadewch ef allan a chanfyddir yr iaith. Mae ei enwi yn gwella cywirdeb ar recordiadau swnllyd ac acenion trwm.speakerLabels: auto, on neu off. Gyda on, mae gan bob brawddeg rif siaradwr; gyda auto, mae labeli'n ymddangos dim ond unwaith y clywir ail siaradwr.Mae'r rhan fwyaf o ffeiliau sain a fideo yn cael eu derbyn: mp3, wav, flac, ogg, m4a, mp4 ac eraill. Mae fideo yn cadw ei sain yn unig. Nid yw'r fformat yn bwysig iawn i gywirdeb, oherwydd mae adnabod lleferydd yn gweithio ar 16 kHz mono ac mae popeth yn cael ei ailsamplu i hynny wrth gyrraedd. Mae WAV stereo 48 kHz o un person yn siarad yn treulio'r rhan fwyaf o'i beitiau yn disgrifio tawelwch mewn ffyddlondeb uchel. Mae'n trawsgrifio cystal â ffeil MP3 neu Opus mono 64 kbps o'r un sgwrs, ond mae tua ugain gwaith yn fwy, felly mae'n cymryd ugain gwaith cymaint o amser i'w uwchlwytho. Os trosglwyddwch cyn uwchlwytho, mono a chyfradd didau gymedrol yw'r ddau osodiad sy'n werth eu newid.
Anfonwch Idempotency-Key gyda phob uwchlwytho, a deallwch pam. Nid yw amser allan rhwydwaith yn dweud dim wrthych am yr hyn a wnaeth y gweinydd: efallai na welodd y cais o gwbl, neu efallai iddo storio'r ffeil a rhoi'r swydd yn y ciw cyn i'r ateb fynd ar goll. Felly mae ailgais dall yn peryglu ail drawsgrifiad ac ail filiad. Mae'r allwedd yn datrys hynny. Pan fo cais yn cael ei ailadrodd gyda'r un allwedd a'r un corff o fewn 24 awr, mae'r gweinydd yn dychwelyd yr ateb cyntaf yn lle creu dim, gan droi "o leiaf unwaith" yn "i bob pwrpas unwaith". Y rheol, felly, yw un allwedd i bob uwchlwytho rhesymegol, wedi'i chynhyrchu cyn y cais cyntaf a'i hailddefnyddio ar bob ailgais. Byddai allwedd newydd ar bob ymgais yn gwneud pob ailgais yn uwchlwytho newydd, ac ni fyddai'r pennawd yn amddiffyn dim.
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
Yr un alwad gyda 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)
Mae'r 201 yn dod cyn i un gair gael ei drawsgrifio; ID swydd sydd gennych, felly y cam nesaf yw aros amdani.
Mae swydd yn symud o queued → transcribing → done, neu'n gorffen mewn failed gyda neges error ac, pan fo'r achos yn un y gall peiriant ei ddarllen, errorCode. Mae pedwerydd cyflwr, cropping, yn golygu bod y swydd wedi'i pharcio nes bod rhywun yn dewis pa ran o'r recordiad i'w thrawsgrifio, fel y mae cam tocio'r Dashboard yn ei wneud. Nid yw uwchlwytho cyffredin byth yn cyrraedd y cyflwr hwn, er bod jobs.wait hefyd yn dychwelyd pan fo swydd yn ei gyrraedd. Darllenwch GET /v1/recordings/{id}/job nes bod y cyflwr yn done neu failed.
Nid oes webhooks, ac mae hynny'n gyfnewid bwriadol. Mae webhook angen pwynt HTTPS cyhoeddus ar eich ochr, gwiriad llofnod fel na all neb arall ei alw, a pholisi ailgeisio ar gyfer pan fo eich pwynt i lawr, sy'n llawer o blymio ar gyfer swydd sy'n cymryd munud. Mae holi angen dim o hynny. Bydd eich sgript yn gofyn "wedi gorffen eto?" bob dwy eiliad, fel plentyn yn y sedd gefn, ac yn wahanol i'r plentyn mae'n cael canran yn ôl.
Gall tudalen we sydd eisoes ar agor ddilyn soced digwyddiadau'r trawsgrifiad yn lle hynny (WS /v1/transcriptions/{id}/events, ffrâm job.progress wrth i'r swydd fynd yn ei blaen). Ar gyfer sgript, holi yw'r offeryn cywir.
Mae'r SDKs yn gwneud yr holi: mae vs.jobs.wait yn darllen y swydd bob 2 eiliad ac yn rhoi pob darlleniad i onProgress (on_progress yn Python). Mae progress y swydd yn cario pct (0 i 100) a processedS, yr eiliadau cyfryngau sydd wedi'u trawsgrifio hyd yn hyn. Mae'r rhaglenni isod yn argraffu llinell dim ond pan fydd y cyflwr neu'r ganran yn newid.
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}")
Sylwch nad yw wait yn codi gwall ar failed; mae'n dychwelyd y swydd ac yn gadael y gwiriad i chi. Mae dwy eiliad yn addas ar gyfer sgwrs. Ar gyfer recordiad tair awr, neu swp o gannoedd, ehangwch yr egwyl (intervalMs, interval_s) neu arafwch wrth i'r swydd fynd yn ei blaen: y darlleniadau cyntaf sy'n dweud fwyaf wrthych, ac mae'r rhai diweddarach gan amlaf yn cadarnhau bod y gweinydd yn dal yn brysur. Heb SDK, ysgrifennwch yr un ddolen â llaw:
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
Pan fydd y cyflwr yn darllen done, mae'r trawsgrifiad yn barod.
Mae GET /v1/recordings/{id}/transcript yn ateb gyda'r trawsgrifiad cyfan mewn un ddogfen:
sentences: yr hyn i'w ddangos i bobl. Mae gan bob un text, speaker, a start a end mewn eiliadau.segments: llinellau'r peiriant, pob un gydag amseriadau ar lefel gair yn words (mae gan bob gair text, start a end).speakers: un cofnod am bob rhif siaradwr, gyda'i name.Mae siaradwyr wedi'u rhifo o 0. Mae speaker o -1 yn golygu bod labeli siaradwyr wedi'u diffodd ar gyfer y recordiad. Ychwanegwch ?lang=es i ddarllen y trawsgrifiad wedi'i gyfieithu i Sbaeneg; daw'r brawddegau yn ôl yn yr iaith honno.
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'
Mae dwy lefel yr amseru yn gwasanaethu dwy swyddogaeth. Brawddegau yw'r uned ddarllen, felly dyna y mae is-deitl neu dudalen trawsgrifiad yn ei dangos. Geiriau yw'r uned bwyntio, felly mae uchafbwynt arddull karaoke, chwiliad neu doriad clip yn cerdded segments[].words yn lle hynny. Mae'r fformatau allforio wedi'u hadeiladu o frawddegau.
Mae GET /v1/recordings/{id}/export yn dychwelyd ffeil go iawn. format yw un o srt, json, xlsx neu pdf, ac mae'r ffeil yn cynnwys unrhyw gywiriadau. Ychwanegwch lang= ar gyfer ffeil wedi'i chyfieithu: mae'r allforiad wedyn yn darllen yn yr iaith honno, un llinell fesul brawddeg, ac mae unrhyw frawddeg nad yw wedi'i chyfieithu eto yn cael ei chyfieithu yn gyntaf.
Mae'n werth adnabod SRT wrth ei olwg, gan mai hon yw'r fformat is-deitl y mae bron pob chwaraewr yn ei dderbyn, ac mae'n destun plaen. Rhestr o giwiau yw ffeil, wedi'u gwahanu gan linellau gwag, ac mae pob ciw yn cynnwys cyfrif, amrediad amser a'r testun. Mae'r amseroedd yn defnyddio coma cyn y milieiliadau, arfer Ffrengig nad yw'r fformat erioed wedi'i ollwng:
1
00:00:01,000 --> 00:00:04,200
Welcome, everyone. Let's get started.
Y gronyn gorau yw un brawddeg fesul ciw: gellir darllen pob ciw yn yr amser y mae ar y sgrin, ac nid oes un yn torri ar draws meddwl. Pan oedd gan y recordiad labeli siaradwyr, mae testun pob ciw yn dechrau gydag enw'r siaradwr a cholon.
Yn SDK TypeScript, mae vs.recordings.export yn dychwelyd Response; darllenwch ei fytiau gyda arrayBuffer(). Yn Python mae'n dychwelyd 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
Mae'r trawsgrifiad wedi'i storio, felly mae'n ymddangos o dan Transcriptions yn y Dashboard, gellir ei rannu drwy ddolen, a gellir ei ddarllen eto unrhyw bryd wedyn. Dyna'r ddolen graidd: creu, aros, darllen, allforio. Mae gweddill y tiwtorial hwn yn trafod yr amrywiadau: cyfryngau sydd eisoes ar-lein, y gwallau y byddwch yn eu cyfarfod, a faint y mae ffeil yn ei gostio.
Os yw'r cyfryngau eisoes ar-lein, rhowch y ddolen i'r API a bydd yn nôl y ffeil ei hun: cyfeiriad cyhoeddus http(s) (mp3, fideo, pennod podlediad) neu fideo YouTube. Yr alwad yw'r un POST /v1/transcriptions, gyda chorff JSON { "url": "..." } yn lle'r uwchlwytho multipart, a name a settings fel o'r blaen. Yn yr SDKs, mae vs.transcriptions.createFromUrl(url) (TypeScript) a vs.transcriptions.create(url=...) (Python) yn gwneud yr un cais.
Cyn creu dim, mae GET /v1/transcriptions/probe?url= yn dweud wrthych beth yw dolen. Mae ei kind yn un o dri:
file: recordiad gyda diwedd; trawsgrifiwch ef yma.live: ffrwd fyw YouTube, ffrwd radio ddiddiwedd neu restr chwarae HLS heb ddiwedd. Nid yw dolen fyw byth yn dod i ben, felly ni ellir ei thrawsgrifio fel ffeil; defnyddiwch hi fel ffynhonnell trawsgrifiad byw yn lle hynny.unknown: ni allai'r gweinydd ddweud.Pan nad yw'r ddolen yn agor fel cyfryngau o gwbl (tudalen we, er enghraifft), mae ok yn false ac mae problem yn esbonio pam mewn un frawddeg. Mae'r probe yn ddewisol, ac wedi'i gyfyngu i 60 galwad y funud.
Mae swydd ddolen yn dechrau yn fetching cyn symud i transcribing a done. Tra mae'n nôl, mae progress.phase yn dweud beth mae'r gweinydd yn ei wneud: fetching tra bo beitiau'n cyrraedd (gyda fetchedBytes a fetchTotalBytes pan fo'r pen arall yn dweud faint), neu extracting tra bo'r sain yn cael ei thynnu allan o fideo. Mae progress.phasePct yn dweud pa mor bell drwy'r cam hwnnw y mae'r swydd. Mae fideo hir yn cymryd amser, felly holwch bob ychydig eiliadau; mae vs.jobs.wait yn derbyn intervalMs (interval_s yn Python) at hynny.
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}%`);
},
});
Mae canllaw Trawsgrifio dolen yn y cyfeirnod yn cynnwys y rhaglen lawn yn y ddwy iaith ac mewn HTTP. Mae nôl o ddolenni yn ychwanegu mwy o ffyrdd i fethu, felly dyma'r gwallau y byddwch yn eu gweld go iawn.
Mae pob galwad a fethodd yn ateb gydag un amlen JSON. code yw'r llinyn i ganghennu arno a status yw'r statws HTTP; mae message ar gyfer pobl a requestId ar gyfer cymorth. O dan data, mae retryable yn dweud a all anfon yr un cais eto helpu, ac mae details yn dal beth bynnag sydd ei angen ar y cod. Mae'r SDKs yn codi un math gwall, VocalStackError, gyda'r un meysydd.
Mae tri chod yn werth eu trin o'r diwrnod cyntaf.
402 insufficient_credit: mae credyd y cyfrif wedi dod i ben, ac ni chodwyd dim. Mae details.balance yn y balans mewn doleri a details.topUpUrl yn ble i ychwanegu credyd. Pan oedd y gost yn hysbys ymlaen llaw, fel ar gyfer ffeil o hyd hysbys, mae details.required yn dweud beth fyddai hi.429 rate_limited: arhoswch Retry-After eiliad. Pan fo details.metric yn creates_per_hour, rydych wedi cyrraedd cyfradd creu'r cynllun (trawsgrifiadau newydd yr awr), nid terfyn byrstio; mae limit a resetsAt yn dweud beth yw'r terfyn a phryd y mae'n rhyddhau.503 unavailable: nid yw'r gwasanaeth ar gael am ychydig. Anfonwch y cais eto ar ôl Retry-After.Mae'r SDKs yn ailgeisio 429 a 503 ar ôl Retry-After, ddwywaith yn ddiofyn (maxRetries / max_retries ar y cleient). Ond dim ond ar alwad sy'n ddiogel i'w hanfon ddwywaith y caiff 503 ei ailgeisio: GET, neu greu sy'n cario Idempotency-Key. Mae creu heb un yn cael ei adael i chi, gan na all yr SDK wybod a aeth y cais cyntaf drwodd. Dyna'r rheswm ymarferol i roi allwedd ar bob uwchlwytho.
Mae pob ymateb hefyd yn cario RateLimit-Limit, RateLimit-Remaining a RateLimit-Reset, fel y gall piblinell brysur arafu cyn y 429.
{
"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…"
}
Mae'r tabl cyfan o godau yn Gwallau, ailgeisiau a therfynau. Mae'r 402 honno yr unig wall sy'n gysylltiedig ag arian, felly dyma beth mae ffeil yn ei gostio.
Mae ffeiliau yn costio $0.20 yr awr o sain, wedi'u bilio fesul eiliad ($0.0033 y munud) gydag isafswm o 5 munud: mae ffeil 30 munud yn costio $0.10. Dyna ddeg cheiniog, llai na'r coffi y byddwch yn ei yfed tra bo'r bar cynnydd yn llenwi. Mae'r gyfradd yr un ar bob cynllun, a'r un drwy'r API ag yn y Dashboard. Mae cyfrif newydd yn dechrau gyda chredyd cofrestru untro o $5, ac nid oes angen cerdyn.
Mae tri therfyn cynllun yn effeithio ar y tiwtorial hwn.
GET /v1/realtime/capabilities fel limits.maxUploadBytes. Mae'r llai o'r ddau yn berthnasol, ac mae ffeil sy'n fwy na hynny yn cael ei gwrthod gyda 413 cyn i unrhyw beth gael ei drawsgrifio. Ar gyfer recordiad hir, mae hyn yn un rheswm arall i uwchlwytho ffeil mono wedi'i chywasgu yn hytrach na WAV.Mae GET /v1/billing yn adrodd eich cynllun, balans a therfynau ar unrhyw adeg; mae'r tabl llawn ar y dudalen brisiau.
O'r fan hon, y camau nesaf yw ffrydio sain mewn amser real pan na allwch aros i ffeil orffen, neu gysylltu asiant AI â'r un cyfrif drwy'r gweinydd MCP, fel ei fod yn trawsgrifio ac yn chwilio ar eich rhan heb unrhyw un o'r cod uchod.