---
title: البدء السريع
icon: Rocket
description: ثبّت SDK 0.18.0، وأكمل أول نسخ دفعي، ثم اختر التسليم السريع أو الفوري.
---

أقصر مسار إلى أول نتيجة هو **النسخ الدفعي** لملف صوت مكتمل. فهو يقبل حاوية
صوت مدعومة ولا يتطلب تحضير مقاطع PCM فورية. أكمل هذا المسار أولًا، ثم اختر
التسليم السريع أو الفوري إذا احتاجه منتجك.

## قبل أن تبدأ

جهّز ما يلي قبل تشغيل أي أمر:

- مفتاح API ومسار Socket.IO تحصل عليهما عبر [مسار الوصول](/ar/authentication) في مؤسستك. تعرض
  الصفحة عنوان الخدمة المهيأ لبيئتها.
- ملف صوت مكتمل ومدعوم. تستخدم الأمثلة أدناه `meeting.wav` وتكتب التسميات
  التوضيحية في `meeting.vtt`.
- إحدى بيئات تشغيل SDK المدعومة:
  - JavaScript / TypeScript: بيئة Node.js أو Bun على الخادم تدعم ES2021 و
    `fetch` و`FormData` و`Blob`. لا ينشر SDK حدًا أدنى لإصدار Node.js أو Bun.
    تُفحص الأمثلة باستخدام Node.js 24 وBun 1.3.14؛ وتفترض أوامر `node`
    المباشرة أدناه بيئة التحقق Node.js 24 تلك.
  - Python 3.10 أو أحدث.
- `ffmpeg` فقط إذا كنت ستجرب مسار البث الفوري الاختياري.

احتفظ بمفتاح API في إعدادات الخادم. لا تضعه في شيفرة المتصفح أو الجوال.

## 1. ثبّت SDK 0.18.0

اختر لغة واحدة. تستخدم الصفحة تسميات تبويبي JavaScript وPython نفسها في كل
بديل.

<CodeBlockTabs defaultValue="JavaScript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="JavaScript">JavaScript / TypeScript</CodeBlockTabsTrigger>
    <CodeBlockTabsTrigger value="Python">Python</CodeBlockTabsTrigger>
  </CodeBlockTabsList>
  <CodeBlockTab value="JavaScript">

```bash
npm install @humain-voice/sdk@0.18.0
```

  </CodeBlockTab>
  <CodeBlockTab value="Python">

```bash
python -m pip install humain-voice==0.18.0
```

  </CodeBlockTab>
</CodeBlockTabs>

**النتيجة المتوقعة:** يكمل مدير الحزم بنجاح ويسجل إصدار SDK الدقيق
`0.18.0`.

## 2. اضبط البيئة

شغّل أوامر التصدير هذه في الصدفة نفسها التي ستشغّل المثال. استبدل المفتاح
بالقيمة المهيأة لمؤسستك.

```bash
export API_URL="https://api.voice.humain.com"
export API_PATH="/socket.io"
export API_KEY="YOUR_API_KEY"
export API_VERSION="v1"

test -n "$API_URL" && test -n "$API_PATH" && test -n "$API_KEY" && echo "HUMAIN Voice environment ready"
```

**النتيجة المتوقعة:** يطبع الأمر الأخير `HUMAIN Voice environment ready`.

تنشر هذه البيئة Socket.IO على المسار `/socket.io`. يستخدم كل عميل Socket.IO
في الإصدار `0.18.0` المسار `/socket.io` افتراضيًا.
اضبط `API_PATH` فقط عندما يحتاج النشر إلى مسار مخصص. تتطلب نقطة النهاية
القديمة `sautech.humain.com` المسار `/realtime/socket.io`. يستخدم عميل Batch
القيم `API_URL` و`API_KEY` و`API_VERSION` فقط.

## 3. نفّذ نسخًا دفعيًا

استخدم زر نسخ كتلة الشيفرة واحفظ المثال المحدد باسم الملف المعروض. يرسل
`meeting.wav`، ويستعلم بمهلة خمس دقائق، ويفعّل تمييز المتحدثين، ويطبع النص
المعاد، ويكتب تسميات WebVTT.

<CodeBlockTabs defaultValue="JavaScript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="JavaScript">JavaScript / TypeScript</CodeBlockTabsTrigger>
    <CodeBlockTabsTrigger value="Python">Python</CodeBlockTabsTrigger>
  </CodeBlockTabsList>
  <CodeBlockTab value="JavaScript">

