Files
screenclipper/apps/worker/worker/chat_capture.py
T
ayrisdevandClaude Sonnet 5 eccc74166a StreamClipper AI Faz 1: ingestion + sinyal analizi + STT + altyapı iskeleti
yt-dlp headless capture (15dk segmentleme), ses peak + chat velocity sinyal
tespiti, OpenAI Whisper STT (kelime zaman damgalı), BullMQ/Redis/Postgres
altyapısı ve kanal durumu + transkript kütüphanesi gösteren Next.js panel.
LLM virality skorlama, render/crop/altyazı ve multi-platform dağıtım bu
fazın kapsamı dışında.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 14:36:50 +03:00

49 lines
1.7 KiB
Python

"""
Captures a YouTube live chat as timestamped JSONL for the lifetime of a
StreamSession. chat-downloader's `get_chat` is a blocking generator, so each
session's capture runs on its own daemon thread.
"""
import json
import threading
from pathlib import Path
from chat_downloader import ChatDownloader
from .queues import SHARED_MEDIA_ROOT
_active_sessions: set[str] = set()
def _capture_loop(session_id: str, video_id: str) -> None:
out_dir = Path(SHARED_MEDIA_ROOT) / "raw" / session_id
out_dir.mkdir(parents=True, exist_ok=True)
chat_path = out_dir / "chat.jsonl"
url = f"https://www.youtube.com/watch?v={video_id}"
print(f"[chat-capture] started for session {session_id} ({url})")
try:
chat = ChatDownloader().get_chat(url)
with chat_path.open("a", encoding="utf-8") as f:
for message in chat:
record = {
"timestamp": message.get("timestamp"),
"time_in_seconds": message.get("time_in_seconds"),
"author": (message.get("author") or {}).get("name"),
"message": message.get("message"),
}
f.write(json.dumps(record, ensure_ascii=False) + "\n")
f.flush()
except Exception as exc: # chat-downloader raises when the stream ends
print(f"[chat-capture] session {session_id} stopped: {exc}")
finally:
_active_sessions.discard(session_id)
def start_chat_capture(session_id: str, video_id: str | None) -> None:
if session_id in _active_sessions or not video_id or video_id == "forced":
return
_active_sessions.add(session_id)
thread = threading.Thread(target=_capture_loop, args=(session_id, video_id), daemon=True)
thread.start()