# Start download process in background context.application.create_task( process_playlist(update.effective_chat.id, url, choice, context) )
def get_playlist_info(url): ydl_opts = 'quiet': True, 'extract_flat': True with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) entries = info.get('entries', []) return ['id': e['id'], 'title': e['title'], 'url': f"https://youtube.com/watch?v=e['id']" for e in entries[:15]] # limit to 15
await query.edit_message_text(f"⏳ Fetching playlist: url\nThis may take a moment...")
await context.bot.send_message(chat_id, f"Found len(videos) videos. Downloading...") Telegram Bot To Download Youtube Playlist
for idx, video in enumerate(videos, 1): await context.bot.send_message(chat_id, f"⬇️ Downloading idx/len(videos): video['title']") try: if format_type == 'video': file_path = await loop.run_in_executor( executor, download_video, video['url'], user_dir ) else: file_path = await loop.run_in_executor( executor, download_audio, video['url'], user_dir ) # Step 3: Send file with open(file_path, 'rb') as f: if format_type == 'video': await context.bot.send_video(chat_id, f, caption=video['title']) else: await context.bot.send_audio(chat_id, f, title=video['title']) # Clean up os.remove(file_path) except Exception as e: await context.bot.send_message(chat_id, f"Failed for video['title']: str(e)")
import yt_dlp import asyncio from concurrent.futures import ThreadPoolExecutor import os executor = ThreadPoolExecutor(max_workers=2)
[Unit] Description=YouTube Playlist Telegram Bot After=network.target [Service] User=youruser WorkingDirectory=/home/youruser/youtube-playlist-bot ExecStart=/home/youruser/youtube-playlist-bot/venv/bin/python bot.py Restart=always # Start download process in background context
pip install python-telegram-bot[job-queue] yt-dlp asyncio aiofiles mkdir downloads temp_files logs 3. Core Design & Architecture | Component | Responsibility | |-----------|----------------| | Telegram Handler | Receives messages, validates URLs, manages user state | | Download Worker | Uses yt-dlp to fetch playlist metadata & download files | | Queue Manager | Prevents overload; processes one playlist per user sequentially | | File Sender | Uploads files to Telegram with progress feedback | | Cleaner | Deletes local files after sending (or after 1 hour) | 4. Implementation Step-by-Step 4.1 Basic Bot Skeleton Create bot.py :
task = asyncio.create_task(process_playlist(chat_id, url, format_type, context)) user_tasks[chat_id] = task await task del user_tasks[chat_id] async def cancel(update, context): chat_id = update.effective_chat.id if chat_id in user_tasks: user_tasks[chat_id].cancel() await update.message.reply_text("Download cancelled.") else: await update.message.reply_text("No active download.") 5.3 Progress Indication (per video) yt-dlp supports progress hooks. Add to download functions:
with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([video_url]) # return actual file path info = ydl.extract_info(video_url, download=False) filename = ydl.prepare_filename(info).replace('.webm', '.mp4') return filename Implementation Step-by-Step 4
What it does: User sends a YouTube playlist URL → Bot processes the playlist → Downloads each video/audio → Sends files to Telegram chat.
def download_audio(video_url, output_path): ydl_opts = 'outtmpl': f'output_path/%(title)s.%(ext)s', 'format': 'bestaudio/best', 'postprocessors': [ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', ], 'quiet': True,
import logging from telegram.ext import Application, CommandHandler, MessageHandler, filters from config import BOT_TOKEN logging.basicConfig(level=logging.INFO) logger = logging.getLogger()
if == " main ": main() 4.2 Add Inline Keyboard & Callback from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import CallbackQueryHandler async def format_choice(update: Update, context): query = update.callback_query await query.answer() choice = query.data # 'audio' or 'video' context.user_data['format'] = choice url = context.user_data['playlist_url']
# Store URL in user_data context.user_data['playlist_url'] = url buttons = [[ InlineKeyboardButton("🎵 Audio (MP3)", callback_data="audio"), InlineKeyboardButton("🎬 Video (MP4)", callback_data="video") ]] await update.message.reply_text( "Choose format:", reply_markup=InlineKeyboardMarkup(buttons) ) def main(): app = Application.builder().token(BOT_TOKEN).build() app.add_handler(CommandHandler("start", start)) app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) app.run_polling()