Allmine API
Live Stream

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

ÖzellikDeğer
EndpointPOST /live-stream/:id/time-extensions
Kimlik DoğrulamaBearer <JWT> zorunlu
Body{ "durationSeconds": number } (1 ve üzeri, saniye cinsinden)
Cevap201 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

  • axios veya fetch için cross-fetch
  • socket.io-client (>=4.x). React Native tarafında WebSocket taşıyıcısını zorunlu kılmak için transports: ['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-stream

Bu 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;
}
  • durationSeconds tamsayı olmalı; validation hataları 400 Bad Request ile döner.
  • token olarak kullanıcıya ait JWT gönderilmeli; aksi halde 401 Unauthorized alı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]);
}
  • forceNew ile her yayın için ayrı bağlantı açılmasını sağlayabilirsiniz.
  • Mobil ağlarda bağlantı sürekliliği için reconnectionAttempts ve timeout değerlerini ihtiyaca göre artırabilirsiniz.
  • Sunucu tarafında JWT doğrulaması eklenirse socket bağlantısında Authorization header veya auth.token parametresi kullanılmalıdır (örnek kod bloklarındaki token değ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-stream

Bu 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_PUBLIC yerine process.env.API_BASE_URL gibi sunucu tarafı değişkenleriyle yapabilirsiniz.
  • Edge runtime kullanıyorsanız fetch default 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 import sayesinde socket.io-client sadece 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.com yerine NEXT_PUBLIC_APP_URL/api üzerinden çağrı yapabilirsiniz.

Hata Senaryoları ve Logging

  • Geçersiz streamId veya yayının aktif olmaması durumunda servis 400 veya 404 dö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 streamTimeExtensionAdded olayı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ış

  1. Kullanıcı JWT ile kimliği doğrulanmış olmalı.
  2. İstemci /live-stream/:id/time-extensions endpoint'ine saniye cinsinden süre gönderir.
  3. Backend süreyi MongoDB'ye kaydeder ve aynı anda streamTimeExtensionAdded olayını stream:<id> odasına emit eder.
  4. 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.

On this page