Live Stream time extension
Yayın süre uzatma endpoint ve kuralları
Canlı Yayın Süre Uzatma İstemci Entegrasyonu
Canlı yayınlara izleyiciler tarafından ek süre gönderilebilmesi için REST API ve Socket.IO tabanlı gerçek zamanlı olaylar sağlanır. Bu doküman, mevcut backend servislerini React Native ve Next.js istemcilerinden nasıl tüketebileceğinizi ve hangi konfigürasyonlara dikkat etmeniz gerektiğini açıklar.
API Özeti
| Özellik | Değer |
|---|---|
| Endpoint | POST /live-stream/:id/time-extensions |
| Kimlik Doğrulama | Bearer <JWT> zorunlu |
| Body | { "durationSeconds": number } (1 ve üzeri, saniye cinsinden) |
| Cevap | 201 Created + LiveStreamTimeExtensionResponseDto |
İstek Gövdesi
{
"durationSeconds": 120
}Başarılı Cevap
{
"id": "65f1b2c9de63f3b7b2a12345",
"streamId": "65ef3d1ae3f9c27c3a123456",
"senderId": "65eec19d8f8b21e92a987654",
"durationSeconds": 120,
"senderSnapshot": {
"_id": "65eec19d8f8b21e92a987654",
"username": "viewer42",
"name": "Ada",
"surname": "Lovelace",
"profilePhoto": {
"_id": "65c9ddf42f1a3e7c12345678",
"url": "https://cdn.allmine.com/media/65c9ddf42f1a3e7c12345678.jpg"
}
},
"createdAt": "2024-03-18T19:22:54.821Z"
}Socket Olayı
Süre uzatma olayı yayın odası (stream:<streamId>) üzerinden streamTimeExtensionAdded adıyla yayınlanır.
interface StreamTimeExtensionPayload {
id: string;
streamId: string;
senderId: string;
durationSeconds: number;
senderSnapshot: {
_id: string;
username: string | null;
name: string | null;
surname: string | null;
profilePhoto: {
_id: string;
url: string;
variants?: Record<string, string>;
mimeType?: string;
} | null;
};
createdAt: string; // ISO 8601
}Odaya katılım sağlamak için /live-stream namespace'ine bağlandıktan sonra joinStream mesajını streamId alanıyla emit etmelisiniz. Ayrılırken leaveStream göndererek odadan çıkın. Bu yöntem hem süre uzatma bildirimlerinin hem de izleyici sayısı gibi diğer yayın içi olayların alınmasını sağlar.
React Native Entegrasyonu
Gerekli Paketler
axiosveyafetchiçincross-fetchsocket.io-client(>=4.x). React Native tarafında WebSocket taşıyıcısını zorunlu kılmak içintransports: ['websocket']ayarı gereklidir.
Çevresel Değişkenler
Mobil istemciler genellikle .env dosyasını hermes/metro üzerinden kullanır. Örnek:
API_BASE_URL=https://api.allmine.com
LIVE_STREAM_SOCKET_URL=https://api.allmine.com/live-streamBu değerleri react-native-config veya Expo'nun app.config.js üzerinden projeye enjekte edin.
Süre Gönderme Fonksiyonu
import axios from 'axios';
const API_BASE_URL = process.env.API_BASE_URL!;
export async function sendTimeExtension({
token,
streamId,
durationSeconds,
}: {
token: string;
streamId: string;
durationSeconds: number;
}) {
const url = `${API_BASE_URL}/live-stream/${streamId}/time-extensions`;
const response = await axios.post(
url,
{ durationSeconds },
{
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
},
);
return response.data;
}durationSecondstamsayı olmalı; validation hataları400 Bad Requestile döner.tokenolarak kullanıcıya ait JWT gönderilmeli; aksi halde401 Unauthorizedalınır.
Socket Yapılandırması
import { useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';
const SOCKET_URL = process.env.LIVE_STREAM_SOCKET_URL!;
export function useLiveStreamSocket(streamId: string, onExtension: (payload: StreamTimeExtensionPayload) => void) {
const socketRef = useRef<Socket>();
useEffect(() => {
const socket = io(SOCKET_URL, {
transports: ['websocket'],
forceNew: true,
reconnectionAttempts: 5,
timeout: 10000,
});
socket.emit('joinStream', { streamId });
socket.on('streamTimeExtensionAdded', onExtension);
socketRef.current = socket;
return () => {
socket.emit('leaveStream', { streamId });
socket.off('streamTimeExtensionAdded', onExtension);
socket.disconnect();
};
}, [streamId, onExtension]);
}forceNewile her yayın için ayrı bağlantı açılmasını sağlayabilirsiniz.- Mobil ağlarda bağlantı sürekliliği için
reconnectionAttemptsvetimeoutdeğerlerini ihtiyaca göre artırabilirsiniz. - Sunucu tarafında JWT doğrulaması eklenirse socket bağlantısında
Authorizationheader veyaauth.tokenparametresi kullanılmalıdır (örnek kod bloklarındakitokendeğişkeni).
Next.js Entegrasyonu
Next.js (App Router) projelerinde socket.io kullanımı yalnızca istemci komponentlerinde yapılmalıdır; SSR ortamında window nesnesi olmadığı için dinamik import tercih edin.
Ortak Konfigürasyon
next.config.js veya .env.local içerisine:
NEXT_PUBLIC_API_BASE_URL=https://api.allmine.com
NEXT_PUBLIC_LIVE_STREAM_SOCKET_URL=https://api.allmine.com/live-streamBu değerler process.env.NEXT_PUBLIC_* üzerinden tarayıcı koduna ulaşır.
Sunucuya Süre Gönderme (Route Handler / Server Action)
// app/actions/live-stream.ts
export async function extendLiveStream({ token, streamId, durationSeconds }: {
token: string;
streamId: string;
durationSeconds: number;
}) {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_BASE_URL}/live-stream/${streamId}/time-extensions`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ durationSeconds }),
cache: 'no-store',
},
);
if (!res.ok) {
const error = await res.json().catch(() => ({}));
throw new Error(error.message ?? 'Süre gönderimi başarısız');
}
return res.json();
}- Route handler kullanıyorsanız (örn.
/app/api/live-stream/[id]/time-extension/route.ts) aynıfetchçağrısınıNEXT_PUBLICyerineprocess.env.API_BASE_URLgibi sunucu tarafı değişkenleriyle yapabilirsiniz. - Edge runtime kullanıyorsanız
fetchdefault olarak desteklenir, sadece timeout ve yeniden deneme stratejilerinizi tanımlayın.
İstemci Komponentinde Socket Dinleme
'use client';
import { useEffect, useState } from 'react';
import dynamic from 'next/dynamic';
const socketIOClient = async () => (await import('socket.io-client')).io;
export function LiveStreamExtensions({ streamId }: { streamId: string }) {
const [extensions, setExtensions] = useState<StreamTimeExtensionPayload[]>([]);
useEffect(() => {
let socket: ReturnType<typeof import('socket.io-client').io> | undefined;
socketIOClient().then((io) => {
socket = io(process.env.NEXT_PUBLIC_LIVE_STREAM_SOCKET_URL!, {
transports: ['websocket'],
reconnectionDelayMax: 5000,
});
socket.emit('joinStream', { streamId });
socket.on('streamTimeExtensionAdded', (payload) => {
setExtensions((prev) => [payload, ...prev]);
});
});
return () => {
if (socket) {
socket.emit('leaveStream', { streamId });
socket.off('streamTimeExtensionAdded');
socket.disconnect();
}
};
}, [streamId]);
return (
<ul className="space-y-2">
{extensions.map((ext) => (
<li key={ext.id} className="border rounded p-2">
<div className="text-sm text-gray-500">
{new Date(ext.createdAt).toLocaleTimeString('tr-TR')}
</div>
<div className="font-medium">
+{ext.durationSeconds} saniye — {ext.senderSnapshot.username ?? 'Anonim'}
</div>
</li>
))}
</ul>
);
}dynamic importsayesindesocket.io-clientsadece tarayıcıda yüklenecektir.- UI tarafında sıralı liste kullanırken gelen payload'ı state'e prepend ederek gerçek zamanlı güncelleme sağlayabilirsiniz.
- Eğer Next.js API route üzerinden proxy kullanıyorsanız CORS yönetimini tek noktadan yapabilir, client tarafında doğrudan
api.yourdomain.comyerineNEXT_PUBLIC_APP_URL/apiüzerinden çağrı yapabilirsiniz.
Hata Senaryoları ve Logging
- Geçersiz
streamIdveya yayının aktif olmaması durumunda servis400veya404döner; UI tarafında kullanıcıya uygun mesaj gösterin. - Socket bağlantısı kurulmadan REST çağrısı başarılı olsa bile diğer izleyiciler güncellemeyi alamaz; bu nedenle
streamTimeExtensionAddedolayının gelmediği durumları telemetri ile takip edin. - Arka planda socket emit işlemi hata verirse servis log kaydı tutar fakat REST cevabı değişmez; client tarafında gerekirse süreyi local state'e optimistik olarak ekleyip socket ile senkronize edin.
Özet Akış
- Kullanıcı JWT ile kimliği doğrulanmış olmalı.
- İstemci
/live-stream/:id/time-extensionsendpoint'ine saniye cinsinden süre gönderir. - Backend süreyi MongoDB'ye kaydeder ve aynı anda
streamTimeExtensionAddedolayınıstream:<id>odasına emit eder. - Odaya abone olan tüm istemciler gerçek zamanlı olarak süre artışını, gönderen bilgisiyle birlikte alır.
Bu yapı React Native ve Next.js istemcileri için minimum konfigürasyonla ölçeklenebilir bir canlı yayın deneyimi sunar.