```ts
import { readFile, writeFile } from 'node:fs/promises';

import {
  BatchDiarization,
  BatchTranscribeClient,
  BatchTranscriptionModel,
  Language,
  Subtitles,
} from '@humain-voice/sdk';

function requiredEnv(name: 'API_KEY' | 'API_URL'): string {
  const value = process.env[name];
  if (!value) throw new Error(`${name} is required`);
  return value;
}

async function main(): Promise<void> {
  const inputPath = process.argv[2] ?? 'meeting.wav';
  const outputPath = process.argv[3] ?? 'meeting.vtt';
  const client = new BatchTranscribeClient({
    api_url: requiredEnv('API_URL'),
    api_key: requiredEnv('API_KEY'),
    api_version: process.env.API_VERSION ?? 'v1',
  });

  try {
    const result = await client.transcribe(
      await readFile(inputPath),
      Language.ArEn,
      {
        asr: BatchTranscriptionModel.BayanArEn,
        diarization: BatchDiarization.On,
        saveResult: true,
        pollInterval: 2,
        timeout: 300,
        onProgress: ({ status }) => console.info('status:', status),
      },
    );

    console.info(result.results?.transcript ?? '');
    await writeFile(outputPath, Subtitles.fromResponse(result).toVtt(), 'utf8');
  } finally {
    await client.close();
  }
}

void main().catch((error: unknown) => {
  console.error(error);
  process.exitCode = 1;
});
```

  </CodeBlockTab>
  <CodeBlockTab value="Python">

```python
from __future__ import annotations

import asyncio
import os
import sys
from pathlib import Path

from humain_voice import stt
from humain_voice.stt.batchtranscription import BatchDiarization


def required_env(name: str) -> str:
    value = os.environ.get(name)
    if not value:
        raise RuntimeError(f"{name} is required")
    return value


async def main() -> None:
    input_path = Path(sys.argv[1] if len(sys.argv) > 1 else "meeting.wav")
    output_path = Path(sys.argv[2] if len(sys.argv) > 2 else "meeting.vtt")

    async with stt.BatchTranscribeClient(
        api_url=required_env("API_URL"),
        api_key=required_env("API_KEY"),
        api_version=os.environ.get("API_VERSION", "v1"),
    ) as client:
        result = await client.transcribe(
            input_path,
            lang=stt.Language.ArEn,
            asr=stt.BatchTranscriptionModel.BayanArEn,
            diarization=BatchDiarization.On,
            save_result=True,
            poll_interval=2.0,
            timeout_seconds=300.0,
            on_progress=lambda response: print("status:", response.status.value),
        )

    print(result.results.transcript if result.results else "")
    output_path.write_text(
        stt.Subtitles.from_response(result).to_vtt(),
        encoding="utf-8",
    )


if __name__ == "__main__":
    asyncio.run(main())
```

  </CodeBlockTab>
</CodeBlockTabs>

شغّل الأمر المطابق للملف الذي حفظته:

- JavaScript / TypeScript: `node batch-transcription.ts meeting.wav meeting.vtt`
- Python: `python batch_transcription.py meeting.wav meeting.vtt`

**النتيجة المتوقعة:** في العمل الناجح، تطبع الطرفية تحديثًا واحدًا أو أكثر
يبدأ بـ`status:` ثم النص المعاد، ويُنشأ `meeting.vtt`. قد ينتج الصوت الذي لا
يحتوي كلامًا متعرفًا عليه نصًا فارغًا.

<Callout type="info">
يكتمل أول طلب HUMAIN Voice عندما تصل المهمة إلى `done` ويُكتب ملف التسميات.
</Callout>

## بعد النتيجة الأولى

- يستعلم المثال كل ثانيتين مع حد لحلقة الاستعلام قدره 300 ثانية. قد يمدد
  الإرسال أو طلب قيد التنفيذ المدة الفعلية. اختر مهلًا تناسب حملك؛ فمهلة الطلب
  ليست مهلة سير العمل الإجمالية.
- يعيد SDK `0.18.0` النتيجة عند `done` ويرفع خطأ عند `failed` أو انتهاء
  المهلة. لا يتوقف مساعد `transcribe()` بشكل خاص عند `cleared`، فتصل المهمة
  الممسوح إلى المهلة المضبوطة. يجب أن يتوقف المستعلم المباشر صراحة عند
  `done` و`failed` و`cleared`.
- يغلق المثال العميل حتى عند إخفاق الإرسال أو الاستعلام. حافظ على نمط التنظيف
  هذا في الإنتاج.

توسّع [وصفة نسخ تسجيل](/ar/recipes/transcribe-a-recording) أنماط الاستعلام،
وتسميات المتحدثين، والتسميات التوضيحية.

## اختر نمط التسليم التالي

