├── runtime.txt
├── start.sh
├── Procfile
├── MyTestBotZ
├── __init__.py
├── a.txt
├── helper
│ ├── a.txt
│ ├── ffmfunc.py
│ └── ytdlfunc.py
├── plugins
│ ├── a.txt
│ ├── start.py
│ ├── youtube.py
│ └── youtube_callback_data.py
└── __main__.py
├── .gitignore
├── requirements.txt
├── config.py
├── app.json
└── README.md
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.8.7
2 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | python3 -m MyTestBotZ
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker : chmod +x start.sh && bash start.sh
2 |
--------------------------------------------------------------------------------
/MyTestBotZ/__init__.py:
--------------------------------------------------------------------------------
1 | users ={}
2 | user_time = {}
3 |
4 |
5 |
--------------------------------------------------------------------------------
/MyTestBotZ/a.txt:
--------------------------------------------------------------------------------
1 | ##### This is Modified version of YouTube Download Bot #####
2 |
--------------------------------------------------------------------------------
/MyTestBotZ/helper/a.txt:
--------------------------------------------------------------------------------
1 | ##### This is Modified version of YouTube Download Bot #####
2 |
--------------------------------------------------------------------------------
/MyTestBotZ/plugins/a.txt:
--------------------------------------------------------------------------------
1 | ##### This is Modified version of YouTube Download Bot #####
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | downloads/*
2 | *.session
3 | __pycache__/
4 | *.pyc
5 | log.txt
6 | venv/
7 | .idea/
8 | pythonenv3.8/
9 | .vscode/
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 |
2 | TgCrypto
3 | https://raw.githubusercontent.com/HasibulKabir/pyrogramarchive/master/pyrogramasyncv0.18.0.zip
4 | git+https://github.com/OO7ROBot/youtube_dl
5 | wget
6 | pillow
7 | hachoir
8 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | BOT_TOKEN = os.environ.get("BOT_TOKEN")
4 | APP_ID = int(os.environ.get("API_ID"))
5 | API_HASH = os.environ.get("API_HASH")
6 |
7 | youtube_next_fetch = 0.5 # time in minute
8 |
9 |
10 | EDIT_TIME = 5
11 |
--------------------------------------------------------------------------------
/MyTestBotZ/__main__.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client
2 | import config
3 |
4 | DOWNLOAD_LOCATION = "./Downloads"
5 | BOT_TOKEN = config.BOT_TOKEN
6 | APP_ID = config.APP_ID
7 | API_HASH = config.API_HASH
8 |
9 | plugins = dict(
10 | root="MyTestBotZ/plugins",
11 | )
12 |
13 | Client(
14 | "YouTubeDLBot",
15 | bot_token=BOT_TOKEN,
16 | api_id=APP_ID,
17 | api_hash=API_HASH,
18 | plugins=plugins,
19 | workers=100
20 | ).run()
21 |
--------------------------------------------------------------------------------
/MyTestBotZ/plugins/start.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, Filters, StopPropagation, InlineKeyboardButton, InlineKeyboardMarkup
2 |
3 |
4 | @Client.on_message(Filters.command(["start"]), group=-2)
5 | async def start(client, message):
6 | # return
7 | joinButton = InlineKeyboardMarkup([
8 | [InlineKeyboardButton("Channel", url="https://t.me/MyTestBotz"), InlineKeyboardButton("Creator", url="https://telegram.me/OO7ROBot") ],
9 | [InlineKeyboardButton(
10 | "🍿 Source Code 🍿", url="https://github.com/OO7ROBot/YoutubeDownloaderBot")]
11 | ])
12 | welcomed = f"""Hey {message.from_user.first_name}\nA Simple YouTube Downloader Bot that can:
13 | ➠ Download YouTube videos
14 | ➠ Download audio from YouTube videos \n\n Made with ♥️ by @MyTestBotZ"""
15 | await message.reply_text(welcomed, reply_markup=joinButton)
16 | raise StopPropagation
17 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "YouTube Downloader Bot",
3 | "description": "A telegram bot To Download Youtube Files ",
4 | "logo": "https://telegra.ph/file/6a3b1febade2313dd0dca.jpg",
5 | "keywords": [
6 | "Youtube","YoutubeDownloader"
7 | ],
8 | "repository": "https://github.com/OO7ROBOT/YoutubeDownloaderBot",
9 | "success_url": "https://t.me/tg_utubebot",
10 | "website": "https://github.com/OO7ROBot/YoutubeDownloaderBot",
11 | "env": {
12 | "API_ID": {"description": "Get this value from https://my.telegram.org", "required": true},
13 | "API_HASH": {"description": "Get this value from https://my.telegram.org" , "required": true},
14 | "BOT_TOKEN": {"description": "Get Bot Token From BotFather Bot","required": true},
15 | "AUTH_USERS": {"description": "Authorize user(s) ID separate by space","required": true}
16 | },
17 | "buildpacks": [
18 | {"url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git"},
19 | {"url": "heroku/python"}
20 | ],
21 | "formation": {
22 | "worker": {
23 | "quantity": 1,
24 | "size": "free"
25 | }
26 | },
27 | "stack": "heroku-20"
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/MyTestBotZ/helper/ffmfunc.py:
--------------------------------------------------------------------------------
1 | import subprocess as sp
2 | import json
3 |
4 |
5 | def probe(vid_file_path):
6 | """
7 | Give a json from ffprobe command line
8 | @vid_file_path : The absolute (full) path of the video file, string.
9 | """
10 | if type(vid_file_path) != str:
11 | raise Exception('Give ffprobe a full file path of the file')
12 |
13 | command = ["ffprobe",
14 | "-loglevel", "quiet",
15 | "-print_format", "json",
16 | "-show_format",
17 | "-show_streams",
18 | vid_file_path
19 | ]
20 |
21 | pipe = sp.Popen(command, stdout=sp.PIPE, stderr=sp.STDOUT)
22 | out, err = pipe.communicate()
23 | return json.loads(out)
24 |
25 |
26 | def duration(vid_file_path):
27 | """
28 | Video's duration in seconds, return a float number
29 | """
30 | _json = probe(vid_file_path)
31 |
32 | if 'format' in _json:
33 | if 'duration' in _json['format']:
34 | return float(_json['format']['duration'])
35 |
36 | if 'streams' in _json:
37 | # commonly stream 0 is the video
38 | for s in _json['streams']:
39 | if 'duration' in s:
40 | return float(s['duration'])
41 |
42 | raise Exception('duration Not found')
43 |
44 |
45 | if __name__ == "__main__":
46 | print(duration("examplefile.mp4")) # 10.008
47 |
--------------------------------------------------------------------------------
/MyTestBotZ/plugins/youtube.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timedelta
2 | from pyrogram import Client, Filters, InlineKeyboardMarkup, InlineKeyboardButton
3 | from MyTestBotZ import user_time
4 | from config import youtube_next_fetch
5 | from MyTestBotZ.helper.ytdlfunc import extractYt, create_buttons
6 | import wget
7 | import os
8 | from PIL import Image
9 | from pyrogram.errors import UserNotParticipant, UserBannedInChannel
10 | AUTH_USERS = set(int(x) for x in os.environ.get("AUTH_USERS", "1248974748 1401477467").split())
11 |
12 |
13 | ytregex = r"^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$"
14 |
15 | @Client.on_message(Filters.regex(ytregex))
16 | async def ytdl(bot, message):
17 | if message.from_user.id not in AUTH_USERS:
18 | return
19 | userLastDownloadTime = user_time.get(message.chat.id)
20 | try:
21 | if userLastDownloadTime > datetime.now():
22 | wait_time = round((userLastDownloadTime - datetime.now()).total_seconds() / 60, 2)
23 | await message.reply_text(f"`Wait {wait_time} Minutes before next Request`")
24 | return
25 | except:
26 | pass
27 |
28 | url = message.text.strip()
29 | await message.reply_chat_action("typing")
30 | try:
31 | title, thumbnail_url, formats = extractYt(url)
32 |
33 | now = datetime.now()
34 | user_time[message.chat.id] = now + \
35 | timedelta(minutes=youtube_next_fetch)
36 |
37 | except Exception:
38 | await message.reply_text("`Failed To Fetch Youtube Data... 😔 \nPossible Youtube Blocked server ip \n#error`")
39 | return
40 | buttons = InlineKeyboardMarkup(list(create_buttons(formats)))
41 | sentm = await message.reply_text("Processing Youtube Url 🔎 🔎 🔎")
42 | try:
43 | # Todo add webp image support in thumbnail by default not supported by pyrogram
44 | # https://www.youtube.com/watch?v=lTTajzrSkCw
45 | img = wget.download(thumbnail_url)
46 | im = Image.open(img).convert("RGB")
47 | output_directory = os.path.join(os.getcwd(), "downloads", str(message.chat.id))
48 | if not os.path.isdir(output_directory):
49 | os.makedirs(output_directory)
50 | thumb_image_path = f"{output_directory}.jpg"
51 | im.save(thumb_image_path,"jpeg")
52 | await message.reply_photo(thumb_image_path, caption=title, reply_markup=buttons)
53 | await sentm.delete()
54 | except Exception as e:
55 | print(e)
56 | try:
57 | thumbnail_url = "https://telegra.ph/file/ce37f8203e1903feed544.png"
58 | await message.reply_photo(thumbnail_url, caption=title, reply_markup=buttons)
59 | except Exception as e:
60 | await sentm.edit(
61 | f"{e} #Error")
62 |
63 |
--------------------------------------------------------------------------------
/MyTestBotZ/helper/ytdlfunc.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 | from pyrogram import Client, Filters, StopPropagation, InlineKeyboardButton, InlineKeyboardMarkup
3 | import yt_dlp
4 | import asyncio
5 |
6 | def humanbytes(num, suffix='B'):
7 | if num is None:
8 | num = 0
9 | else:
10 | num = int(num)
11 |
12 | for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
13 | if abs(num) < 1024.0:
14 | return "%3.1f%s%s" % (num, unit, suffix)
15 | num /= 1024.0
16 | return "%.1f%s%s" % (num, 'Yi', suffix)
17 |
18 |
19 | def buttonmap(item):
20 | quality = item['format']
21 | if "audio" in quality:
22 | return [InlineKeyboardButton(f"{quality} 🎵 {humanbytes(item['filesize'])}",
23 | callback_data=f"ytdata||audio||{item['format_id']}||{item['yturl']}")]
24 | else:
25 | return [InlineKeyboardButton(f"{quality} 📹 {humanbytes(item['filesize'])}",
26 | callback_data=f"ytdata||video||{item['format_id']}||{item['yturl']}")]
27 |
28 | def create_buttons(quailitylist):
29 | return map(buttonmap, quailitylist)
30 |
31 | def extractYt(yturl):
32 | ydl = yt_dlp.YoutubeDL()
33 | with ydl:
34 | qualityList = []
35 | r = ydl.extract_info(yturl, download=False)
36 | for format in r['formats']:
37 | # Filter dash video(without audio)
38 | if not "dash" in str(format['format']).lower():
39 | qualityList.append(
40 | {"format": format['format'], "filesize": format['filesize'], "format_id": format['format_id'],
41 | "yturl": yturl})
42 |
43 | return r['title'], r['thumbnail'], qualityList
44 |
45 | async def downloadvideocli(command_to_exec):
46 | process = await asyncio.create_subprocess_exec(
47 | *command_to_exec,
48 | # stdout must a pipe to be accessible as process.stdout
49 | stdout=asyncio.subprocess.PIPE,
50 | stderr=asyncio.subprocess.PIPE, )
51 | stdout, stderr = await process.communicate()
52 | e_response = stderr.decode().strip()
53 | t_response = stdout.decode().strip()
54 | print(e_response)
55 | filename = t_response.split("Merging formats into")[-1].split('"')[1]
56 | return filename
57 |
58 |
59 | async def downloadaudiocli(command_to_exec):
60 | process = await asyncio.create_subprocess_exec(
61 | *command_to_exec,
62 | # stdout must a pipe to be accessible as process.stdout
63 | stdout=asyncio.subprocess.PIPE,
64 | stderr=asyncio.subprocess.PIPE, )
65 | stdout, stderr = await process.communicate()
66 | e_response = stderr.decode().strip()
67 | t_response = stdout.decode().strip()
68 | print("Download error:", e_response)
69 |
70 | return t_response.split("Destination")[-1].split("Deleting")[0].split(":")[-1].strip()
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Youtube Downloader bot
2 | #### modified version of Aryan Vikash [YouTube download bot](https://github.com/aryanvikash/Youtube-Downloader-Bot)
3 |
4 | #
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |