feat: implement downloading and processing of Kick and Twitch URLs

This commit is contained in:
Renato
2026-07-03 17:11:58 +02:00
parent 4b0e66f5c2
commit b67d3f3a82
2 changed files with 41 additions and 14 deletions
+4 -4
View File
@@ -473,7 +473,7 @@ def download_youtube_video(url, output_dir="."):
import time
import sys
print("📥 Downloading video from YouTube using custom solver...")
print("📥 Downloading video using custom solver...")
step_start_time = time.time()
# Ensure output_dir is absolute path
@@ -492,14 +492,14 @@ def download_youtube_video(url, output_dir="."):
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
print("🚨 YOUTUBE DOWNLOAD ERROR 🚨", file=sys.stderr)
print("🚨 DOWNLOAD ERROR 🚨", file=sys.stderr)
print(result.stderr, file=sys.stderr)
raise Exception(f"YouTube download failed: {result.stderr}")
raise Exception(f"Video download failed: {result.stderr}")
try:
output_data = json.loads(result.stdout.strip())
downloaded_file = output_data['filepath']
video_title = output_data.get('title', 'youtube_video')
video_title = output_data.get('title', 'video')
sanitized_title = sanitize_filename(video_title)
except Exception as e:
print(f"⚠️ JSON parsing failed, trying fallback detection. Error: {e}")
+37 -10
View File
@@ -113,7 +113,7 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE):
elif qsize > 0:
await update.message.reply_text(f"{qsize} videos en cola, iniciando pronto...")
else:
await update.message.reply_text("💤 Sin actividad. Mandame un link de YouTube.")
await update.message.reply_text("💤 Sin actividad. Mandame un link de YouTube, Twitch o Kick.")
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
@@ -125,9 +125,9 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
log.warning(f" Ignoring unauthorized chat_id={chat_id}")
return
if not re.search(r"(youtube\.com|youtu\.be)", text):
log.info(" Not a YouTube URL, ignoring.")
await update.message.reply_text("❌ Mandame un link de YouTube (youtube.com o youtu.be).")
if not re.search(r"(youtube\.com|youtu\.be|twitch\.tv|kick\.com)", text):
log.info(" Not a YouTube, Twitch or Kick URL, ignoring.")
await update.message.reply_text("❌ Mandame un link de YouTube, Twitch o Kick.")
return
qsize = queue.qsize()
@@ -206,10 +206,37 @@ async def process_queue(bot):
log.info(f"[queue] Job done. Queue size now: {queue.qsize()}")
def get_yt_id(url: str) -> str:
"""Extract YouTube video ID from URL."""
match = re.search(r"(?:v=|\/shorts\/|\/embed\/|\/v\/|youtu\.be\/)([a-zA-Z0-9_-]{11})", url)
return match.group(1) if match else "unknown"
def get_video_id(url: str) -> str:
"""Extract unique video ID from YouTube, Twitch, or Kick URL."""
# 1. YouTube
yt_match = re.search(r"(?:v=|\/shorts\/|\/embed\/|\/v\/|youtu\.be\/)([a-zA-Z0-9_-]{11})", url)
if yt_match:
return yt_match.group(1)
# 2. Twitch VODs
twitch_vod_match = re.search(r"twitch\.tv\/videos\/(\d+)", url)
if twitch_vod_match:
return f"twitch_vod_{twitch_vod_match.group(1)}"
# 3. Twitch Clips
twitch_clip_match = re.search(r"(?:clips\.twitch\.tv\/|twitch\.tv\/\w+\/clip\/)([a-zA-Z0-9_-]+)", url)
if twitch_clip_match:
return f"twitch_clip_{twitch_clip_match.group(1)}"
# 4. Kick VODs (contains UUID)
kick_vod_match = re.search(r"kick\.com\/(?:video\/|(?:\w+\/videos\/))([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})", url)
if kick_vod_match:
return f"kick_vod_{kick_vod_match.group(1)}"
# 5. Kick Clips
kick_clip_match = re.search(r"kick\.com\/\w+\/clips\/([a-zA-Z0-9_-]+)", url)
if kick_clip_match:
return f"kick_clip_{kick_clip_match.group(1)}"
# Fallback to md5 hash of URL to ensure uniqueness
import hashlib
url_hash = hashlib.md5(url.encode('utf-8')).hexdigest()[:10]
return f"video_{url_hash}"
# ─── Main video processor ─────────────────────────────────────────────────────
@@ -218,7 +245,7 @@ async def process_video(bot, url: str):
log.info(f"[process] URL: {url}")
# 1. Extract video ID and Get title
yt_id = get_yt_id(url)
yt_id = get_video_id(url)
title = await get_video_title(url)
log.info(f"[process] Video ID: {yt_id}, Title: {title!r}")
@@ -298,7 +325,7 @@ async def process_video(bot, url: str):
log.debug(f"[main.py] {line[:200]}")
# Parse progress markers from main.py output
if "Downloading video from YouTube" in line:
if "Downloading video" in line:
await edit_msg(
f"🎬 *{title[:50] or 'Video'}*\n\n"
f"📥 Descargando..."