فيديوهاتيالتوثيق

حزمة Laravel

حزمة ‎videohati/laravel‎ الرسمية — التثبيت والإعداد ورفع الفيديو وإنشاء جلسات التشغيل والتحقّق من Webhooks وتضمين المشغّل من تطبيق Laravel.

videohati/laravel هي حزمة PHP الرسمية لإطار Laravel. يسجّل مزوّد الخدمة وواجهة ‏Videohati تلقائيًا، والتحقّق من شهادة TLS مفعّل دائمًا، ولا تحتوي الاستثناءات على مفتاحك أبدًا. تتطلّب PHP الإصدار 8.2 فأحدث، وLaravel 10 أو 11. الإصدار ‏1.0.0-rc.0.

التثبيت

composer require videohati/laravel

اضبط الاعتمادات في .env:

VIDEOHATI_API_KEY=vh_live_...
VIDEOHATI_PROJECT_ID=01JZ9WV3N8GQ5T2M7K4C6XBARF

يضع مفتاح vh_test_... العميل مقابل بيانات الاختبار؛ ويتبع الوضع بادئة المفتاح دائمًا.

الإعداد

انشر ملف الإعداد لتغيير القيم الافتراضية:

php artisan vendor:publish --tag=videohati-config

يقرأ config/videohati.php متغيّرات البيئة الآتية:

المتغيّرالافتراضيماذا يضبط
VIDEOHATI_API_KEYمفتاح API للمشروع.
VIDEOHATI_BASE_URLhttps://api.videohati.comأصل واجهة API.
VIDEOHATI_PROJECT_IDالمشروع الافتراضي لموجّه @videohatiPlayer.
VIDEOHATI_WEBHOOK_SECRETسرّ نقطة النهاية لـ SignatureVerifier.
VIDEOHATI_PLAYER_URLhttps://cdn.videohati.com/player.jsرابط حزمة المشغّل للموجّه.
VIDEOHATI_TIMEOUT30مهلة HTTP بالثواني.

الواجهة وحقن التبعية

اصل إلى العميل عبر واجهة Videohati أو بحقن Videohati\Laravel\Client.

use Videohati\Laravel\Facades\Videohati;

$videos = Videohati::videos()->list(state: 'ready');
use Videohati\Laravel\Client;

class LessonController
{
    public function __construct(private readonly Client $videohati) {}

    public function show(string $videoId)
    {
        $video = $this->videohati->videos()->get($videoId);
        // ...
    }
}

يكشف العميل الدوال videos() وplayback() وwebhookEndpoints() وwebhooks() (مُتحقّق التوقيع).

الفيديوهات

تُعيد videos()->list() مجموعة Laravel Collection<VideoDto>.

الدالةالتوقيعماذا تفعل
listlist(?string $state, int $limit = 20, ?string $cursor)صفحة واحدة بصيغة Collection<VideoDto>.
listPagelistPage(?string $state, int $limit = 20, ?string $cursor)مصفوفة الصفحة الخام، بما فيها nextCursor.
getget(string $videoId)كائن VideoDto واحد.
createcreate(string $originalFilename)تُسجّل فيديو؛ تُعيد معرّفه. الخطوة 1.
deletedelete(string $videoId)حذف ناعم؛ يُلغي أي رفع نشط.
startUploadstartUpload(string $videoId, int $sizeBytes, string $contentType, ?string $checksumSha256, ?string $idempotencyKey)يفتح رفعًا مُجزّأً؛ يُعيد UploadStartDto. الخطوة 2.
completeUploadcompleteUpload(string $videoId, ?string $checksumSha256)يُنهي الرفع. الخطوة 3.
abortUploadabortUpload(string $videoId)يُلغي رفعًا مفتوحًا.
uploadupload(string $filePath, ?string $contentType, ?string $idempotencyKey, ?callable $onProgress)يُنفّذ التدفّق كاملًا في استدعاء واحد.
use Videohati\Laravel\Facades\Videohati;

$videos = Videohati::videos()->list(state: 'ready', limit: 20);
$videos->each(fn ($video) => logger($video->id . ' ' . $video->state));

$video = Videohati::videos()->get($videoId); // VideoDto
Videohati::videos()->delete($videoId);       // حذف ناعم

upload($filePath) يُنفّذ POST /v1/videos ثم POST .../upload/start ثم رفع الأجزاء تِباعًا (3 محاولات لكل جزء؛ يُعاد 5xx/429 ويفشل 4xx فورًا) ثم ‏POST .../upload/complete. ويستقبل ردّ التقدّم البايتات المرفوعة والمجموع.

$result = Videohati::videos()->upload(
    storage_path('app/lecture-01.mp4'),
    onProgress: fn (int $uploaded, int $total) => logger("{$uploaded}/{$total}"),
);

يحمل VideoDto الحقول id وstate وoriginalFilename وsizeBytes و contentType وchecksumSha256 وerrorCode وerrorMessage وreadyAt و failedAt وcreatedAt وupdatedAt — وفي ردود التفاصيل upload وencoding. ويضيف UploadStartDto الدالة partUrl($partNumber) لبناء رابط رفع كل جزء.

إنشاء جلسة تشغيل

