درس تطبيقي

ابنِ لبث TikTok LIVE أوفرلاي مخصصًا

أنشئ أوفرلاي لحظيًا كمصدر متصفح متوافق مع OBS يعرض الدردشة المباشرة وتنبيهات الهدايا وعدد المشاهدين من أي بث TikTok LIVE.

ما الذي ستبنيه

أوفرلاي بث يعمل في المتصفح مع OBS أو Streamlabs أو أي برنامج يدعم مصادر المتصفح. يعرض:

  • رسائل الدردشة المباشرة لحظيًا
  • تنبيهات هدايا متحركة مع قيمتها بالماسات
  • عدد المشاهدين الحالي
  • شريط دردشة متحرك مع صور المستخدمين

المتطلبات

  • تثبيت Node.js 18+
  • مفتاح TikTools API مجاني - احصل عليه من هنا
  • OBS Studio (أو أي برنامج بث يدعم مصدر المتصفح)

الخطوة 1: جهّز المشروع

mkdir tiktok-overlay && cd tiktok-overlay
npm init -y
npm install @tiktool/live express

الخطوة 2: أنشئ الخادم

أنشئ server.mjs: يتصل بـ TikTok ويبث الأحداث عبر Server-Sent Events (SSE) إلى أوفرلاي المتصفح:

import express from 'express'
import { TikTokLive } from '@tiktool/live'

const app = express()
const clients = new Set()

// Serve the overlay HTML
app.get('/', (req, res) => {
  res.sendFile('overlay.html', { root: '.' })
})

// SSE endpoint for real-time events
app.get('/events', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive',
  })
  clients.add(res)
  req.on('close', () => clients.delete(res))
})

function broadcast(event, data) {
  for (const client of clients) {
    client.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`)
  }
}

// Connect to TikTok LIVE
const tiktok = new TikTokLive({
  uniqueId: process.env.STREAMER || 'tv_asahi_news',
  apiKey: process.env.TIKTOOL_API_KEY
})

tiktok.on('chat', (e) => broadcast('chat', {
  user: e.nickname, comment: e.comment, avatar: e.profilePictureUrl
}))

tiktok.on('gift', (e) => broadcast('gift', {
  user: e.nickname, gift: e.giftName, diamonds: e.diamondCount
}))

tiktok.on('viewer_count', (e) => broadcast('viewers', {
  count: e.viewerCount
}))

await tiktok.connect()
app.listen(3333, () => console.log('Overlay: http://localhost:3333'))

الخطوة 3: أنشئ HTML الأوفرلاي

أنشئ overlay.html: هذا ما سيعرضه OBS كمصدر متصفح:

<!DOCTYPE html>
<html>
<head>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: 'Inter', sans-serif;
      background: transparent;
      overflow: hidden;
    }

    .chat-feed {
      position: fixed; bottom: 20px; left: 20px;
      width: 360px; max-height: 400px;
      display: flex; flex-direction: column-reverse;
      gap: 6px; overflow: hidden;
    }

    .chat-msg {
      background: rgba(0, 0, 0, 0.6);
      border-radius: 8px; padding: 8px 12px;
      color: #fff; font-size: 14px;
      animation: slideIn 0.3s ease;
    }

    .chat-msg .user { color: #fff; font-weight: 600; }

    .gift-alert {
      position: fixed; top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      background: rgba(0, 0, 0, 0.8);
      border: 2px solid var(--border-primary);
      border-radius: 16px; padding: 24px 40px;
      text-align: center; color: #fff;
      animation: popIn 0.5s ease;
      display: none;
    }

    .gift-alert.show { display: block; }
    .gift-alert .gift-name { font-size: 28px; font-weight: 800; }
    .gift-alert .gift-user { font-size: 16px; color: #94a3b8; }
    .gift-alert .gift-diamonds { font-size: 18px; color: #fff; }

    .viewer-count {
      position: fixed; top: 20px; right: 20px;
      background: rgba(0, 0, 0, 0.6);
      border-radius: 8px; padding: 8px 16px;
      color: #fff; font-size: 14px; font-weight: 600;
    }

    @keyframes slideIn {
      from { opacity: 0; transform: translateY(10px); }
      to { opacity: 1; transform: translateY(0); }
    }

    @keyframes popIn {
      0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
      100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    }
  </style>
</head>
<body>
  <div class="chat-feed" id="chat"></div>
  <div class="gift-alert" id="gift">
    <div class="gift-name" id="gift-name"></div>
    <div class="gift-user" id="gift-user"></div>
    <div class="gift-diamonds" id="gift-diamonds"></div>
  </div>
  <div class="viewer-count" id="viewers">👁 0</div>

  <script>
    const chat = document.getElementById('chat')
    const gift = document.getElementById('gift')
    const src = new EventSource('/events')

    src.addEventListener('chat', (e) => {
      const d = JSON.parse(e.data)
      const el = document.createElement('div')
      el.className = 'chat-msg'
      el.innerHTML = `<span class="user">${d.user}</span> ${d.comment}`
      chat.prepend(el)
      if (chat.children.length > 50) chat.lastChild.remove()
    })

    src.addEventListener('gift', (e) => {
      const d = JSON.parse(e.data)
      document.getElementById('gift-name').textContent = `🎁 ${d.gift}`
      document.getElementById('gift-user').textContent = `from ${d.user}`
      document.getElementById('gift-diamonds').textContent = `${d.diamonds} 💎`
      gift.classList.add('show')
      setTimeout(() => gift.classList.remove('show'), 4000)
    })

    src.addEventListener('viewers', (e) => {
      const d = JSON.parse(e.data)
      document.getElementById('viewers').textContent = `👁 ${d.count.toLocaleString()}`
    })
  </script>
</body>
</html>

الخطوة 4: شغّله وأضفه إلى OBS

TIKTOOL_API_KEY=your_key STREAMER=target_username node server.mjs

ثم في OBS:

  • أضف مصدر متصفح جديدًا
  • اضبط الرابط على http://localhost:3333
  • اضبط العرض على 1920 والارتفاع على 1080
  • فعّل "Shutdown source when not visible"

أفكار للتخصيص

  • سمات مخصصة - غيّر الألوان والخطوط والتخطيط لتناسب هوية بثك
  • مؤثرات صوتية - شغّل أصواتًا عند وصول الهدايا باستخدام Web Audio API
  • صور GIF متحركة - اعرض صور GIF متحركة لهدايا محددة
  • أشرطة الأهداف - أضف أشرطة تقدم لأهداف الدعم والهدايا
  • منصة كبار الداعمين - اعرض أفضل 3 داعمين لحظيًا