feat: implement downloading and processing of Kick and Twitch URLs
This commit is contained in:
+4
-4
@@ -473,7 +473,7 @@ def download_youtube_video(url, output_dir="."):
|
|||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
print("📥 Downloading video from YouTube using custom solver...")
|
print("📥 Downloading video using custom solver...")
|
||||||
step_start_time = time.time()
|
step_start_time = time.time()
|
||||||
|
|
||||||
# Ensure output_dir is absolute path
|
# 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)
|
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print("🚨 YOUTUBE DOWNLOAD ERROR 🚨", file=sys.stderr)
|
print("🚨 DOWNLOAD ERROR 🚨", file=sys.stderr)
|
||||||
print(result.stderr, 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:
|
try:
|
||||||
output_data = json.loads(result.stdout.strip())
|
output_data = json.loads(result.stdout.strip())
|
||||||
downloaded_file = output_data['filepath']
|
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)
|
sanitized_title = sanitize_filename(video_title)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"⚠️ JSON parsing failed, trying fallback detection. Error: {e}")
|
print(f"⚠️ JSON parsing failed, trying fallback detection. Error: {e}")
|
||||||
|
|||||||
+37
-10
@@ -113,7 +113,7 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
elif qsize > 0:
|
elif qsize > 0:
|
||||||
await update.message.reply_text(f"⏳ {qsize} videos en cola, iniciando pronto...")
|
await update.message.reply_text(f"⏳ {qsize} videos en cola, iniciando pronto...")
|
||||||
else:
|
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):
|
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}")
|
log.warning(f" Ignoring unauthorized chat_id={chat_id}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not re.search(r"(youtube\.com|youtu\.be)", text):
|
if not re.search(r"(youtube\.com|youtu\.be|twitch\.tv|kick\.com)", text):
|
||||||
log.info(" Not a YouTube URL, ignoring.")
|
log.info(" Not a YouTube, Twitch or Kick URL, ignoring.")
|
||||||
await update.message.reply_text("❌ Mandame un link de YouTube (youtube.com o youtu.be).")
|
await update.message.reply_text("❌ Mandame un link de YouTube, Twitch o Kick.")
|
||||||
return
|
return
|
||||||
|
|
||||||
qsize = queue.qsize()
|
qsize = queue.qsize()
|
||||||
@@ -206,10 +206,37 @@ async def process_queue(bot):
|
|||||||
log.info(f"[queue] Job done. Queue size now: {queue.qsize()}")
|
log.info(f"[queue] Job done. Queue size now: {queue.qsize()}")
|
||||||
|
|
||||||
|
|
||||||
def get_yt_id(url: str) -> str:
|
def get_video_id(url: str) -> str:
|
||||||
"""Extract YouTube video ID from URL."""
|
"""Extract unique video ID from YouTube, Twitch, or Kick URL."""
|
||||||
match = re.search(r"(?:v=|\/shorts\/|\/embed\/|\/v\/|youtu\.be\/)([a-zA-Z0-9_-]{11})", url)
|
# 1. YouTube
|
||||||
return match.group(1) if match else "unknown"
|
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 ─────────────────────────────────────────────────────
|
# ─── Main video processor ─────────────────────────────────────────────────────
|
||||||
@@ -218,7 +245,7 @@ async def process_video(bot, url: str):
|
|||||||
log.info(f"[process] URL: {url}")
|
log.info(f"[process] URL: {url}")
|
||||||
|
|
||||||
# 1. Extract video ID and Get title
|
# 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)
|
title = await get_video_title(url)
|
||||||
log.info(f"[process] Video ID: {yt_id}, Title: {title!r}")
|
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]}")
|
log.debug(f"[main.py] {line[:200]}")
|
||||||
|
|
||||||
# Parse progress markers from main.py output
|
# Parse progress markers from main.py output
|
||||||
if "Downloading video from YouTube" in line:
|
if "Downloading video" in line:
|
||||||
await edit_msg(
|
await edit_msg(
|
||||||
f"🎬 *{title[:50] or 'Video'}*\n\n"
|
f"🎬 *{title[:50] or 'Video'}*\n\n"
|
||||||
f"📥 Descargando..."
|
f"📥 Descargando..."
|
||||||
|
|||||||
Reference in New Issue
Block a user