Repository: aryanvikash/Youtube-Downloader-Bot
Branch: master
Commit: 9910023d39d3
Files: 17
Total size: 15.1 KB
Directory structure:
gitextract_chqsk1l4/
├── .gitignore
├── Procfile
├── README.md
├── app.json
├── bot/
│ ├── __init__.py
│ └── __main__.py
├── config.py
├── helper/
│ ├── ffmfunc.py
│ └── ytdlfunc.py
├── plugins/
│ ├── help.py
│ ├── start.py
│ ├── youtube.py
│ └── youtube_callback_data.py
├── requirements.txt
├── runtime.txt
├── start.sh
└── utils/
└── util.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
downloads/*
*.session
__pycache__/
*.pyc
log.txt
venv/
.idea/
pythonenv3.8/
.vscode/
================================================
FILE: Procfile
================================================
worker : chmod +x start.sh && bash start.sh
================================================
FILE: README.md
================================================
# Youtube Dl bot 😉
## Prerequisite
ffmpeg
## install dependencies
pip3 install -r requirements.txt
## Setup Bot
- Change configuration config.py File
- install dependencies
- python3 -m bot
## Thanks ❤️
* [Spechide](https://telegram.dog/SpEcHIDe) for his [AnyDlBot](https://github.com/SpEcHiDe/AnyDLBot)
* [HasibulKabir](https://telegram.dog/HasibulKabir)
[](https://heroku.com/deploy?template=https://github.com/aryanvikash/Youtube-Downloader-Bot/tree/master)
================================================
FILE: app.json
================================================
{
"name": "YouTube Downloader Bot",
"description": "A telegram bot To Download Youtube Files ",
"logo": "https://telegra.ph/file/6a3b1febade2313dd0dca.jpg",
"keywords": [
"Youtube","YoutubeDownloader"
],
"repository": "https://github.com/aryanvikash/Youtube-Downloader-Bot",
"success_url": "https://t.me/youtubdlbot",
"website": "https://github.com/aryanvikash/Youtube-Downloader-Bot",
"env": {
"API_ID": {"description": "Get this value from https://my.telegram.org", "required": true},
"API_HASH": {"description": "Get this value from https://my.telegram.org" , "required": true},
"BOT_TOKEN": {"description": "Get Bot Token From BotFather Bot","required": true}
},
"buildpacks": [
{"url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git"},
{"url": "heroku/python"}
],
"formation": {
"worker": {
"quantity": 1,
"size": "free"
}
},
"stack": "heroku-20"
}
================================================
FILE: bot/__init__.py
================================================
users ={}
user_time = {}
================================================
FILE: bot/__main__.py
================================================
from pyrogram import Client
import config
DOWNLOAD_LOCATION = "./Downloads"
BOT_TOKEN = config.BOT_TOKEN
APP_ID = config.APP_ID
API_HASH = config.API_HASH
plugins = dict(
root="plugins",
)
Client(
"YouTubeDlBot",
bot_token=BOT_TOKEN,
api_id=APP_ID,
api_hash=API_HASH,
plugins=plugins,
workers=100
).run()
================================================
FILE: config.py
================================================
import os
BOT_TOKEN = os.environ.get("BOT_TOKEN")
APP_ID = int(os.environ.get("API_ID"))
API_HASH = os.environ.get("API_HASH")
youtube_next_fetch = 0 # time in minute
EDIT_TIME = 5
================================================
FILE: helper/ffmfunc.py
================================================
import subprocess as sp
import json
def probe(vid_file_path):
"""
Give a json from ffprobe command line
@vid_file_path : The absolute (full) path of the video file, string.
"""
if type(vid_file_path) != str:
raise Exception('Give ffprobe a full file path of the file')
command = ["ffprobe",
"-loglevel", "quiet",
"-print_format", "json",
"-show_format",
"-show_streams",
vid_file_path
]
pipe = sp.Popen(command, stdout=sp.PIPE, stderr=sp.STDOUT)
out, err = pipe.communicate()
return json.loads(out)
def duration(vid_file_path):
"""
Video's duration in seconds, return a float number
"""
_json = probe(vid_file_path)
if 'format' in _json:
if 'duration' in _json['format']:
return float(_json['format']['duration'])
if 'streams' in _json:
# commonly stream 0 is the video
for s in _json['streams']:
if 'duration' in s:
return float(s['duration'])
raise Exception('duration Not found')
if __name__ == "__main__":
print(duration("examplefile.mp4")) # 10.008
================================================
FILE: helper/ytdlfunc.py
================================================
from __future__ import unicode_literals
from pyrogram import Client, Filters, StopPropagation, InlineKeyboardButton, InlineKeyboardMarkup
import youtube_dl
from utils.util import humanbytes
import asyncio
def buttonmap(item):
quality = item['format']
if "audio" in quality:
return [InlineKeyboardButton(f"{quality} 🎵 {humanbytes(item['filesize'])}",
callback_data=f"ytdata||audio||{item['format_id']}||{item['yturl']}")]
else:
return [InlineKeyboardButton(f"{quality} 📹 {humanbytes(item['filesize'])}",
callback_data=f"ytdata||video||{item['format_id']}||{item['yturl']}")]
# Return a array of Buttons
def create_buttons(quailitylist):
return map(buttonmap, quailitylist)
# extract Youtube info
def extractYt(yturl):
ydl = youtube_dl.YoutubeDL()
with ydl:
qualityList = []
r = ydl.extract_info(yturl, download=False)
for format in r['formats']:
# Filter dash video(without audio)
if not "dash" in str(format['format']).lower():
qualityList.append(
{"format": format['format'], "filesize": format['filesize'], "format_id": format['format_id'],
"yturl": yturl})
return r['title'], r['thumbnail'], qualityList
# Need to work on progress
# def downloadyt(url, fmid, custom_progress):
# ydl_opts = {
# 'format': f"{fmid}+bestaudio",
# "outtmpl": "test+.%(ext)s",
# 'noplaylist': True,
# 'progress_hooks': [custom_progress],
# }
# with youtube_dl.YoutubeDL(ydl_opts) as ydl:
# ydl.download([url])
# https://github.com/SpEcHiDe/AnyDLBot
async def downloadvideocli(command_to_exec):
process = await asyncio.create_subprocess_exec(
*command_to_exec,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE, )
stdout, stderr = await process.communicate()
e_response = stderr.decode().strip()
t_response = stdout.decode().strip()
print(e_response)
filename = t_response.split("Merging formats into")[-1].split('"')[1]
return filename
async def downloadaudiocli(command_to_exec):
process = await asyncio.create_subprocess_exec(
*command_to_exec,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE, )
stdout, stderr = await process.communicate()
e_response = stderr.decode().strip()
t_response = stdout.decode().strip()
print("Download error:", e_response)
return t_response.split("Destination")[-1].split("Deleting")[0].split(":")[-1].strip()
================================================
FILE: plugins/help.py
================================================
from pyrogram import Client, Filters
@Client.on_message(Filters.command(["help"]))
async def start(client, message):
helptxt = f"Currently Only supports Youtube Single (No playlist) Just Send Youtube Url"
await message.reply_text(helptxt)
================================================
FILE: plugins/start.py
================================================
from pyrogram import Client, Filters, StopPropagation, InlineKeyboardButton, InlineKeyboardMarkup
@Client.on_message(Filters.command(["start"]), group=-2)
async def start(client, message):
# return
joinButton = InlineKeyboardMarkup([
[InlineKeyboardButton("Channel", url="https://t.me/aryan_bots")],
[InlineKeyboardButton(
"Report Bugs 😊", url="https://t.me/aryanvikash")]
])
welcomed = f"Hey <b>{message.from_user.first_name}</b>\n/help for More info"
await message.reply_text(welcomed, reply_markup=joinButton)
raise StopPropagation
================================================
FILE: plugins/youtube.py
================================================
from datetime import datetime, timedelta
from pyrogram import Client, Filters, InlineKeyboardMarkup, InlineKeyboardButton
from bot import user_time
from config import youtube_next_fetch
from helper.ytdlfunc import extractYt, create_buttons
import wget
import os
from PIL import Image
ytregex = r"^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$"
@Client.on_message(Filters.regex(ytregex))
async def ytdl(_, message):
userLastDownloadTime = user_time.get(message.chat.id)
try:
if userLastDownloadTime > datetime.now():
wait_time = round((userLastDownloadTime - datetime.now()).total_seconds() / 60, 2)
await message.reply_text(f"`Wait {wait_time} Minutes before next Request`")
return
except:
pass
url = message.text.strip()
await message.reply_chat_action("typing")
try:
title, thumbnail_url, formats = extractYt(url)
now = datetime.now()
user_time[message.chat.id] = now + \
timedelta(minutes=youtube_next_fetch)
except Exception:
await message.reply_text("`Failed To Fetch Youtube Data... 😔 \nPossible Youtube Blocked server ip \n#error`")
return
buttons = InlineKeyboardMarkup(list(create_buttons(formats)))
sentm = await message.reply_text("Processing Youtube Url 🔎 🔎 🔎")
try:
# Todo add webp image support in thumbnail by default not supported by pyrogram
# https://www.youtube.com/watch?v=lTTajzrSkCw
img = wget.download(thumbnail_url)
im = Image.open(img).convert("RGB")
output_directory = os.path.join(os.getcwd(), "downloads", str(message.chat.id))
if not os.path.isdir(output_directory):
os.makedirs(output_directory)
thumb_image_path = f"{output_directory}.jpg"
im.save(thumb_image_path,"jpeg")
await message.reply_photo(thumb_image_path, caption=title, reply_markup=buttons)
await sentm.delete()
except Exception as e:
print(e)
try:
thumbnail_url = "https://telegra.ph/file/ce37f8203e1903feed544.png"
await message.reply_photo(thumbnail_url, caption=title, reply_markup=buttons)
except Exception as e:
await sentm.edit(
f"<code>{e}</code> #Error")
================================================
FILE: plugins/youtube_callback_data.py
================================================
import asyncio
import os
from pyrogram import (Client,
InlineKeyboardButton,
InlineKeyboardMarkup,
ContinuePropagation,
InputMediaDocument,
InputMediaVideo,
InputMediaAudio)
from helper.ffmfunc import duration
from helper.ytdlfunc import downloadvideocli, downloadaudiocli
from PIL import Image
from hachoir.metadata import extractMetadata
from hachoir.parser import createParser
@Client.on_callback_query()
async def catch_youtube_fmtid(c, m):
cb_data = m.data
if cb_data.startswith("ytdata||"):
yturl = cb_data.split("||")[-1]
format_id = cb_data.split("||")[-2]
media_type = cb_data.split("||")[-3].strip()
print(media_type)
if media_type == 'audio':
buttons = InlineKeyboardMarkup([[InlineKeyboardButton(
"Audio", callback_data=f"{media_type}||{format_id}||{yturl}"), InlineKeyboardButton("Document",
callback_data=f"docaudio||{format_id}||{yturl}")]])
else:
buttons = InlineKeyboardMarkup([[InlineKeyboardButton(
"Video", callback_data=f"{media_type}||{format_id}||{yturl}"), InlineKeyboardButton("Document",
callback_data=f"docvideo||{format_id}||{yturl}")]])
await m.edit_message_reply_markup(buttons)
else:
raise ContinuePropagation
@Client.on_callback_query()
async def catch_youtube_dldata(c, q):
cb_data = q.data.strip()
#print(q.message.chat.id)
# Callback Data Check
yturl = cb_data.split("||")[-1]
format_id = cb_data.split("||")[-2]
thumb_image_path = "/app/downloads" + \
"/" + str(q.message.chat.id) + ".jpg"
print(thumb_image_path)
if os.path.exists(thumb_image_path):
width = 0
height = 0
metadata = extractMetadata(createParser(thumb_image_path))
#print(metadata)
if metadata.has("width"):
width = metadata.get("width")
if metadata.has("height"):
height = metadata.get("height")
img = Image.open(thumb_image_path)
if cb_data.startswith(("audio", "docaudio", "docvideo")):
img.resize((320, height))
else:
img.resize((90, height))
img.save(thumb_image_path, "JPEG")
# print(thumb_image_path)
if not cb_data.startswith(("video", "audio", "docaudio", "docvideo")):
print("no data found")
raise ContinuePropagation
filext = "%(title)s.%(ext)s"
userdir = os.path.join(os.getcwd(), "downloads", str(q.message.chat.id))
if not os.path.isdir(userdir):
os.makedirs(userdir)
await q.edit_message_reply_markup(
InlineKeyboardMarkup([[InlineKeyboardButton("Downloading...", callback_data="down")]]))
filepath = os.path.join(userdir, filext)
# await q.edit_message_reply_markup([[InlineKeyboardButton("Processing..")]])
audio_command = [
"youtube-dl",
"-c",
"--prefer-ffmpeg",
"--extract-audio",
"--audio-format", "mp3",
"--audio-quality", format_id,
"-o", filepath,
yturl,
]
video_command = [
"youtube-dl",
"-c",
"--embed-subs",
"-f", f"{format_id}+bestaudio",
"-o", filepath,
"--hls-prefer-ffmpeg", yturl]
loop = asyncio.get_event_loop()
med = None
if cb_data.startswith("audio"):
filename = await downloadaudiocli(audio_command)
med = InputMediaAudio(
media=filename,
thumb=thumb_image_path,
caption=os.path.basename(filename),
title=os.path.basename(filename)
)
if cb_data.startswith("video"):
filename = await downloadvideocli(video_command)
dur = round(duration(filename))
med = InputMediaVideo(
media=filename,
duration=dur,
width=width,
height=height,
thumb=thumb_image_path,
caption=os.path.basename(filename),
supports_streaming=True
)
if cb_data.startswith("docaudio"):
filename = await downloadaudiocli(audio_command)
med = InputMediaDocument(
media=filename,
thumb=thumb_image_path,
caption=os.path.basename(filename),
)
if cb_data.startswith("docvideo"):
filename = await downloadvideocli(video_command)
dur = round(duration(filename))
med = InputMediaDocument(
media=filename,
thumb=thumb_image_path,
caption=os.path.basename(filename),
)
if med:
loop.create_task(send_file(c, q, med, filename))
else:
print("med not found")
async def send_file(c, q, med, filename):
print(med)
try:
await q.edit_message_reply_markup(
InlineKeyboardMarkup([[InlineKeyboardButton("Uploading...", callback_data="down")]]))
await c.send_chat_action(chat_id=q.message.chat.id, action="upload_document")
# this one is not working
await q.edit_message_media(media=med)
except Exception as e:
print(e)
await q.edit_message_text(e)
finally:
try:
os.remove(filename)
os.remove(thumb_image_path)
except:
pass
================================================
FILE: requirements.txt
================================================
TgCrypto
https://raw.githubusercontent.com/HasibulKabir/pyrogramarchive/master/pyrogramasyncv0.18.0.zip
git+https://github.com/ytdl-org/youtube-dl
wget
pillow
hachoir
================================================
FILE: runtime.txt
================================================
python-3.8.7
================================================
FILE: start.sh
================================================
python3 -m bot
================================================
FILE: utils/util.py
================================================
def humanbytes(num, suffix='B'):
if num is None:
num = 0
else:
num = int(num)
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
gitextract_chqsk1l4/
├── .gitignore
├── Procfile
├── README.md
├── app.json
├── bot/
│ ├── __init__.py
│ └── __main__.py
├── config.py
├── helper/
│ ├── ffmfunc.py
│ └── ytdlfunc.py
├── plugins/
│ ├── help.py
│ ├── start.py
│ ├── youtube.py
│ └── youtube_callback_data.py
├── requirements.txt
├── runtime.txt
├── start.sh
└── utils/
└── util.py
SYMBOL INDEX (14 symbols across 7 files) FILE: helper/ffmfunc.py function probe (line 5) | def probe(vid_file_path): function duration (line 26) | def duration(vid_file_path): FILE: helper/ytdlfunc.py function buttonmap (line 8) | def buttonmap(item): function create_buttons (line 18) | def create_buttons(quailitylist): function extractYt (line 23) | def extractYt(yturl): function downloadvideocli (line 53) | async def downloadvideocli(command_to_exec): function downloadaudiocli (line 67) | async def downloadaudiocli(command_to_exec): FILE: plugins/help.py function start (line 5) | async def start(client, message): FILE: plugins/start.py function start (line 5) | async def start(client, message): FILE: plugins/youtube.py function ytdl (line 14) | async def ytdl(_, message): FILE: plugins/youtube_callback_data.py function catch_youtube_fmtid (line 19) | async def catch_youtube_fmtid(c, m): function catch_youtube_dldata (line 42) | async def catch_youtube_dldata(c, q): function send_file (line 148) | async def send_file(c, q, med, filename): FILE: utils/util.py function humanbytes (line 1) | def humanbytes(num, suffix='B'):
Condensed preview — 17 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (17K chars).
[
{
"path": ".gitignore",
"chars": 84,
"preview": "downloads/*\n*.session\n__pycache__/\n*.pyc\nlog.txt\nvenv/\n.idea/\npythonenv3.8/\n.vscode/"
},
{
"path": "Procfile",
"chars": 44,
"preview": "worker : chmod +x start.sh && bash start.sh\n"
},
{
"path": "README.md",
"chars": 551,
"preview": "# Youtube Dl bot 😉\n## Prerequisite\n ffmpeg\n \n \n## install dependencies\n pip3 install -r requirements.txt\n\n\n## "
},
{
"path": "app.json",
"chars": 1001,
"preview": "{\n \"name\": \"YouTube Downloader Bot\",\n \"description\": \"A telegram bot To Download Youtube Files \",\n \"logo\": \"https://t"
},
{
"path": "bot/__init__.py",
"chars": 27,
"preview": "users ={}\nuser_time = {}\n\n\n"
},
{
"path": "bot/__main__.py",
"chars": 338,
"preview": "from pyrogram import Client\nimport config\n\nDOWNLOAD_LOCATION = \"./Downloads\"\nBOT_TOKEN = config.BOT_TOKEN\n\nAPP_ID = conf"
},
{
"path": "config.py",
"chars": 186,
"preview": "import os\n\nBOT_TOKEN = os.environ.get(\"BOT_TOKEN\")\nAPP_ID = int(os.environ.get(\"API_ID\"))\nAPI_HASH = os.environ.get(\"AP"
},
{
"path": "helper/ffmfunc.py",
"chars": 1196,
"preview": "import subprocess as sp\nimport json\n\n\ndef probe(vid_file_path):\n \"\"\"\n Give a json from ffprobe command line\n @v"
},
{
"path": "helper/ytdlfunc.py",
"chars": 2765,
"preview": "from __future__ import unicode_literals\nfrom pyrogram import Client, Filters, StopPropagation, InlineKeyboardButton, Inl"
},
{
"path": "plugins/help.py",
"chars": 250,
"preview": "from pyrogram import Client, Filters\n\n\n@Client.on_message(Filters.command([\"help\"]))\nasync def start(client, message):\n "
},
{
"path": "plugins/start.py",
"chars": 589,
"preview": "from pyrogram import Client, Filters, StopPropagation, InlineKeyboardButton, InlineKeyboardMarkup\n\n\n@Client.on_message(F"
},
{
"path": "plugins/youtube.py",
"chars": 2365,
"preview": "from datetime import datetime, timedelta\nfrom pyrogram import Client, Filters, InlineKeyboardMarkup, InlineKeyboardButto"
},
{
"path": "plugins/youtube_callback_data.py",
"chars": 5537,
"preview": "import asyncio\nimport os\n\nfrom pyrogram import (Client,\n InlineKeyboardButton,\n "
},
{
"path": "requirements.txt",
"chars": 168,
"preview": "\nTgCrypto\nhttps://raw.githubusercontent.com/HasibulKabir/pyrogramarchive/master/pyrogramasyncv0.18.0.zip\ngit+https://git"
},
{
"path": "runtime.txt",
"chars": 13,
"preview": "python-3.8.7\n"
},
{
"path": "start.sh",
"chars": 16,
"preview": "python3 -m bot\n"
},
{
"path": "utils/util.py",
"chars": 311,
"preview": "def humanbytes(num, suffix='B'):\n if num is None:\n num = 0\n else:\n num = int(num)\n\n for unit in ["
}
]
About this extraction
This page contains the full source code of the aryanvikash/Youtube-Downloader-Bot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 17 files (15.1 KB), approximately 4.1k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.