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>
49 lines
1.7 KiB
Python
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()
|