| النمط | استخدمه عندما | تسليم الصوت | تدفق النتيجة |
|---|---|---|---|
| النسخ الدفعي | لديك تسجيل مكتمل، بما في ذلك الاجتماعات أو المكالمات أو حلقات البودكاست الأطول | ارفع مرة واحدة | استعلم عن المهمة حتى `done` أو `failed` أو `cleared` |
| النسخ السريع | تحتاج حمولة صوت مكتملة وقصيرة إلى زمن وصول أقل، مثل دور واحد في محادثة وكيل | أرسل الحمولة المكتملة مرة واحدة عبر Socket.IO | استقبل أحداث الرفع والنسخ حتى النتيجة النهائية |
| النسخ الفوري | ما زال الصوت يصل من ميكروفون أو مكالمة أو مصدر مباشر | أرسل مقاطع PCM16 little-endian بتردد 16 kHz وأحادية القناة | استبدل النص المؤقت حتى تصل استجابة نهائية أو نهاية كلام |

النسخ السريع ليس مسار الصوت الطويل أو البودكاست. استخدم Batch لهذه التسجيلات
المكتملة؛ واستخدم النسخ السريع عندما تكون الحمولة المكتملة قصيرة ويهم زمن
الوصول.

## اختياري: نفّذ نسخًا فوريًا

يجب أن يكون دخل Realtime مسبقًا بصيغة PCM16 little-endian خام، بتردد 16 kHz،
وأحادي القناة. حوّل تسجيلًا لهذا المثال:

```bash
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ar 16000 -ac 1 speech.pcm
```

**النتيجة المتوقعة:** ينتهي `ffmpeg` بنجاح وينشئ `speech.pcm`. لا يملك PCM
الخام ترويسة ملف قابلة للتشغيل.

احفظ المثال المحدد باسم الملف المعروض:

<CodeBlockTabs defaultValue="JavaScript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="JavaScript">JavaScript / TypeScript</CodeBlockTabsTrigger>
    <CodeBlockTabsTrigger value="Python">Python</CodeBlockTabsTrigger>
  </CodeBlockTabsList>
  <CodeBlockTab value="JavaScript">

```ts
import { readFile, writeFile } from 'node:fs/promises';

import {
  type ErrorResponse,
  Language,
  RealtimeClient,
  Subtitles,
  type WordSegment,
} from '@humain-voice/sdk';

const CHUNK_BYTES = 3_200; // 100 ms of PCM16LE, 16 kHz, mono audio.

function requiredEnv(name: 'API_KEY' | 'API_PATH' | 'API_URL'): string {
  const value = process.env[name];
  if (!value) throw new Error(`${name} is required`);
  return value;
}

async function pause(milliseconds: number): Promise<void> {
  await new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
}

async function main(): Promise<void> {
  const inputPath = process.argv[2] ?? 'speech.pcm';
  const outputPath = process.argv[3] ?? 'speech.vtt';
  const client = new RealtimeClient({
    api_url: requiredEnv('API_URL'),
    api_path: requiredEnv('API_PATH'),
    api_key: requiredEnv('API_KEY'),
  });
  const finalizedWords: WordSegment[] = [];
  let serverError: ErrorResponse | undefined;
  let protocolFinalObserved = false;

  try {
    const stream = await client.startStream(Language.ArEn, {
      onResponse: (response) => {
        const kind = response.is_final
          ? 'final'
          : response.is_speech_final
            ? 'speech-final'
            : 'partial';
        console.info(`${kind}:`, response.transcription);
        if (response.is_final) protocolFinalObserved = true;
        if (response.is_final || response.is_speech_final) {
          // The current public Realtime contract does not guarantee increasing
          // seq values, so collect final words in arrival order instead of
          // asking RealtimeSubtitles to deduplicate by id:seq.
          finalizedWords.push(...response.words);
        }
      },
      // The released SDK can invoke a stream handler more than once for one
      // routed error, so keep this callback idempotent.
      onError: (error) => {
        serverError = error;
      },
    });

    const pcm = await readFile(inputPath);
    for (let offset = 0; offset < pcm.length; offset += CHUNK_BYTES) {
      await stream.send(pcm.subarray(offset, offset + CHUNK_BYTES));
      await pause(100);
    }

    // close() sends the last frame and waits for protocol is_final, a routed
    // error, or this timeout. It resolves rather than throwing on timeout.
    await stream.close(5);
    if (serverError) {
      throw new Error(serverError.message ?? serverError.code ?? 'Realtime stream failed');
    }
    if (!protocolFinalObserved) {
      throw new Error('Realtime stream ended before protocol is_final');
    }
    await writeFile(outputPath, Subtitles.fromWords(finalizedWords).toVtt(), 'utf8');
  } finally {
    await client.disconnect();
  }
}

void main().catch((error: unknown) => {
  console.error(error);
  process.exitCode = 1;
});
```

  </CodeBlockTab>
  <CodeBlockTab value="Python">