لا تُرسل مفتاح API إلى المتصفح أبدًا. أنشئ الجلسة في الخادم وأصدِر رمز الجلسة فقط إلى الصفحة.

تُعيد playback()->createSession() كائن PlaybackSessionDto يحمل sessionId وmanifestUrl وsessionToken وwatermarkToken وexpiresAt و heartbeatIntervalSeconds وshowFreeBadge.

$session = Videohati::playback()->createSession(
    videoId: $video->id,
    viewerDisplayText: $user->name,
);

التوقيع الكامل هو createSession(string $videoId, ?int $ttlSeconds, ?array $referrerAllowlist, ?string $viewerIdFromRequest, ?string $viewerDisplayText).

تضمين المشغّل

يُصدِر موجّه Blade المسمّى @videohatiPlayer مقتطف التضمين — وسم <script> للمشغّل مع عنصر data-videohati-*.

{{-- resources/views/lesson.blade.php --}}
@videohatiPlayer(['videoId' => $video->id, 'sessionToken' => $session->sessionToken])

يعود projectId افتراضيًا إلى VIDEOHATI_PROJECT_ID. المفاتيح الاختيارية: ‏projectId وautoplay ('muted' أو true) وwidth وheight وlang (‏'ar' / 'en') وviewerText وapiOrigin وplayerUrl. انظر صفحة المشغّل للسمات والأحداث.

نقاط نهاية Webhook

تدير webhookEndpoints() نقاط النهاية وتقرأ التسليمات. تُعيد كل دالة جسم JSON المُحلَّل مصفوفةً.

الدالةالتوقيعماذا تفعل
registerregister(string $projectId, array $params)تُسجّل نقطة نهاية. يُعاد secret مرة واحدة.
listlist(string $projectId)تسرد نقاط النهاية.
getget(string $projectId, string $id)نقطة نهاية واحدة.
updateupdate(string $projectId, string $id, array $params)تُحدّث نقطة نهاية.
deletedelete(string $projectId, string $id)حذف ناعم لنقطة نهاية.
rotateSecretrotateSecret(string $projectId, string $id)تسكّ سرًّا جديدًا (يُعاد مرة واحدة).
reopenreopen(string $projectId, string $id)تمحو قاطع الدائرة المتعثّر.
sendTestsendTest(string $projectId, string $id)تُدرِج تسليمًا اصطناعيًا من نوع video.encoding.ready.
listDeliverieslistDeliveries(string $projectId, string $id, array $query = [])صفحة واحدة من محاولات التسليم.

التحقّق من Webhooks

تُعيد webhooks() كائن SignatureVerifier. مرّر جسم الطلب الخام — لا JSON مُعاد تسلسله. تحقّق داخل متحكّم أو مسار، أو غلّفه في وسيطة (middleware).

use Illuminate\Http\Request;
use Videohati\Laravel\Webhooks\SignatureVerifier;

Route::post('/webhooks/videohati', function (Request $request) {
    $event = SignatureVerifier::verifyAndParse(
        $request->getContent(), // الجسم الخام
        $request->header(SignatureVerifier::HEADER, ''),
        config('videohati.webhook_secret'),
    );

    if ($event === null) {
        abort(400); // توقيع غير صالح
    }

    if ($event['type'] === 'video.encoding.ready') {
        // استجب لجاهزية الفيديو.
    }

    return response()->noContent(); // 204
});

تُعيد SignatureVerifier::verify(...) قيمة منطقية حين تحتاج التحقّق فقط؛ وتُعيد ‏verifyAndParse(...) مصفوفة الغلاف المُحلَّل أو null. الترويسة هي ‏Videohati-Signature: t=<unix_ts>,v1=<hex_sha256> (HMAC-SHA256 على ‏<unix_ts>.<raw_body>)؛ والمقارنة ثابتة الزمن، وتُرفَض الطوابع الزمنية الأقدم من 300 ثانية.

الأخطاء

كل استجابة غير 2xx تُطلق فئة فرعية مكتوبة من ‏Videohati\Laravel\Exceptions\VideohatiException، تحمل status وapiCode (رمز الآلة من واجهة API، مثل video_not_found).

الفئةالحالةمتى
ConnectionExceptionلم يصل الطلب إلى واجهة API (DNS، TLS، المقبس، أو انتهاء المهلة).
ValidationException400فشل الجسم أو الاستعلام في التحقّق.
AuthenticationException401اعتماد مفقود أو غير صالح.
PermissionException403اعتماد صالح، لكن الصلاحية غير كافية.
NotFoundException404المورد مفقود أو غير مرئيّ.
ConflictException409تعارض مع الحالة الراهنة للمورد.
RateLimitException429تجاوز حدّ المعدّل؛ اقرأ retryAfterSeconds.
ServerException5xxفشلت واجهة API في إتمام الطلب.
use Videohati\Laravel\Exceptions\NotFoundException;
use Videohati\Laravel\Exceptions\RateLimitException;
use Videohati\Laravel\Exceptions\VideohatiException;

try {
    $video = Videohati::videos()->get($videoId);
} catch (NotFoundException) {
    abort(404);
} catch (RateLimitException $e) {
    sleep($e->retryAfterSeconds ?? 1);
} catch (VideohatiException $e) {
    report($e); // $e->status, $e->apiCode, $e->getMessage()
}