التوثيقالتفريغ المباشر
التفريغ المباشر
أرسل صوت الميكروفون أو علامة التبويب إلى transcript1 عبر WebSocket، واستلم سطور الترجمة النصية مع ترجمة فورية اختيارية أثناء حديث الناس.
يحوّل التفريغ المباشر تدفق الصوت إلى سطور ترجمة نصية بينما لا يزال الناس يتحدثون. يرسل عميلك صوت PCM خام عبر WebSocket، فيعيد transcript1 سطورا تبدأ تخمينا مؤقتا ثم تصبح نهائية. أضف translate_to لتحمل كل سطر ترجمته أيضا. وعند انتهاء الجلسة تُحفظ النتيجة تفريغا عاديا.
لا توجد باقات منفصلة للتفريغ المباشر: فهو يستخدم رصيد الدقائق نفسه الذي تستخدمه بقية الميزات. راجع الفوترة لمعرفة كيف يُحتسب وقت الاتصال.
متى تستخدم التفريغ المباشر
| استخدم التفريغ المباشر عندما | استخدم التفريغ العادي عندما |
|---|---|
| لا يزال الصوت قيد الإنتاج: مكالمة أو محاضرة أو بث أو فيديو تشاهده. | لديك رابط أو ملف جاهز. |
| تحتاج إلى الترجمة النصية خلال ثانية أو ثانيتين من الكلام. | تريد أعلى دقة ويمكنك انتظار النتيجة. |
| تريد ترجمة فورية بجانب كل سطر. | تريد ترجمة نص مفرغ مكتمل، أو الترجمة إلى عدة لغات. |
يقبل التفريغ المباشر الصوت فقط، بصيغة PCM خام. ولا يقبل روابط الفيديو ولا الملفات المرفوعة ولا WebM أو Opus أو MP3. التقط الصوت في عميلك وحوّله إلى PCM أولا. يوضح قسم التقاط الصوت في المتصفح الطريقة.
بداية سريعة مع SDK
ثبّت الإصدار 1.5.0 أو أحدث من SDK: npm i @transcript1/sdk@latest. تنشئ t1.live.start الجلسة وتتصل، وتكتمل بعد أن تبدأ الجلسة، فيمكنك إرسال الصوت فورا.
import { createReadStream } from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';
import { Transcript1 } from '@transcript1/sdk';
const t1 = new Transcript1(); // reads TRANSCRIPT1_API_KEY
const live = await t1.live.start({ language: 'en', translateTo: 'es', sampleRate: 16000 });
live.on('line', line => {
const mark = line.final ? 'final ' : 'partial';
console.log(mark, line.text, '→', line.translation?.text ?? '');
});
live.on('warning', warning => console.warn(warning.code));
// speech.pcm: 16 kHz, mono, signed 16-bit little-endian, no header
for await (const chunk of createReadStream('./speech.pcm', { highWaterMark: 3200 })) {
live.send(chunk);
await sleep(100); // 3,200 bytes are 100 ms of audio: send at real-time speed
}
const result = await live.end();
console.log(result.reason, result.transcriptionId, result.minutesCharged);تُحدَّث السطور في مكانها. استبدل السطر الذي لديك عندما يصل سطر بالمعرف id نفسه، أو اقرأها جميعا من live.lines. تُنهي end() الجلسة وتنتظر السطور الأخيرة وتحفظ النص المفرغ. راجع Node.js SDK لكل الدوال والأحداث.
بداية سريعة مع WebSocket مباشر
بدون SDK هناك خطوتان: أنشئ جلسة عبر HTTPS، ثم افتح WebSocket بالرمز المميز ذي الاستخدام الواحد الذي عاد إليك.
إنشاء جلسة
curl -X POST https://api.transcript1.com/v1/live/sessions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Content-Type: application/json" \
-d '{"language": "en", "translate_to": "es", "sample_rate": 16000}'يجيب API بالرمز 201:
{
"id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
"status": "waiting",
"transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
"websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD/stream",
"token": "Zk3x...",
"token_expires_at": "2026-11-02T12:01:00Z",
"rate": 1
}تكون قيمة transcription_id هي null عندما تكون save هي false. الرمز المميز يُستخدم مرة واحدة وتنتهي صلاحيته بعد 60 ثانية، لذا اتصل فورا. تمثل rate عدد دقائق الرصيد المحتسبة لكل دقيقة بدأت في هذه الجلسة؛ راجع الفوترة.
يقبل جسم الطلب الحقول التالية. كلها اختيارية، والنطاق المطلوب هو transcriptions:write:
| الحقل | الافتراضي | القاعدة |
|---|---|---|
language | auto | auto، أو رمز من حرفين تدعمه ميزة التفريغ المباشر. |
translate_to | لا يوجد | رمز من حرفين. لغة هدف واحدة لكل جلسة. راجع الترجمة المباشرة. |
diarize | false | يضع تسميات للمتحدثين. بعض الإعدادات لا تستطيع تمييز المتحدثين، مثل استخدامه مع language: "auto"، وتجيب بالرمز 422. راجع الإمكانات. |
audio_format | pcm_s16le | القيمة الوحيدة. الحقل موجود لإضافة صيغ أخرى لاحقا. |
sample_rate | 16000 | عدد صحيح من 8000 إلى 48000. أحادي القناة فقط. |
save | true | القيمة false لا تحتفظ بشيء بعد انتهاء الجلسة. راجع النصوص المحفوظة. |
الاتصال والبث
افتح WebSocket مع الرمز المميز بوصفه البروتوكول الفرعي الثاني. يعمل هذا في المتصفحات وفي Node 22+ دون ترويسات مخصصة. انتظر session.started قبل إرسال الصوت: تُهمل الإطارات الثنائية المرسلة قبله، وتصلك رسالة تحذير audio_dropped.
import { createReadStream } from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';
const session = await fetch('https://api.transcript1.com/v1/live/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRANSCRIPT1_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ language: 'en', translate_to: 'es', sample_rate: 16000 }),
}).then(response => response.json());
const ws = new WebSocket(session.websocket_url, ['t1-live', session.token]);
const lines = new Map();
ws.addEventListener('message', async message => {
const event = JSON.parse(message.data);
if (event.type === 'session.started') {
for await (const chunk of createReadStream('./speech.pcm', { highWaterMark: 3200 })) {
ws.send(chunk);
await sleep(100);
}
ws.send(JSON.stringify({ type: 'end' }));
} else if (event.type === 'line') {
lines.set(event.line.id, event.line); // the full current state: replace by id
console.log(event.line.final, event.line.text, event.line.translation?.text);
} else if (event.type === 'session.ended') {
console.log('ended:', event.reason, event.minutes_charged);
}
});
ws.addEventListener('close', event => console.log('closed', event.code));لا يوجد WebSocket مدمج في Node 20: استخدم حزمة ws، التي لها الصيغة نفسها new WebSocket(url, protocols).
يرد الخادم على الرمز المميز الصالح بالبروتوكول الفرعي t1-live فقط. الرمز الخاطئ لا يُفشل المصافحة أبدا: يُفتح المقبس وتصلك الرسالة error ثم يُغلق برمز من 4xxx، فيعرض المتصفح السبب الحقيقي بدل 1006 العام. راجع رموز الإغلاق.
التقاط الصوت في المتصفح
يجب أن يكون صوت التفريغ المباشر PCM أحادي القناة بعينات 16 بت موقّعة بترتيب little-endian وبمعدل sample_rate الخاص بالجلسة. تعطيك المتصفحات عينات عشرية، لذا حوّلها داخل AudioWorklet. يلتقط المثال التالي الميكروفون بصيغة PCM بتردد 16 kHz في مقاطع مدة كل منها 100 ms ويرسله عبر مدخل SDK الخاص بالمتصفح. تعيد getAccessToken رمز وصول OAuth؛ لا تضع مفتاح API في المتصفح أبدا. راجع الأمان.
import { createLiveClient } from '@transcript1/sdk/live';
const workletSource = `
class Pcm16Processor extends AudioWorkletProcessor {
constructor() {
super();
this.buffer = new Int16Array(1600); // 100 ms at 16 kHz
this.filled = 0;
}
process(inputs) {
const channel = inputs[0][0];
if (!channel) return true;
for (let i = 0; i < channel.length; i++) {
const sample = Math.max(-1, Math.min(1, channel[i]));
this.buffer[this.filled++] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;
if (this.filled === this.buffer.length) {
this.port.postMessage(this.buffer.slice());
this.filled = 0;
}
}
return true;
}
}
registerProcessor('pcm16', Pcm16Processor);
`;
const client = createLiveClient({ getAccessToken: () => myApp.getOAuthAccessToken() });
const stream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1 } });
const context = new AudioContext({ sampleRate: 16000 });
const moduleUrl = URL.createObjectURL(new Blob([workletSource], { type: 'text/javascript' }));
await context.audioWorklet.addModule(moduleUrl);
// the context's real rate: a browser that cannot do 16 kHz picks its own
const live = await client.start({ language: 'auto', sampleRate: context.sampleRate });
live.on('line', renderLine);
const microphone = context.createMediaStreamSource(stream);
const capture = new AudioWorkletNode(context, 'pcm16');
capture.port.onmessage = event => live.send(event.data); // an Int16Array
const mute = context.createGain();
mute.gain.value = 0;
microphone.connect(capture).connect(mute).connect(context.destination);
async function stop() {
stream.getTracks().forEach(track => track.stop());
await context.close();
return live.end();
}كُتب الـ worklet لتردد 16 kHz. إذا عمل السياق بمعدل آخر فلن يكون المقطع 100 ms، لكن PCM يبقى صحيحا لأن sampleRate يطابق السياق. لالتقاط صوت علامة تبويب أو فيديو بدل الميكروفون، أنشئ MediaStream بالدالة getDisplayMedia أو من captureStream() لعنصر وسائط، ثم صله بالطريقة نفسها.
الترجمة المباشرة
أضف translate_to إلى الجلسة فيترجم transcript1 كل سطر أثناء نطقه.
- لغة هدف واحدة لكل جلسة. أضف لغات أخرى إلى النص المحفوظ لاحقا عبر نقطة نهاية الترجمة.
- لكل
lineنص أصلي (textوfinal) وكائنtranslation(languageوtextوfinal). تكونtranslationهيnullعندما لا تُضبطtranslate_to. - يصبح الأصل والترجمة نهائيين بشكل مستقل: قد يكون للسطر أصل نهائي بينما لا تزال ترجمته تتغير. اعرض السطر نهائيا عندما يصبح الاثنان نهائيين.
- السطر الذي هو أصلا باللغة الهدف يستخدم نصه نفسه ترجمة له، ويصبح نهائيا عند إغلاق السطر.
- يُغلق السطر عندما يتوقف المتحدث، أو يتغير المتحدث، أو يتجاوز الفاصل بين الكلمات 0.9 ثانية، أو يبلغ السطر صفين من الترجمة النصية (84 حرفا) أو 15 ثانية، أو ترسل
finalize. مع تفعيل الترجمة قد يستغرق إغلاق السطر نحو 3 ثوان إضافية أثناء وصول الترجمة. في هذه الحالة قد تبقى الترجمة مفتوحة (final: false) وتستمر في النمو بعد أن يصبح الأصل نهائيا. - الترجمة مجانية. لا تستهلك دقائق.
تعتمد اللغات الهدف وأزواج اللغات المتاحة على مزود الكلام المباشر. اقرأ الإمكانات بدل تثبيت القواعد في الشيفرة. تعيد لغة الهدف غير المدعومة 422 unsupported_translation. على سبيل المثال، قد يشترط المزود language صريحة بدل auto، أو يسمح فقط بالأزواج التي تتضمن الإنجليزية.
بروتوكول WebSocket
الاتصال
new WebSocket(websocket_url, ['t1-live', token]);ينتقل الرمز المميز بوصفه بروتوكولا فرعيا لـ WebSocket. لا يظهر أبدا في عنوان URL ولا في سجل الوصول. لا ترسل ترويسة Authorization: الرمز المميز هو بيانات الاعتماد. يرسل الخادم ping كل 30 ثانية؛ وتجيب المتصفحات وحزمة ws تلقائيا. يُقطع المقبس الذي لا يرسل pong خلال 60 ثانية.
الاتصال الصالح بجلسة لها مقبس بالفعل يحل محل المقبس القديم. راجع الاستئناف والاستبدال.
رسائل العميل
| الرسالة | المعنى |
|---|---|
| إطار ثنائي | الصوت: PCM s16le أحادي القناة بمعدل sample_rate الخاص بالجلسة. يجب أن يكون طول كل إطار زوجيا وبحد أقصى 256 KB. نحو 100 ms لكل إطار حجم مناسب. |
{"type":"finalize"} | يغلق السطر الحالي الآن، مثلا عند إيقاف الفيديو مؤقتا. |
{"type":"end"} | يُنهي الجلسة ويحفظها. يجيب الخادم بـ session.ended ثم يغلق المقبس. |
| أي شيء آخر | حدث error غير فادح بالرمز invalid_message. |
الإطار ذو الطول الفردي أو الكبير أكثر من اللازم يعد مخالفة للبروتوكول ويغلق المقبس بالرمز 4008.
ابدأ إرسال الصوت بعد session.started. العميل الذي يرسل الصوت أسرع من الزمن الحقيقي يُحاسب على مدة الصوت بدل وقت الاتصال (راجع الفوترة)، لذا أرسل الصوت بالسرعة التي يُنتج بها.
أحداث الخادم
كل حدث هو إطار نصي JSON له type.
| الحدث | يُرسل عندما |
|---|---|
session.started | قُبل الاتصال. أرسل الصوت من الآن. |
line | أُنشئ سطر ترجمة أو تغيّر. |
usage | بعد كل عملية احتساب. |
warning | هناك أمر يحتاج انتباهك لكن الجلسة تستمر. |
error | مشكلة غير فادحة، أو سبب إغلاق فوري. |
reconnect | الخادم على وشك إعادة التشغيل. استأنف الجلسة. |
session.ended | انتهت الجلسة وحُفظت. آخر حدث قبل الإغلاق العادي. |
يوضح session.started هل هذا استئناف وكم من الصوت عالجته الجلسة حتى الآن:
{
"type": "session.started",
"session_id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
"transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
"resumed": false,
"offset_ms": 0
}يحمل line الحالة الكاملة الحالية لسطر واحد، لذا استبدل السطر الذي لديك بالمعرف id نفسه. راجع كائن السطر.
{
"type": "line",
"line": {
"id": 12,
"start_ms": 53120,
"end_ms": 56040,
"speaker": null,
"language": "en",
"text": "Welcome back to the stream",
"final": true,
"translation": { "language": "es", "text": "Bienvenidos de nuevo", "final": false }
}
}يُرسل usage بعد كل عملية احتساب، ومعه وقت الاتصال ومدة الصوت ودقائق الرصيد المحتسبة حتى الآن والدقائق المتبقية لديك:
{
"type": "usage",
"connected_ms": 61240,
"audio_ms": 60100,
"minutes_charged": 2,
"minutes_remaining": 898
}يحمل warning قيمة code:
code | المعنى |
|---|---|
balance_low | ستفشل عملية الاحتساب التالية. تنتهي الجلسة بالسبب balance_exhausted عند حلول موعدها. لا يُرسل أثناء تفعيل الشحن التلقائي. |
audio_dropped | وصل الصوت قبل أن تكون الجلسة جاهزة، أو وصل صوت كثير أثناء اتصال مزود الكلام. أُهمل أقدم صوت. |
idle_timeout_soon | لا صوت منذ 4 دقائق. تنتهي الجلسة بعد 5 دقائق دون صوت. أرسل الصوت لإبقائها. |
{ "type": "warning", "code": "idle_timeout_soon" }يحمل error قيمتي code وmessage. يمكن أن تصل هذه الرموز على مقبس مباشر:
code | فادح | المعنى |
|---|---|---|
invalid_message | لا | إطار نصي ليس finalize ولا end. |
invalid_token | نعم | الرمز المميز غير صالح أو منتهي أو مستخدم من قبل. يُغلق المقبس بالرمز 4001. |
not_found | نعم | الجلسة غير موجودة. يُغلق المقبس بالرمز 4004. |
session_ended | نعم | انتهت الجلسة بالفعل. يُغلق المقبس بالرمز 4004. |
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }يصل reconnect قبل أن يغلق الخادم المقبس بالرمز 1012، مثلا أثناء النشر. احصل على رمز مميز جديد واستأنف الجلسة.
{ "type": "reconnect", "reason": "server_restart" }session.ended هو آخر حدث في كل جلسة. الأرقام هي الإجماليات النهائية:
{
"type": "session.ended",
"reason": "client_ended",
"transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
"minutes_charged": 3,
"connected_ms": 142500,
"audio_ms": 141900
}كائن السطر
| الحقل | المعنى |
|---|---|
id | رقم يبدأ من 0 ويزيد بواحد لكل سطر. وهو فهرس المقطع في النص المحفوظ. |
start_ms, end_ms | البداية والنهاية بحسب زمن صوت الجلسة. يستمر هذا الزمن عبر إعادة الاتصال. |
speaker | تسمية المتحدث عند تفعيل diarize، وإلا null. التسميات نفسها المستخدمة في النصوص المفرغة العادية. |
language | لغة السطر المكتشفة أو المحددة، أو null إذا لم تُعرف بعد. |
text | النص الأصلي حتى الآن. |
final | تكون true متى توقف النص الأصلي عن التغير. |
translation | null بدون translate_to، وإلا language وtext وfinal. |
اعرض السطور ذات final: false بنمط أخف: فنصها قد يتغير. أما نص السطر ذي final: true فلا يتغير أبدا، ولا يُحذف سطر نهائي.
يستخدم SDK الصيغة camelCase لحقول السطر: startMs وendMs.
أسباب الانتهاء
يعطي session.ended وGET /v1/live/sessions/:id القيمة end_reason:
| السبب | المعنى |
|---|---|
client_ended | أرسلت end. |
balance_exhausted | فشلت عملية الاحتساب التالية. تنتهي الجلسة عند حد الدقيقة وتُحفظ. |
idle_timeout | لا صوت لمدة 5 دقائق. |
max_duration | ظلت الجلسة متصلة 300 دقيقة. ابدأ جلسة جديدة. |
disconnected | غاب العميل أكثر من دقيقتين دون استئناف. |
upstream_error | فشل مزود الكلام. |
internal_error | حدث خطأ من جانبنا. |
not_connected | لم يتصل أحد بالجلسة خلال 5 دقائق. لا تُحتسب عليها رسوم أبدا ولا تظهر إلا عبر GET. |
رموز الإغلاق
| الرمز | المعنى |
|---|---|
1000 | إغلاق عادي. يأتي دائما بعد session.ended. |
1011 | خطأ غير متوقع في الخادم. يستعيد الخادم الجلسة: استأنفها برمز مميز جديد. |
1012 | إعادة تشغيل الخادم. يأتي بعد reconnect: استأنف الجلسة برمز مميز جديد. |
4001 | الرمز المميز غير صالح أو منتهي أو مستخدم من قبل. |
4004 | لم تُعثر على الجلسة أو انتهت بالفعل. |
4008 | مخالفة للبروتوكول: إطار ثنائي بطول فردي أو بحجم كبير أكثر من اللازم. |
4010 | حلّ محله اتصال أحدث بالجلسة نفسها. |
أثناء إعادة تشغيل API قد يُجاب طلب WebSocket جديد بالرمز HTTP 503. حاول مجددا بعد قليل.
الاستئناف والاستبدال
تصمد الجلسة أمام انقطاع الشبكة ونشر API وتعطله. تنتظرك الجلسة دقيقتين بعد الانقطاع، ثم تنتهي بالسبب disconnected وتُحفظ.
- استدعِ
POST /v1/live/sessions/:id/token. تتضمن الاستجابةtokenوtoken_expires_atوwebsocket_url، وهي الحقول نفسها عند إنشاء الجلسة. يفشل الطلب بالرمز409 session_endedبعد انتهاء الجلسة. - افتح WebSocket جديدا بهذا الرمز المميز.
- يصل
session.startedمعresumed: true. القيمةoffset_msهي كمية الصوت التي عالجتها الجلسة: تابع الصوت من تلك النقطة. تستمر معرفات السطور.
تُحفظ السطور التي كانت نهائية. أما الجزء الجاري لحظة الانقطاع فيضيع، لذا أعد إرسال بضع ثوان من الصوت ابتداء من offset_ms إذا كنت تخزنه مؤقتا. لا تُحتسب فترات إعادة الاتصال.
الاستبدال هو الاستدعاء نفسه. إذا ظن الخادم أن مقبسا لا يزال مفتوحا، فإن اتصالا صالحا جديدا يحل محله، ويُغلق المقبس القديم بالرمز 4010 بعد حفظ سطوره. لا يُمنع الاستئناف أبدا بسبب حد الجلسات المتزامنة.
يفعل SDK كل ذلك نيابة عنك: عند الانقطاع أو حدث reconnect يحصل على رمز مميز ويعيد الاتصال بمهلة تتدرج من 0.5 إلى 8 ثوان لمدة تصل إلى دقيقتين، ويعيد إرسال آخر 10 ثوان من الصوت الذي أرسلته خلال الانقطاع.
الفوترة
يسحب التفريغ المباشر من رصيد الدقائق نفسه الذي تسحب منه التفريغات العادية.
- يُحتسب وقت الاتصال، من لحظة قبول المقبس حتى إغلاقه. يُحتسب الصمت والفيديو المتوقف مؤقتا. أما الوقت بين الانقطاع والاستئناف فلا يُحتسب.
- لكل دقيقة بدأت، بحد أدنى 1. تُحتسب الدقيقة الأولى عند قبول أول اتصال، وكل دقيقة تالية عند بدايتها.
- الأسرع من الزمن الحقيقي يُحتسب بمدة الصوت. إذا كان الصوت الذي ترسله أطول من وقت الاتصال فتُحتسب مدة الصوت بدلا منه.
- تُقرأ نسبة الاحتساب من API. تكلّف الدقيقة التي بدأت
rateمن دقائق الرصيد، مع التقريب إلى الأعلى. النسبة عادة 1 ويمكن أن تحتوي على كسور. اقرأ النسبة المباشرة منrates.liveفيGET /v1/account، ومنrateفيGET /v1/live/capabilities، ومنrateفي الجلسة التي تنشئها. تبقى نسبة الجلسة ثابتة بعد إنشائها. - الترجمة مجانية.
- عدم الاتصال يعني عدم الاحتساب. الجلسة التي لا يُتصل بها خلال 5 دقائق تنتهي بالسبب
not_connected. - تُردّ الرسوم عند الفشل. إذا انتهت جلسة بالسبب
upstream_errorأوinternal_errorقبل أن تنتج سطرا واحدا، تُرد كل الرسوم. أما الجلسة الفارغة بسبب الصمت فتُحتسب وتُحفظ فارغة. - عند نفاد الرصيد وكان الشحن التلقائي مفعلا، يخصم transcript1 من بطاقتك المحفوظة بينما تواصل الجلسة البث، وتستمر الجلسة. وإذا كان الشحن التلقائي متوقفا أو معلقا، أو فشل الخصم، تُنهى الجلسة وتُحفظ وتنتهي بالسبب
balance_exhaustedعند حد الدقيقة. يسبق ذلك تحذيرbalance_low، إلا إذا كان الشحن التلقائي مفعلا.
يعيد إنشاء الجلسة 402 insufficient_minutes عندما يقل رصيدك القابل للاستخدام عن دقيقة واحدة بدأت بالنسبة الحالية. وإذا كان الشحن التلقائي مفعلا، يحاول transcript1 الشحن أولا. راجع الفوترة وحدود الاستخدام.
الحدود
| Free | Monthly | Annual | |
|---|---|---|---|
| الجلسات المباشرة المتزامنة | 1 | 2 | 3 |
- تُحتسب الجلسة ما دامت في الحالة
waitingأوconnectedأوdisconnected(ضمن مهلة الدقيقتين). عند تجاوز الحد يعيدPOST /v1/live/sessionsالرمز429 too_many_live_sessionsمعRetry-After: 30. - لا تُحتسب الجلسات المباشرة ضمن حد المهام النشطة.
- تستمر الجلسة 300 دقيقة من وقت الاتصال كحد أقصى. ثم تنتهي بالسبب
max_duration. - الجلسة التي لا صوت فيها لمدة 5 دقائق تنتهي بالسبب
idle_timeout؛ وتصلكidle_timeout_soonبعد 4 دقائق. يظل البث المتوقف مؤقتا يحتسب وقت اتصال ويُعد خاملا، لذا أنهِ الجلسة إذا طال التوقف. - يحدّ transcript1 جميع الجلسات المباشرة معا. عندما تكون كلها مستخدمة يعيد
POST /v1/live/sessionsالرمز503 live_capacity، وهو قابل لإعادة المحاولة. انتظرRetry-After(30 ثانية) ثم حاول مجددا. يُفحص هذا الحد عند إنشاء الجلسة فقط، ولا يُفحص في منتصفها أبدا. - إنشاء الجلسات والحصول على الرموز المميزة يُحتسبان ضمن حد الطلبات في الدقيقة.
- الصوت بصيغة PCM خام فقط:
pcm_s16le، أحادي القناة، من 8000 إلى 48000 Hz.
الإمكانات
يصف GET /v1/live/capabilities ما يدعمه التفريغ المباشر الآن، حتى لا تضطر العملاء إلى تثبيت قواعد المزود في الشيفرة. النطاق المطلوب هو transcriptions:read.
{
"available": true,
"auto_language": true,
"languages": ["en", "es"],
"translation": {
"targets": ["es", "de"],
"pairs": "any",
"requires_language": false
},
"diarize": true,
"audio_formats": ["pcm_s16le"],
"sample_rate": { "min": 8000, "max": 48000 },
"max_session_minutes": 300,
"rate": 1
}القوائم هنا مختصرة. اقرأها كاملة من API.
- القيمة
translation.pairsهي"any"(أي لغة مكتشفة إلى أي هدف)، أو قائمة كائنات فيهاfromوto. - القيمة
translation.requires_languageتكونtrueعندما تحتاج الترجمة إلىlanguageصريحة بدلauto. - عندما لا يكون التفريغ المباشر مهيأ تكون الاستجابة
available: falseمعrateفقط، ويعيد إنشاء الجلسة503 live_unavailable.
مع SDK: await t1.live.capabilities().
قراءة جلسة
يعيد GET /v1/live/sessions/:id حالة جلسة. النطاق المطلوب هو transcriptions:read.
curl https://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
"status": "ended",
"end_reason": "client_ended",
"transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
"options": {
"language": "en",
"translate_to": "es",
"diarize": false,
"audio_format": "pcm_s16le",
"sample_rate": 16000,
"save": true
},
"connected_ms": 142500,
"audio_ms": 141900,
"minutes_charged": 3,
"rate": 1,
"created_at": "2026-11-02T11:58:12Z",
"ended_at": "2026-11-02T12:00:35Z"
}قيمة status هي waiting أو connected أو disconnected أو ended. وتبقى end_reason هي null حتى تنتهي الجلسة. مع SDK: await t1.live.get(id).
الأخطاء
تستخدم أخطاء الطلبات بنية الخطأ المعتادة. هذه هي الأخطاء التي يعيدها POST /v1/live/sessions ونقاط النهاية المرتبطة به:
| حالة HTTP | code | قابل لإعادة المحاولة | متى |
|---|---|---|---|
| 422 | invalid_request | لا | حقل ناقص أو غير صالح، مثل sample_rate خارج النطاق من 8000 إلى 48000. |
| 422 | unsupported_language | لا | قيمة language غير مدعومة في التفريغ المباشر. |
| 422 | unsupported_translation | لا | لغة الهدف، أو زوج اللغتين، غير مدعوم. |
| 402 | insufficient_minutes | لا | رصيدك القابل للاستخدام أقل من دقيقة واحدة بدأت بالنسبة الحالية. |
| 403 | email_not_verified | لا | أكد بريدك الإلكتروني أولا. |
| 429 | too_many_live_sessions | نعم | استُخدمت جلسات باقتك المباشرة المتزامنة كلها. انتظر Retry-After. |
| 503 | live_capacity | نعم | السعة المباشرة كلها مستخدمة. انتظر Retry-After (30 ثانية). |
| 503 | live_unavailable | لا | التفريغ المباشر غير متاح الآن. |
| 409 | session_ended | لا | POST /v1/live/sessions/:id/token لجلسة انتهت. |
تصل مشكلات المقبس على هيئة أحداث error ورموز إغلاق.
النصوص المحفوظة
مع save: true (الافتراضي) تُحفظ الجلسة تفريغا عاديا عند انتهائها. ولا يُخزَّن الصوت أبدا.
- يُعاد
transcription_idعند إنشاء الجلسة. حالتهliveأثناء الجلسة وcompletedبعد انتهائها. والجلسة التي لم يُتصل بها تترك تفريغا بالحالةcanceled. - للتفريغ
source: { "platform": "live", "session_id": "lv_…" }. لا رابط ولا ملف. وتعرضه لوحة التحكم بوصفه جلسة مباشرة. - يصبح كل سطر مقطعا واحدا، له الفهرس نفسه الخاص بـ
idالسطر. اقرأ النص المفرغ بصيغة JSON أو نص أو SRT أو VTT كأي تفريغ آخر: راجع مرجع API. - تُحفظ الترجمة المباشرة معه. اقرأها بالمعامل
translationكما هو موضح في الترجمة. - أضف لغات ترجمة أخرى لاحقا عبر
POST /v1/transcriptions/:id/translationsبعد أن تصبح حالة التفريغcompleted. راجع الترجمة.
مع save: false لا يُحتفظ بشيء بعد انتهاء الجلسة: لا يوجد تفريغ وتكون قيمة transcription_id هي null. وتستلم كل سطر عبر المقبس وتنطبق الرسوم المعتادة.
الأمان
- الرمز المميز الذي تحصل عليه عند إنشاء الجلسة يُستخدم مرة واحدة وتنتهي صلاحيته بعد 60 ثانية. يُرسل بوصفه بروتوكولا فرعيا لـ WebSocket لا في عنوان URL، فلا يدخل سجلات الوصول. والرمز المسرّب عديم الفائدة بعد استخدامه أو انتهاء صلاحيته.
- استخدم مفتاح API من خادم تتحكم فيه فقط. لا تضع مفتاح API في صفحة ويب أو امتداد أبدا.
- في المتصفح أو الامتداد، سجّل دخول المستخدمين عبر OAuth ومرر رمز وصول OAuth إلى
getAccessTokenفيcreateLiveClient. يحتاج الرمز إلى النطاقtranscriptions:writeلبدء الجلسات. تنتهي صلاحية رموز وصول OAuth بعد ساعة واحدة، وكل استئناف يطلب رمزا جديدا منgetAccessToken. - يمكن لخادمك أيضا إنشاء الجلسة بمفتاح API وتسليم
websocket_urlوtokenفقط إلى المتصفح. عندها يتصل العميل مباشرة. ولا يستطيع الاستئناف دون رمز مميز جديد منك.
تاريخ التحديث 3 نوفمبر 2026