```python
from __future__ import annotations

import asyncio
import os
import sys
from pathlib import Path

from humain_voice import stt


CHUNK_BYTES = 3_200  # 100 ms of PCM16LE, 16 kHz, mono audio.


def required_env(name: str) -> str:
    value = os.environ.get(name)
    if not value:
        raise RuntimeError(f"{name} is required")
    return value


async def main() -> None:
    input_path = Path(sys.argv[1] if len(sys.argv) > 1 else "speech.pcm")
    output_path = Path(sys.argv[2] if len(sys.argv) > 2 else "speech.vtt")
    finalized_words: list[stt.WordSegment] = []
    server_error: stt.ErrorResponse | None = None
    protocol_final_observed = False

    def handle_response(response: stt.RtTranscribeResponse) -> None:
        nonlocal protocol_final_observed
        if response.is_final:
            kind = "final"
        elif response.is_speech_final:
            kind = "speech-final"
        else:
            kind = "partial"
        print(f"{kind}:", response.transcription)
        if response.is_final:
            protocol_final_observed = True
        if response.is_final or response.is_speech_final:
            # The current public Realtime contract does not guarantee increasing
            # seq values, so collect final words in arrival order instead of
            # asking RealtimeSubtitles to deduplicate by id:seq.
            finalized_words.extend(response.words)

    def handle_error(error: stt.ErrorResponse | None) -> None:
        # The released SDK can invoke a stream handler more than once for one
        # routed error, so keep this callback idempotent.
        nonlocal server_error
        server_error = error

    client = stt.RealtimeClient(
        api_url=required_env("API_URL"),
        api_path=required_env("API_PATH"),
        api_key=required_env("API_KEY"),
    )
    async with client:
        stream = await client.start_stream(
            language=stt.Language.ArEn,
            on_response=handle_response,
            on_error=handle_error,
        )
        pcm = input_path.read_bytes()
        for offset in range(0, len(pcm), CHUNK_BYTES):
            await stream.send(pcm[offset : offset + CHUNK_BYTES])
            await asyncio.sleep(0.1)


        # close() sends the last frame and waits for protocol is_final, a routed
        # error, or this timeout. It returns rather than raising on timeout.
        await stream.close(timeout_seconds=5.0)

    if server_error is not None:
        raise RuntimeError(server_error.message or server_error.code or "Realtime stream failed")
    if not protocol_final_observed:
        raise RuntimeError("Realtime stream ended before protocol is_final")
    output_path.write_text(
        stt.Subtitles.from_words(finalized_words).to_vtt(),
        encoding="utf-8",
    )


if __name__ == "__main__":
    asyncio.run(main())
```

  </CodeBlockTab>
</CodeBlockTabs>

شغّل الأمر المطابق للملف الذي حفظته:

- JavaScript / TypeScript: `node realtime-transcription.ts speech.pcm speech.vtt`
- Python: `python realtime_transcription.py speech.pcm speech.vtt`

**النتيجة المتوقعة:** تصف الطرفية الاستجابات بـ`partial:` أو `final:` أو
`speech-final:`، ويكتب التدفق الناجح التسميات النهائية في `speech.vtt`.

بعد النجاح، أبقِ نص واجهة المستخدم المؤقت منفصلًا واستبدله عند وصول أحداث
النتائج. يجمع المثال الكلمات من الأحداث النهائية أو أحداث نهاية الكلام فقط، ثم
يستخدم `Subtitles` لعرضها؛ ولا يعتمد على `seq` لأن ترتيبها ليس جزءًا من عقد
Realtime السلكي الحالي. يغلق المثال التدفق، وينتظر حتى خمس ثوانٍ لوصول
`is_final` على مستوى البروتوكول، ويفصل العميل أثناء التنظيف. ويبلغ انتهاء
المهلة بوصفه عدم اكتمال بدلاً من كتابة ملف تسميات عادي.

## الخطوات التالية

تابع المسار الذي يطابق منتجك. قبل حركة الإنتاج، أعد تشغيله بمدخلات ممثلة
واختبر المهل والحالات النهائية والانقطاعات وإعادة المحاولة والتنظيف مهما كان
نمط التسليم.

<Cards>
  <Card href="/ar/recipes/transcribe-a-recording" title="تعمق في المسار الدفعي" description="أضف استعلامًا مباشرًا محدودًا، وتوفيق المتحدثين، وخرج التسميات." />
  <Card href="/ar/sdk" title="استخدم النسخ السريع" description="أرسل حمولة قصيرة ومكتملة عبر عميل SDK السريع." />
  <Card href="/ar/recipes/realtime-transcription" title="ابنِ النسخ الفوري" description="تعامل مع تأطير PCM، والنص المؤقت، والتسميات النهائية، والتنظيف." />
</Cards>
