# التفريغ المباشر

> أرسل صوت الميكروفون أو علامة التبويب إلى transcript1 عبر WebSocket، واستلم سطور الترجمة النصية مع ترجمة فورية اختيارية أثناء حديث الناس.

Canonical: https://transcript1.com/ar/docs/live
Language: ar
Last updated: 2026-11-03

يحوّل التفريغ المباشر تدفق الصوت إلى سطور ترجمة نصية بينما لا يزال الناس يتحدثون. يرسل عميلك صوت PCM خام عبر WebSocket، فيعيد transcript1 سطورا تبدأ تخمينا مؤقتا ثم تصبح نهائية. أضف `translate_to` لتحمل كل سطر ترجمته أيضا. وعند انتهاء الجلسة تُحفظ النتيجة تفريغا عاديا.

لا توجد باقات منفصلة للتفريغ المباشر: فهو يستخدم رصيد الدقائق نفسه الذي تستخدمه بقية الميزات. راجع [الفوترة](https://transcript1.com/ar/docs/live#billing) لمعرفة كيف يُحتسب وقت الاتصال.

## متى تستخدم التفريغ المباشر

| استخدم التفريغ المباشر عندما | استخدم [التفريغ العادي](https://transcript1.com/ar/docs/getting-started) عندما |
|---|---|
| لا يزال الصوت قيد الإنتاج: مكالمة أو محاضرة أو بث أو فيديو تشاهده. | لديك رابط أو ملف جاهز. |
| تحتاج إلى الترجمة النصية خلال ثانية أو ثانيتين من الكلام. | تريد أعلى دقة ويمكنك انتظار النتيجة. |
| تريد ترجمة فورية بجانب كل سطر. | تريد ترجمة نص مفرغ مكتمل، أو الترجمة إلى عدة لغات. |

يقبل التفريغ المباشر الصوت فقط، بصيغة PCM خام. ولا يقبل روابط الفيديو ولا الملفات المرفوعة ولا WebM أو Opus أو MP3. التقط الصوت في عميلك وحوّله إلى PCM أولا. يوضح قسم [التقاط الصوت في المتصفح](https://transcript1.com/ar/docs/live#capture-audio-in-a-browser) الطريقة.

## بداية سريعة مع SDK

ثبّت الإصدار 1.5.0 أو أحدث من SDK: `npm i @transcript1/sdk@latest`. تنشئ `t1.live.start` الجلسة وتتصل، وتكتمل بعد أن تبدأ الجلسة، فيمكنك إرسال الصوت فورا.

```javascript
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](https://transcript1.com/ar/docs/sdk#live-transcription) لكل الدوال والأحداث.

## بداية سريعة مع WebSocket مباشر

بدون SDK هناك خطوتان: أنشئ جلسة عبر HTTPS، ثم افتح WebSocket بالرمز المميز ذي الاستخدام الواحد الذي عاد إليك.

### إنشاء جلسة

```bash
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`:

```json
{
  "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` عدد دقائق الرصيد المحتسبة لكل دقيقة بدأت في هذه الجلسة؛ راجع [الفوترة](https://transcript1.com/ar/docs/live#billing).

يقبل جسم الطلب الحقول التالية. كلها اختيارية، والنطاق المطلوب هو `transcriptions:write`:

| الحقل | الافتراضي | القاعدة |
|---|---|---|
| `language` | `auto` | `auto`، أو رمز من حرفين تدعمه ميزة التفريغ المباشر. |
| `translate_to` | لا يوجد | رمز من حرفين. لغة هدف واحدة لكل جلسة. راجع [الترجمة المباشرة](https://transcript1.com/ar/docs/live#translate-live). |
| `diarize` | `false` | يضع تسميات للمتحدثين. بعض الإعدادات لا تستطيع تمييز المتحدثين، مثل استخدامه مع `language: "auto"`، وتجيب بالرمز `422`. راجع [الإمكانات](https://transcript1.com/ar/docs/live#capabilities). |
| `audio_format` | `pcm_s16le` | القيمة الوحيدة. الحقل موجود لإضافة صيغ أخرى لاحقا. |
| `sample_rate` | `16000` | عدد صحيح من 8000 إلى 48000. أحادي القناة فقط. |
| `save` | `true` | القيمة `false` لا تحتفظ بشيء بعد انتهاء الجلسة. راجع [النصوص المحفوظة](https://transcript1.com/ar/docs/live#saved-transcripts). |

### الاتصال والبث

افتح WebSocket مع الرمز المميز بوصفه البروتوكول الفرعي الثاني. يعمل هذا في المتصفحات وفي Node 22+ دون ترويسات مخصصة. انتظر `session.started` قبل إرسال الصوت: تُهمل الإطارات الثنائية المرسلة قبله، وتصلك رسالة تحذير `audio_dropped`.

```javascript
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 العام. راجع [رموز الإغلاق](https://transcript1.com/ar/docs/live#close-codes).

## التقاط الصوت في المتصفح

يجب أن يكون صوت التفريغ المباشر PCM أحادي القناة بعينات 16 بت موقّعة بترتيب little-endian وبمعدل `sample_rate` الخاص بالجلسة. تعطيك المتصفحات عينات عشرية، لذا حوّلها داخل `AudioWorklet`. يلتقط المثال التالي الميكروفون بصيغة PCM بتردد 16 kHz في مقاطع مدة كل منها 100 ms ويرسله عبر مدخل SDK الخاص بالمتصفح. تعيد `getAccessToken` رمز وصول OAuth؛ لا تضع مفتاح API في المتصفح أبدا. راجع [الأمان](https://transcript1.com/ar/docs/live#security).

```javascript
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 كل سطر أثناء نطقه.

- لغة هدف واحدة لكل جلسة. أضف لغات أخرى إلى النص المحفوظ لاحقا عبر [نقطة نهاية الترجمة](https://transcript1.com/ar/docs/live#saved-transcripts).
- لكل `line` نص أصلي (`text` و`final`) وكائن `translation` (`language` و`text` و`final`). تكون `translation` هي `null` عندما لا تُضبط `translate_to`.
- يصبح الأصل والترجمة نهائيين بشكل مستقل: قد يكون للسطر أصل نهائي بينما لا تزال ترجمته تتغير. اعرض السطر نهائيا عندما يصبح الاثنان نهائيين.
- السطر الذي هو أصلا باللغة الهدف يستخدم نصه نفسه ترجمة له، ويصبح نهائيا عند إغلاق السطر.
- يُغلق السطر عندما يتوقف المتحدث، أو يتغير المتحدث، أو يتجاوز الفاصل بين الكلمات 0.9 ثانية، أو يبلغ السطر صفين من الترجمة النصية (84 حرفا) أو 15 ثانية، أو ترسل `finalize`. مع تفعيل الترجمة قد يستغرق إغلاق السطر نحو 3 ثوان إضافية أثناء وصول الترجمة. في هذه الحالة قد تبقى الترجمة مفتوحة (`final: false`) وتستمر في النمو بعد أن يصبح الأصل نهائيا.
- الترجمة مجانية. لا تستهلك دقائق.

تعتمد اللغات الهدف وأزواج اللغات المتاحة على مزود الكلام المباشر. اقرأ [الإمكانات](https://transcript1.com/ar/docs/live#capabilities) بدل تثبيت القواعد في الشيفرة. تعيد لغة الهدف غير المدعومة `422 unsupported_translation`. على سبيل المثال، قد يشترط المزود `language` صريحة بدل `auto`، أو يسمح فقط بالأزواج التي تتضمن الإنجليزية.

## بروتوكول WebSocket

### الاتصال

```javascript
new WebSocket(websocket_url, ['t1-live', token]);
```

ينتقل الرمز المميز بوصفه بروتوكولا فرعيا لـ WebSocket. لا يظهر أبدا في عنوان URL ولا في سجل الوصول. لا ترسل ترويسة `Authorization`: الرمز المميز هو بيانات الاعتماد. يرسل الخادم ping كل 30 ثانية؛ وتجيب المتصفحات وحزمة `ws` تلقائيا. يُقطع المقبس الذي لا يرسل pong خلال 60 ثانية.

الاتصال الصالح بجلسة لها مقبس بالفعل يحل محل المقبس القديم. راجع [الاستئناف والاستبدال](https://transcript1.com/ar/docs/live#resume-and-takeover).

### رسائل العميل

| الرسالة | المعنى |
|---|---|
| إطار ثنائي | الصوت: PCM s16le أحادي القناة بمعدل `sample_rate` الخاص بالجلسة. يجب أن يكون طول كل إطار زوجيا وبحد أقصى 256 KB. نحو 100 ms لكل إطار حجم مناسب. |
| `{"type":"finalize"}` | يغلق السطر الحالي الآن، مثلا عند إيقاف الفيديو مؤقتا. |
| `{"type":"end"}` | يُنهي الجلسة ويحفظها. يجيب الخادم بـ `session.ended` ثم يغلق المقبس. |
| أي شيء آخر | حدث `error` غير فادح بالرمز `invalid_message`. |

الإطار ذو الطول الفردي أو الكبير أكثر من اللازم يعد مخالفة للبروتوكول ويغلق المقبس بالرمز `4008`.

ابدأ إرسال الصوت بعد `session.started`. العميل الذي يرسل الصوت أسرع من الزمن الحقيقي يُحاسب على مدة الصوت بدل وقت الاتصال (راجع [الفوترة](https://transcript1.com/ar/docs/live#billing))، لذا أرسل الصوت بالسرعة التي يُنتج بها.

### أحداث الخادم

كل حدث هو إطار نصي JSON له `type`.

| الحدث | يُرسل عندما |
|---|---|
| `session.started` | قُبل الاتصال. أرسل الصوت من الآن. |
| `line` | أُنشئ سطر ترجمة أو تغيّر. |
| `usage` | بعد كل عملية احتساب. |
| `warning` | هناك أمر يحتاج انتباهك لكن الجلسة تستمر. |
| `error` | مشكلة غير فادحة، أو سبب إغلاق فوري. |
| `reconnect` | الخادم على وشك إعادة التشغيل. استأنف الجلسة. |
| `session.ended` | انتهت الجلسة وحُفظت. آخر حدث قبل الإغلاق العادي. |

يوضح `session.started` هل هذا استئناف وكم من الصوت عالجته الجلسة حتى الآن:

```json
{
  "type": "session.started",
  "session_id": "lv_8fK2Lm0PqRsTuVwXyZaBcD",
  "transcription_id": "tr_3NcQvXy7BtHjKd5ErFgLmA",
  "resumed": false,
  "offset_ms": 0
}
```

يحمل `line` الحالة الكاملة الحالية لسطر واحد، لذا استبدل السطر الذي لديك بالمعرف `id` نفسه. راجع [كائن السطر](https://transcript1.com/ar/docs/live#the-line-object).

```json
{
  "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` بعد كل عملية احتساب، ومعه وقت الاتصال ومدة الصوت ودقائق الرصيد المحتسبة حتى الآن والدقائق المتبقية لديك:

```json
{
  "type": "usage",
  "connected_ms": 61240,
  "audio_ms": 60100,
  "minutes_charged": 2,
  "minutes_remaining": 898
}
```

يحمل `warning` قيمة `code`:

| `code` | المعنى |
|---|---|
| `balance_low` | ستفشل عملية الاحتساب التالية. تنتهي الجلسة بالسبب `balance_exhausted` عند حلول موعدها. لا يُرسل أثناء تفعيل [الشحن التلقائي](https://transcript1.com/ar/docs/billing-and-limits#auto-top-up). |
| `audio_dropped` | وصل الصوت قبل أن تكون الجلسة جاهزة، أو وصل صوت كثير أثناء اتصال مزود الكلام. أُهمل أقدم صوت. |
| `idle_timeout_soon` | لا صوت منذ 4 دقائق. تنتهي الجلسة بعد 5 دقائق دون صوت. أرسل الصوت لإبقائها. |

```json
{ "type": "warning", "code": "idle_timeout_soon" }
```

يحمل `error` قيمتي `code` و`message`. يمكن أن تصل هذه الرموز على مقبس مباشر:

| `code` | فادح | المعنى |
|---|---|---|
| `invalid_message` | لا | إطار نصي ليس `finalize` ولا `end`. |
| `invalid_token` | نعم | الرمز المميز غير صالح أو منتهي أو مستخدم من قبل. يُغلق المقبس بالرمز `4001`. |
| `not_found` | نعم | الجلسة غير موجودة. يُغلق المقبس بالرمز `4004`. |
| `session_ended` | نعم | انتهت الجلسة بالفعل. يُغلق المقبس بالرمز `4004`. |

```json
{ "type": "error", "code": "invalid_message", "message": "Expected {\"type\":\"finalize\"} or {\"type\":\"end\"}" }
```

يصل `reconnect` قبل أن يغلق الخادم المقبس بالرمز `1012`، مثلا أثناء النشر. احصل على رمز مميز جديد واستأنف الجلسة.

```json
{ "type": "reconnect", "reason": "server_restart" }
```

`session.ended` هو آخر حدث في كل جلسة. الأرقام هي الإجماليات النهائية:

```json
{
  "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` وتُحفظ.

1. استدعِ `POST /v1/live/sessions/:id/token`. تتضمن الاستجابة `token` و`token_expires_at` و`websocket_url`، وهي الحقول نفسها عند إنشاء الجلسة. يفشل الطلب بالرمز `409 session_ended` بعد انتهاء الجلسة.
2. افتح WebSocket جديدا بهذا الرمز المميز.
3. يصل `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` قبل أن تنتج سطرا واحدا، تُرد كل الرسوم. أما الجلسة الفارغة بسبب الصمت فتُحتسب وتُحفظ فارغة.
- **عند نفاد الرصيد** وكان [الشحن التلقائي](https://transcript1.com/ar/docs/billing-and-limits#auto-top-up) مفعلا، يخصم transcript1 من بطاقتك المحفوظة بينما تواصل الجلسة البث، وتستمر الجلسة. وإذا كان الشحن التلقائي متوقفا أو معلقا، أو فشل الخصم، تُنهى الجلسة وتُحفظ وتنتهي بالسبب `balance_exhausted` عند حد الدقيقة. يسبق ذلك تحذير `balance_low`، إلا إذا كان الشحن التلقائي مفعلا.

يعيد إنشاء الجلسة `402 insufficient_minutes` عندما يقل رصيدك القابل للاستخدام عن دقيقة واحدة بدأت بالنسبة الحالية. وإذا كان الشحن التلقائي مفعلا، يحاول transcript1 الشحن أولا. راجع [الفوترة وحدود الاستخدام](https://transcript1.com/ar/docs/billing-and-limits#live-transcription).

## الحدود

| | 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`.

```json
{
  "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`.

```bash
curl https://api.transcript1.com/v1/live/sessions/lv_8fK2Lm0PqRsTuVwXyZaBcD \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
```

```json
{
  "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)`.

## الأخطاء

تستخدم أخطاء الطلبات [بنية الخطأ](https://transcript1.com/ar/docs/errors) المعتادة. هذه هي الأخطاء التي يعيدها `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` و[رموز إغلاق](https://transcript1.com/ar/docs/live#close-codes).

## النصوص المحفوظة

مع `save: true` (الافتراضي) تُحفظ الجلسة تفريغا عاديا عند انتهائها. ولا يُخزَّن الصوت أبدا.

- يُعاد `transcription_id` عند إنشاء الجلسة. حالته `live` أثناء الجلسة و`completed` بعد انتهائها. والجلسة التي لم يُتصل بها تترك تفريغا بالحالة `canceled`.
- للتفريغ `source: { "platform": "live", "session_id": "lv_…" }`. لا رابط ولا ملف. وتعرضه لوحة التحكم بوصفه جلسة مباشرة.
- يصبح كل سطر مقطعا واحدا، له الفهرس نفسه الخاص بـ `id` السطر. اقرأ النص المفرغ بصيغة JSON أو نص أو SRT أو VTT كأي تفريغ آخر: راجع [مرجع API](https://transcript1.com/ar/docs/api-reference).
- تُحفظ الترجمة المباشرة معه. اقرأها بالمعامل `translation` كما هو موضح في [الترجمة](https://transcript1.com/ar/docs/translation#read-the-translation).
- أضف لغات ترجمة أخرى لاحقا عبر `POST /v1/transcriptions/:id/translations` بعد أن تصبح حالة التفريغ `completed`. راجع [الترجمة](https://transcript1.com/ar/docs/translation#translate-an-existing-transcript).

مع `save: false` لا يُحتفظ بشيء بعد انتهاء الجلسة: لا يوجد تفريغ وتكون قيمة `transcription_id` هي `null`. وتستلم كل سطر عبر المقبس وتنطبق الرسوم المعتادة.

## الأمان

- الرمز المميز الذي تحصل عليه عند إنشاء الجلسة يُستخدم مرة واحدة وتنتهي صلاحيته بعد 60 ثانية. يُرسل بوصفه بروتوكولا فرعيا لـ WebSocket لا في عنوان URL، فلا يدخل سجلات الوصول. والرمز المسرّب عديم الفائدة بعد استخدامه أو انتهاء صلاحيته.
- استخدم مفتاح API من خادم تتحكم فيه فقط. لا تضع مفتاح API في صفحة ويب أو امتداد أبدا.
- في المتصفح أو الامتداد، سجّل دخول المستخدمين عبر OAuth ومرر رمز وصول OAuth إلى `getAccessToken` في `createLiveClient`. يحتاج الرمز إلى النطاق `transcriptions:write` لبدء الجلسات. تنتهي صلاحية رموز وصول OAuth بعد ساعة واحدة، وكل استئناف يطلب رمزا جديدا من `getAccessToken`.
- يمكن لخادمك أيضا إنشاء الجلسة بمفتاح API وتسليم `websocket_url` و`token` فقط إلى المتصفح. عندها يتصل العميل مباشرة. ولا يستطيع الاستئناف دون رمز مميز جديد منك.
