so basically i'm trying to build telegram bot using telebot (pyTelegramBotAPI) library and yt-dlp so that the user can download the video he wants with the quality he wants.
also because yt-dlp shows list of every possible modes of what you could download from that video (only audio, only video, etc), i had to filter some of them Because I want only video with sound to be downloadable
the problem is that when i use the bot, there is only one quality that you can download and that's 360p. everything works perfect except that you can only download 360p quality.
here is part of my code:
import telebotfrom yt_dlp import YoutubeDLimport osimport randomimport stringimport requestsBOT_TOKEN = "my telegram bot token"bot = telebot.TeleBot(BOT_TOKEN)DOWNLOAD_DIR = "youtube_downloads"os.makedirs(DOWNLOAD_DIR, exist_ok=True)YOUTUBE_COOKIES_FILE = "youtube_cookies.txt"video_data_cache = {}@bot.message_handler(commands=["youtubedownload"])def youtube_download_command(message): if not message.reply_to_message: bot.reply_to(message, "please replay this command to link of your youtube video") return video_url = message.reply_to_message.text try: ydl_opts = {"quiet": True,"no_warnings": True,"cookies": YOUTUBE_COOKIES_FILE, } with YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(video_url, download=False) video_title = info.get("title", "Uncertain title") thumbnail_url = info.get("thumbnail", None) formats = [ f for f in info.get("formats", []) if f.get("vcodec") != "none" and f.get("acodec") != "none" ] video_data_cache[message.chat.id] = {"formats": formats,"video_url": video_url,"title": video_title, } quality_list = "\n".join( [f"{idx + 1}. {f['format_note']} - {f['filesize'] // 1024 // 1024 if f.get('filesize') else 'unknown'} MB" for idx, f in enumerate(formats)] ) if thumbnail_url: thumbnail_file = f"{DOWNLOAD_DIR}/thumbnail_{random_string(8)}.jpg" download_image(thumbnail_url, thumbnail_file) with open(thumbnail_file, "rb") as thumb: bot.send_photo( message.chat.id, thumb, caption=( f"🎥 *{video_title}*\n\n" f"please choose one of the qualities below:\n\n{quality_list}\n\n""replay the number of quality you want" ), parse_mode="Markdown" ) os.remove(thumbnail_file) else: bot.reply_to( message, f"🎥 *{video_title}*\n\n" f"please choose one of the qualities below:\n\n{quality_list}\n\n""replay the number of quality you want", parse_mode="Markdown" ) except Exception as e: bot.reply_to(message, f"Unfortunately, an error occurred:\n{e}")@bot.message_handler(func=lambda message: message.chat.id in video_data_cache)def download_selected_quality(message): try: user_selection = int(message.text) - 1 video_info = video_data_cache.get(message.chat.id) if video_info is None: bot.reply_to(message, "Unfortunately, the video information was not found. Please try again.") return formats = video_info["formats"] if user_selection < 0 or user_selection >= len(formats): bot.reply_to(message, "The selected quality number is not valid.") return selected_format = formats[user_selection] random_filename = f"{random_string(12)}.mp4" output_path = os.path.join(DOWNLOAD_DIR, random_filename) ydl_opts = {"format": selected_format["format_id"],"outtmpl": output_path,"cookies": YOUTUBE_COOKIES_FILE,"quiet": True,"no_warnings": True, } with YoutubeDL(ydl_opts) as ydl: ydl.download([video_info["video_url"]]) with open(output_path, "rb") as video_file: bot.send_video( message.chat.id, video_file, caption=f"🎥 {video_info['title']}\n\n" ) os.remove(output_path) video_data_cache.pop(message.chat.id) except Exception as e: bot.reply_to(message, f"An error occurred:\n{e}")def random_string(length=8): return ''.join(random.choices(string.ascii_letters + string.digits, k=length))def download_image(url, path): response = requests.get(url, stream=True) if response.status_code == 200: with open(path, "wb") as file: for chunk in response.iter_content(1024): file.write(chunk) else: raise Exception("Error downloading thumbnail.")
At first, I tried to use the pytube library instead, but I came to the conclusion that the same yt-dlp library is better and more complete. Second, I tried to change the video filter, but it didn't work, and only 360p downloadable quality was displayed in the desired list.