diff --git a/plugins/youtube-preview.py b/plugins/youtube-preview.py
index 1cac89a..4a7305e 100644
--- a/plugins/youtube-preview.py
+++ b/plugins/youtube-preview.py
@@ -7,11 +7,11 @@ import re
import logging
import asyncio
import aiohttp
-from pytubefix import YouTube
+import yt_dlp
import simplematrixbotlib as botlib
from youtube_title_parse import get_artist_title
-LYRICIST_API_URL = "https://lyrist.vercel.app/api/{}"
+LYRICIST_API_URL = "https://lyrist.vercel.app/api/{}/{}"
def seconds_to_minutes_seconds(seconds):
@@ -43,18 +43,26 @@ async def fetch_lyrics(song, artist):
"""
try:
async with aiohttp.ClientSession() as session:
- async with session.get(LYRICIST_API_URL.format(song, artist)) as response:
- data = await response.json()
- return data.get("lyrics")
+ url = LYRICIST_API_URL.format(artist, song)
+ logging.info(f"Fetching lyrics from: {url}")
+ async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
+ if response.status == 200:
+ data = await response.json()
+ return data.get("lyrics")
+ else:
+ logging.warning(f"Lyrics API returned status {response.status}")
+ return None
+ except asyncio.TimeoutError:
+ logging.error("Timeout fetching lyrics")
+ return None
except Exception as e:
logging.error(f"Error fetching lyrics: {str(e)}")
return None
-
async def fetch_youtube_info(youtube_url):
"""
- Asynchronously fetches information about a YouTube video.
+ Asynchronously fetches information about a YouTube video using yt-dlp.
Args:
youtube_url (str): The URL of the YouTube video.
@@ -64,26 +72,71 @@ async def fetch_youtube_info(youtube_url):
None if an error occurs during fetching.
"""
try:
- video = YouTube(youtube_url)
- title = video.title
- artist, song = get_artist_title(title)
+ logging.info(f"Fetching YouTube info for: {youtube_url}")
+
+ # Configure yt-dlp options
+ ydl_opts = {
+ 'quiet': True,
+ 'no_warnings': True,
+ 'extract_flat': False,
+ 'skip_download': True,
+ }
+
+ # Run yt-dlp in thread pool to avoid blocking
+ loop = asyncio.get_event_loop()
+
+ def extract_info():
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
+ return ydl.extract_info(youtube_url, download=False)
+
+ info = await loop.run_in_executor(None, extract_info)
+
+ if not info:
+ logging.error("No info returned from yt-dlp")
+ return None
+
+ # Extract video information
+ title = info.get('title', 'Unknown Title')
+ description = info.get('description', 'No description available')
+ duration = info.get('duration', 0)
+ view_count = info.get('view_count', 0)
+ uploader = info.get('uploader', 'Unknown')
+
+ logging.info(f"Video title: {title}")
+
+ length = seconds_to_minutes_seconds(duration)
+
+ # Parse artist and song from title
+ artist, song = get_artist_title(title)
+ logging.info(f"Parsed artist: {artist}, song: {song}")
+
+ # Limit description length to avoid huge messages
+ if len(description) > 500:
+ description = description[:500] + "..."
- description = video.description
- length = seconds_to_minutes_seconds(video.length)
- views = video.views
- author = video.author
description_with_breaks = description.replace('\n', '
')
- # Fetching lyrics
- lyrics = await fetch_lyrics(song, artist)
- lyrics = lyrics.replace('\n', "
")
+ # Build basic info message
+ info_message = f"""🎬🎝 Title: {title}
Length: {length} | Views: {view_count:,} | Uploader: {uploader}
⤵︎Description⤵︎
{description_with_breaks} """
+
+ # Try to fetch lyrics if artist and song were parsed
+ if artist and song:
+ logging.info("Attempting to fetch lyrics...")
+ lyrics = await fetch_lyrics(song, artist)
+ if lyrics:
+ lyrics = lyrics.replace('\n', "
")
+ # Limit lyrics length
+ if len(lyrics) > 3000:
+ lyrics = lyrics[:3000] + "
...(truncated)"
+ info_message += f"
🎵 Lyrics:
{lyrics} "
+ else:
+ logging.info("No lyrics found")
+ else:
+ logging.info("Could not parse artist/song from title, skipping lyrics")
- info_message = f"""🎬🎝 Title: {title} | Length: {length} minutes | Views: {views}\n⤵︎Description⤵︎
{description_with_breaks} """
- if lyrics:
- info_message += f"
🎵 Lyrics:
{lyrics} "
return info_message
except Exception as e:
- logging.error(f"Error fetching YouTube video information: {str(e)}")
+ logging.error(f"Error fetching YouTube video information: {str(e)}", exc_info=True)
return None
@@ -102,14 +155,20 @@ async def handle_command(room, message, bot, prefix, config):
None
"""
match = botlib.MessageMatch(room, message, bot, prefix)
- if match.is_not_from_this_bot() and re.search(r'youtube\.com/watch\?v=', message.body):
- logging.info("YouTube link detected")
- video_id_match = re.search(r'youtube\.com/watch\?v=([^\s]+)', message.body)
+
+ # Check if message contains a YouTube link
+ if match.is_not_from_this_bot() and re.search(r'(youtube\.com/watch\?v=|youtu\.be/)', message.body):
+ logging.info(f"YouTube link detected in message: {message.body}")
+
+ # Match both youtube.com and youtu.be formats
+ video_id_match = re.search(r'(?:youtube\.com/watch\?v=|youtu\.be/)([a-zA-Z0-9_-]{11})', message.body)
+
if video_id_match:
video_id = video_id_match.group(1)
youtube_url = f"https://www.youtube.com/watch?v={video_id}"
- logging.info(f"Fetching information for YouTube video: {youtube_url}")
- retry_count = 3
+ logging.info(f"Fetching information for YouTube video ID: {video_id}")
+
+ retry_count = 2 # Reduced retries since yt-dlp is more reliable
while retry_count > 0:
info_message = await fetch_youtube_info(youtube_url)
if info_message:
@@ -117,8 +176,12 @@ async def handle_command(room, message, bot, prefix, config):
logging.info("Sent YouTube video information to the room")
break
else:
- logging.info("Retrying...")
+ logging.warning(f"Failed to fetch info, retrying... ({retry_count-1} attempts left)")
retry_count -= 1
- await asyncio.sleep(1) # wait for 1 second before retrying
+ if retry_count > 0:
+ await asyncio.sleep(2) # wait for 2 seconds before retrying
else:
- logging.error("Failed to fetch YouTube video information after retries")
+ logging.error("Failed to fetch YouTube video information after all retries")
+ await bot.api.send_text_message(room.room_id, "Failed to fetch YouTube video information. The video may be unavailable or age-restricted.")
+ else:
+ logging.warning("Could not extract video ID from YouTube URL")
diff --git a/requirements.txt b/requirements.txt
index 9449463..c7a8dce 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,5 @@
python-dotenv
requests
-pytubefix
duckduckgo_search
nio
markdown2
@@ -11,4 +10,4 @@ youtube_title_parse
dnspython
croniter
schedule
-
+yt-dlp