imseldrith commited on
Commit
7ec3e65
·
1 Parent(s): 9934299

Upload 11 files

Browse files
Uploader/README.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h1>FAQ ?</h1>
2
+ <br>
3
+ <h3>How to edit config.py</h3>
4
+
5
+ <p> Here is sample edited config.py for deploy to Locally/VPS. copy and past and edit with your variables</p>
6
+ <p> Go To sample_config.py and edit with your veriable</p>
7
+
8
+ <pre>
9
+ # sample config file
10
+
11
+ class Config(object):
12
+
13
+ # get a token from @BotFather
14
+ BOT_TOKEN = "5568340867:AAGuPzlgwqgHtgqmdL7yt12PRLrXFjt98Zg"
15
+
16
+ # Get these values from my.telegram.org
17
+ API_ID = 12345
18
+ API_HASH = "uPzlgwqgHtgqmdL7yt12PRLrXFj"
19
+
20
+ # No need to change
21
+ DOWNLOAD_LOCATION = "./DOWNLOADS"
22
+ ADL_BOT_RQ = {}
23
+ CHUNK_SIZE = 128
24
+ TG_MAX_FILE_SIZE = 4194304000
25
+ HTTP_PROXY = ""
26
+ PROCESS_MAX_TIMEOUT = 3700
27
+
28
+ # TG Ids
29
+ LOG_CHANNEL = -1001798969594
30
+ OWNER_ID = 1288398723
31
+
32
+ # bot username without @
33
+ BOT_USERNAME = "AdvanceUrlUploaderBot"
34
+
35
+ # auth users
36
+ AUTH_USERS = [OWNER_ID, 1288398722, 1288398724, 1288398725]
37
+ </pre>
Uploader/button.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+ import json
25
+ import time
26
+ import shutil
27
+ import asyncio
28
+ import logging
29
+ import subprocess
30
+
31
+ from pyrogram.types import *
32
+ from datetime import datetime
33
+
34
+ from Uploader.utitles import *
35
+ if bool(os.environ.get("WEBHOOK")):
36
+ from Uploader.config import Config
37
+ else:
38
+ from sample_config import Config
39
+ from Uploader.script import Translation
40
+ from Uploader.functions.ran_text import random_char
41
+ from Uploader.functions.display_progress import progress_for_pyrogram, humanbytes
42
+
43
+ logging.basicConfig(level=logging.DEBUG,
44
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
45
+ logger = logging.getLogger(__name__)
46
+ logging.getLogger("pyrogram").setLevel(logging.WARNING)
47
+
48
+
49
+ logging.basicConfig(level=logging.DEBUG,
50
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
51
+ logger = logging.getLogger(__name__)
52
+
53
+
54
+ async def youtube_dl_call_back(bot, update):
55
+ cb_data = update.data
56
+ # youtube_dl extractors
57
+ tg_send_type, youtube_dl_format, youtube_dl_ext, ranom = cb_data.split("|")
58
+ print(cb_data)
59
+ random1 = random_char(5)
60
+ save_ytdl_json_path = Config.DOWNLOAD_LOCATION + \
61
+ "/" + str(update.from_user.id) + f'{ranom}' + ".json"
62
+ try:
63
+ with open(save_ytdl_json_path, "r", encoding="utf8") as f:
64
+ response_json = json.load(f)
65
+ except (FileNotFoundError) as e:
66
+ await update.message.delete()
67
+ return False
68
+ youtube_dl_url = update.message.reply_to_message.text
69
+ custom_file_name = str(response_json.get("title")) + \
70
+ "_" + youtube_dl_format + "." + youtube_dl_ext
71
+ youtube_dl_username = None
72
+ youtube_dl_password = None
73
+ if "|" in youtube_dl_url:
74
+ url_parts = youtube_dl_url.split("|")
75
+ if len(url_parts) == 2:
76
+ youtube_dl_url = url_parts[0]
77
+ custom_file_name = url_parts[1]
78
+ elif len(url_parts) == 4:
79
+ youtube_dl_url = url_parts[0]
80
+ custom_file_name = url_parts[1]
81
+ youtube_dl_username = url_parts[2]
82
+ youtube_dl_password = url_parts[3]
83
+ else:
84
+ for entity in update.message.reply_to_message.entities:
85
+ if entity.type == "text_link":
86
+ youtube_dl_url = entity.url
87
+ elif entity.type == "url":
88
+ o = entity.offset
89
+ l = entity.length
90
+ youtube_dl_url = youtube_dl_url[o:o + l]
91
+ if youtube_dl_url is not None:
92
+ youtube_dl_url = youtube_dl_url.strip()
93
+ if custom_file_name is not None:
94
+ custom_file_name = custom_file_name.strip()
95
+ # https://stackoverflow.com/a/761825/4723940
96
+ if youtube_dl_username is not None:
97
+ youtube_dl_username = youtube_dl_username.strip()
98
+ if youtube_dl_password is not None:
99
+ youtube_dl_password = youtube_dl_password.strip()
100
+ logger.info(youtube_dl_url)
101
+ logger.info(custom_file_name)
102
+ else:
103
+ for entity in update.message.reply_to_message.entities:
104
+ if entity.type == "text_link":
105
+ youtube_dl_url = entity.url
106
+ elif entity.type == "url":
107
+ o = entity.offset
108
+ l = entity.length
109
+ youtube_dl_url = youtube_dl_url[o:o + l]
110
+ await update.message.edit_caption(
111
+ caption=Translation.DOWNLOAD_START.format(custom_file_name)
112
+
113
+ )
114
+ description = Translation.CUSTOM_CAPTION_UL_FILE
115
+ if "fulltitle" in response_json:
116
+ description = response_json["fulltitle"][:1021]
117
+ # escape Markdown and special characters
118
+ tmp_directory_for_each_user = Config.DOWNLOAD_LOCATION + \
119
+ "/" + str(update.from_user.id) + f'{random1}'
120
+ if not os.path.isdir(tmp_directory_for_each_user):
121
+ os.makedirs(tmp_directory_for_each_user)
122
+ download_directory = f"{tmp_directory_for_each_user}/{custom_file_name}"
123
+
124
+ command_to_exec = []
125
+ if tg_send_type == "audio":
126
+ command_to_exec = [
127
+ "yt-dlp",
128
+ "-c",
129
+ "--max-filesize", str(Config.TG_MAX_FILE_SIZE),
130
+ "--bidi-workaround",
131
+ "--extract-audio",
132
+ "--audio-format", youtube_dl_ext,
133
+ "--audio-quality", youtube_dl_format,
134
+ youtube_dl_url,
135
+ "-o", download_directory
136
+ ]
137
+ else:
138
+ # command_to_exec = ["yt-dlp", "-f", youtube_dl_format, "--hls-prefer-ffmpeg", "--recode-video", "mp4", "-k", youtube_dl_url, "-o", download_directory]
139
+ minus_f_format = youtube_dl_format
140
+ if "youtu" in youtube_dl_url:
141
+ minus_f_format = f"{youtube_dl_format}+bestaudio"
142
+ command_to_exec = [
143
+ "yt-dlp",
144
+ "-c",
145
+ "--max-filesize", str(Config.TG_MAX_FILE_SIZE),
146
+ "--embed-subs",
147
+ "-f", minus_f_format,
148
+ "--bidi-workaround",
149
+ youtube_dl_url,
150
+ "-o", download_directory
151
+ ]
152
+
153
+ if Config.HTTP_PROXY != "":
154
+ command_to_exec.append("--proxy")
155
+ command_to_exec.append(Config.HTTP_PROXY)
156
+ if youtube_dl_username is not None:
157
+ command_to_exec.append("--username")
158
+ command_to_exec.append(youtube_dl_username)
159
+ if youtube_dl_password is not None:
160
+ command_to_exec.append("--password")
161
+ command_to_exec.append(youtube_dl_password)
162
+ command_to_exec.append("--no-warnings")
163
+ # command_to_exec.append("--quiet")
164
+ logger.info(command_to_exec)
165
+ start = datetime.now()
166
+ process = await asyncio.create_subprocess_exec(
167
+ *command_to_exec,
168
+ # stdout must a pipe to be accessible as process.stdout
169
+ stdout=asyncio.subprocess.PIPE,
170
+ stderr=asyncio.subprocess.PIPE,
171
+ )
172
+ # Wait for the subprocess to finish
173
+ stdout, stderr = await process.communicate()
174
+ e_response = stderr.decode().strip()
175
+ t_response = stdout.decode().strip()
176
+ logger.info(e_response)
177
+ logger.info(t_response)
178
+ ad_string_to_replace = "please report this issue on https://github.com/kalanakt/All-Url-Uploader/issues"
179
+ if e_response and ad_string_to_replace in e_response:
180
+ error_message = e_response.replace(ad_string_to_replace, "")
181
+ await update.message.edit_caption(
182
+
183
+ text=error_message
184
+ )
185
+ return False
186
+
187
+ if t_response:
188
+ logger.info(t_response)
189
+ try:
190
+ os.remove(save_ytdl_json_path)
191
+ except FileNotFoundError as exc:
192
+ pass
193
+
194
+ end_one = datetime.now()
195
+ time_taken_for_download = (end_one - start).seconds
196
+ file_size = Config.TG_MAX_FILE_SIZE + 1
197
+ try:
198
+ file_size = os.stat(download_directory).st_size
199
+ except FileNotFoundError as exc:
200
+ download_directory = os.path.splitext(
201
+ download_directory)[0] + "." + "mkv"
202
+ # https://stackoverflow.com/a/678242/4723940
203
+ file_size = os.stat(download_directory).st_size
204
+
205
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{update.from_user.id}.jpg"
206
+ thumb = download_location if os.path.isfile(
207
+ download_location) else None
208
+
209
+ if ((file_size > Config.TG_MAX_FILE_SIZE)):
210
+ await update.message.edit_caption(
211
+
212
+ caption=Translation.RCHD_TG_API_LIMIT.format(
213
+ time_taken_for_download, humanbytes(file_size))
214
+
215
+ )
216
+ else:
217
+ await update.message.edit_caption(
218
+ caption=Translation.UPLOAD_START.format(custom_file_name)
219
+
220
+ )
221
+ start_time = time.time()
222
+ if tg_send_type == "video":
223
+ width, height, duration = await Mdata01(download_directory)
224
+ await update.message.reply_video(
225
+ # chat_id=update.message.chat.id,
226
+ video=download_directory,
227
+ caption=description,
228
+ duration=duration,
229
+ width=width,
230
+ height=height,
231
+ supports_streaming=True,
232
+ thumb=thumb,
233
+ # reply_to_message_id=update.id,
234
+ progress=progress_for_pyrogram,
235
+ progress_args=(
236
+ Translation.UPLOAD_START,
237
+ update.message,
238
+ start_time
239
+ )
240
+ )
241
+ elif tg_send_type == "audio":
242
+ duration = await Mdata03(download_directory)
243
+ await update.message.reply_audio(
244
+ # chat_id=update.message.chat.id,
245
+ audio=download_directory,
246
+ caption=description,
247
+ duration=duration,
248
+ thumb=thumb,
249
+ # reply_to_message_id=update.id,
250
+ progress=progress_for_pyrogram,
251
+ progress_args=(
252
+ Translation.UPLOAD_START,
253
+ update.message,
254
+ start_time
255
+ )
256
+ )
257
+ elif tg_send_type == "vm":
258
+ width, duration = await Mdata02(download_directory)
259
+ await update.message.reply_video_note(
260
+ # chat_id=update.message.chat.id,
261
+ video_note=download_directory,
262
+ duration=duration,
263
+ length=width,
264
+ thumb=thumb,
265
+ # reply_to_message_id=update.id,
266
+ progress=progress_for_pyrogram,
267
+ progress_args=(
268
+ Translation.UPLOAD_START,
269
+ update.message,
270
+ start_time
271
+ )
272
+ )
273
+ else:
274
+ await update.message.reply_document(
275
+ # chat_id=update.message.chat.id,
276
+ document=download_directory,
277
+ caption=description,
278
+ # parse_mode=enums.ParseMode.HTML,
279
+ # reply_to_message_id=update.id,
280
+ thumb=thumb,
281
+ progress=progress_for_pyrogram,
282
+ progress_args=(
283
+ Translation.UPLOAD_START,
284
+ update.message,
285
+ start_time
286
+ )
287
+ )
288
+
289
+ end_two = datetime.now()
290
+ time_taken_for_upload = (end_two - end_one).seconds
291
+ try:
292
+ shutil.rmtree(tmp_directory_for_each_user)
293
+ except Exception:
294
+ pass
295
+ await update.message.edit_caption(
296
+ caption=Translation.AFTER_SUCCESSFUL_UPLOAD_MSG_WITH_TS.format(
297
+ time_taken_for_download, time_taken_for_upload)
298
+
299
+ )
300
+
301
+ logger.info(f"Downloaded in: {str(time_taken_for_download)}")
302
+ logger.info(f"Uploaded in: {str(time_taken_for_upload)}")
Uploader/callbacks.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+ from Uploader.functions.display_progress import progress_for_pyrogram, humanbytes
25
+ if bool(os.environ.get("WEBHOOK")):
26
+ from Uploader.config import Config
27
+ else:
28
+ from sample_config import Config
29
+ from Uploader.dl_button import ddl_call_back
30
+ from Uploader.button import youtube_dl_call_back
31
+ from Uploader.script import Translation
32
+ from pyrogram import Client, types
33
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
34
+ import logging
35
+ logging.basicConfig(level=logging.DEBUG,
36
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ @Client.on_callback_query()
41
+ async def button(bot, update):
42
+ if update.data == "home":
43
+ await update.message.edit(
44
+ text=Translation.START_TEXT.format(update.from_user.mention),
45
+ reply_markup=Translation.START_BUTTONS,
46
+ # disable_web_page_preview=True
47
+ )
48
+ elif update.data == "help":
49
+ await update.message.edit(
50
+ text=Translation.HELP_TEXT,
51
+ reply_markup=Translation.HELP_BUTTONS,
52
+ # disable_web_page_preview=True
53
+ )
54
+ elif update.data == "about":
55
+ await update.message.edit(
56
+ text=Translation.ABOUT_TEXT,
57
+ reply_markup=Translation.ABOUT_BUTTONS,
58
+ # disable_web_page_preview=True
59
+ )
60
+ elif "close" in update.data:
61
+ await update.message.delete(True)
62
+ elif "|" in update.data:
63
+ await youtube_dl_call_back(bot, update)
64
+ elif "=" in update.data:
65
+ await ddl_call_back(bot, update)
66
+
67
+ else:
68
+ await update.message.delete()
Uploader/commands.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+
25
+ from pyrogram import Client, filters
26
+ from pyrogram.types import Message
27
+ from Uploader.script import Translation
28
+
29
+ if bool(os.environ.get("WEBHOOK")):
30
+ from Uploader.config import Config
31
+ else:
32
+ from sample_config import Config
33
+
34
+
35
+ @Client.on_message(
36
+ filters.command("start") & filters.private,
37
+ )
38
+ async def start_bot(_, m: Message):
39
+ return await m.reply_text(
40
+ Translation.START_TEXT.format(m.from_user.first_name),
41
+ reply_markup=Translation.START_BUTTONS,
42
+ disable_web_page_preview=True,
43
+ quote=True,
44
+ )
45
+
46
+
47
+ @Client.on_message(
48
+ filters.command("help") & filters.private,
49
+ )
50
+ async def help_bot(_, m: Message):
51
+ return await m.reply_text(
52
+ Translation.HELP_TEXT,
53
+ reply_markup=Translation.HELP_BUTTONS,
54
+ disable_web_page_preview=True,
55
+ )
56
+
57
+
58
+ @Client.on_message(
59
+ filters.command("about") & filters.private,
60
+ )
61
+ async def aboutme(_, m: Message):
62
+ return await m.reply_text(
63
+ Translation.ABOUT_TEXT,
64
+ reply_markup=Translation.ABOUT_BUTTONS,
65
+ disable_web_page_preview=True,
66
+ )
Uploader/config.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+
25
+ import logging
26
+
27
+ logging.basicConfig(
28
+ format='%(name)s - %(levelname)s - %(message)s',
29
+ handlers=[logging.FileHandler('log.txt'),
30
+ logging.StreamHandler()],
31
+ level=logging.INFO
32
+ )
33
+
34
+
35
+ class Config(object):
36
+ WEBHOOK = os.environ.get("BOT_TOKEN", False)
37
+ # Get a token from @BotFather
38
+ BOT_TOKEN = os.environ.get("BOT_TOKEN", "")
39
+ # The Telegram API things
40
+ API_ID = int(os.environ.get("API_ID", 12345))
41
+ API_HASH = os.environ.get("API_HASH")
42
+ # Get these values from my.telegram.org
43
+ # Array to store users who are authorized to use the bot
44
+
45
+ # File /video download location
46
+ DOWNLOAD_LOCATION = "./DOWNLOADS"
47
+
48
+ MEGA_EMAIL = os.environ.get("MEGA_EMAIL", "None")
49
+ # If deploying on vps edit the above value as example := Mega_email = "Your-Mega_email-inside-inverted-commas."
50
+
51
+ # This is not necessary! Enter your mega password only if you have a mega.nz account with pro/business features.
52
+ MEGA_PASSWORD = os.environ.get("MEGA_PASSWORD", "None")
53
+ # If deploying on vps edit the above value as example := Mega_password = "Your-Mega_password-inside-inverted-commas."
54
+ # Telegram maximum file upload size
55
+ TG_MAX_FILE_SIZE = 4194304000
56
+
57
+ # Chunk size that should be used with requests
58
+ CHUNK_SIZE = int(os.environ.get("CHUNK_SIZE", 128))
59
+ # Proxy for accessing youtube-dl in GeoRestricted Areas
60
+ # Get your own proxy from https://github.com/rg3/youtube-dl/issues/1091#issuecomment-230163061
61
+ HTTP_PROXY = os.environ.get("HTTP_PROXY", "")
62
+
63
+ # Set timeout for subprcess
64
+ PROCESS_MAX_TIMEOUT = 3700
65
+
66
+ LOG_CHANNEL = int(os.environ.get("LOG_CHANNEL", -100))
67
+ OWNER_ID = int(os.environ.get("OWNER_ID", "12356"))
68
+ BOT_USERNAME = os.environ.get("BOT_USERNAME", "")
69
+ ADL_BOT_RQ = {}
70
+ AUTH_USERS = list({int(x)
71
+ for x in os.environ.get("AUTH_USERS", "0").split()})
72
+ AUTH_USERS.append(OWNER_ID)
Uploader/dl_button.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+ import time
25
+ import aiohttp
26
+ import asyncio
27
+ import logging
28
+
29
+ from datetime import datetime
30
+
31
+ from Uploader.functions.display_progress import progress_for_pyrogram, humanbytes, TimeFormatter
32
+ from Uploader.utitles import *
33
+ from Uploader.script import Translation
34
+ if bool(os.environ.get("WEBHOOK")):
35
+ from Uploader.config import Config
36
+ else:
37
+ from sample_config import Config
38
+
39
+ logging.basicConfig(level=logging.DEBUG,
40
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
41
+ logger = logging.getLogger(__name__)
42
+ logging.getLogger("pyrogram").setLevel(logging.WARNING)
43
+
44
+
45
+ async def ddl_call_back(bot, update): # sourcery skip: low-code-quality
46
+ cb_data = update.data
47
+ tg_send_type, youtube_dl_format, youtube_dl_ext = cb_data.split("=")
48
+ youtube_dl_url = update.message.reply_to_message.text
49
+ custom_file_name = os.path.basename(youtube_dl_url)
50
+ if " " in youtube_dl_url:
51
+ url_parts = youtube_dl_url.split(" * ")
52
+ if len(url_parts) == 2:
53
+ youtube_dl_url = url_parts[0]
54
+ custom_file_name = url_parts[1]
55
+ else:
56
+ for entity in update.message.reply_to_message.entities:
57
+ if entity.type == "text_link":
58
+ youtube_dl_url = entity.url
59
+ elif entity.type == "url":
60
+ o = entity.offset
61
+ l = entity.length
62
+ youtube_dl_url = youtube_dl_url[o:o + l]
63
+ if youtube_dl_url is not None:
64
+ youtube_dl_url = youtube_dl_url.strip()
65
+ if custom_file_name is not None:
66
+ custom_file_name = custom_file_name.strip()
67
+ else:
68
+ for entity in update.message.reply_to_message.entities:
69
+ if entity.type == "text_link":
70
+ youtube_dl_url = entity.url
71
+ elif entity.type == "url":
72
+ o = entity.offset
73
+ l = entity.length
74
+ youtube_dl_url = youtube_dl_url[o:o + l]
75
+ description = custom_file_name
76
+ if f".{youtube_dl_ext}" not in custom_file_name:
77
+ custom_file_name += f'.{youtube_dl_ext}'
78
+ logger.info(youtube_dl_url)
79
+ logger.info(custom_file_name)
80
+ start = datetime.now()
81
+ await bot.edit_message_text(text=Translation.DOWNLOAD_START.format(custom_file_name), chat_id=update.message.chat.id, message_id=update.message.id)
82
+
83
+ tmp_directory_for_each_user = f"{Config.DOWNLOAD_LOCATION}/{str(update.from_user.id)}"
84
+
85
+ if not os.path.isdir(tmp_directory_for_each_user):
86
+ os.makedirs(tmp_directory_for_each_user)
87
+ download_directory = f"{tmp_directory_for_each_user}/{custom_file_name}"
88
+ command_to_exec = []
89
+ async with aiohttp.ClientSession() as session:
90
+ c_time = time.time()
91
+ try:
92
+ await download_coroutine(bot, session, youtube_dl_url, download_directory, update.message.chat.id, update.message.id, c_time)
93
+
94
+ except asyncio.TimeoutError:
95
+ await bot.edit_message_text(text=Translation.SLOW_URL_DECED, chat_id=update.message.chat.id, message_id=update.message.id)
96
+
97
+ return False
98
+ if os.path.exists(download_directory):
99
+ save_ytdl_json_path = f"{Config.DOWNLOAD_LOCATION}/{str(update.message.chat.id)}.json"
100
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{update.from_user.id}.jpg"
101
+ thumb = download_location if os.path.isfile(
102
+ download_location) else None
103
+
104
+ if os.path.exists(save_ytdl_json_path):
105
+ os.remove(save_ytdl_json_path)
106
+ end_one = datetime.now()
107
+ await bot.edit_message_text(text=Translation.UPLOAD_START, chat_id=update.message.chat.id, message_id=update.message.id)
108
+
109
+ file_size = Config.TG_MAX_FILE_SIZE + 1
110
+ try:
111
+ file_size = os.stat(download_directory).st_size
112
+ except FileNotFoundError as exc:
113
+ download_directory = f"{os.path.splitext(download_directory)[0]}.mkv"
114
+ file_size = os.stat(download_directory).st_size
115
+ if file_size > Config.TG_MAX_FILE_SIZE:
116
+ await bot.edit_message_text(chat_id=update.message.chat.id, text=Translation.RCHD_TG_API_LIMIT, message_id=update.message.id)
117
+
118
+ else:
119
+ start_time = time.time()
120
+ if tg_send_type == "video":
121
+ width, height, duration = await Mdata01(download_directory)
122
+ await bot.send_video(chat_id=update.message.chat.id, video=download_directory, thumb=thumb, caption=description, duration=duration, width=width, height=height, supports_streaming=True, reply_to_message_id=update.message.reply_to_message.id, progress=progress_for_pyrogram, progress_args=(Translation.UPLOAD_START, update.message, start_time))
123
+
124
+ elif tg_send_type == "audio":
125
+ duration = await Mdata03(download_directory)
126
+ await bot.send_audio(chat_id=update.message.chat.id, audio=download_directory, thumb=thumb, caption=description, duration=duration, reply_to_message_id=update.message.reply_to_message.id, progress=progress_for_pyrogram, progress_args=(Translation.UPLOAD_START, update.message, start_time))
127
+
128
+ elif tg_send_type == "vm":
129
+ width, duration = await Mdata02(download_directory)
130
+ await bot.send_video_note(chat_id=update.message.chat.id, video_note=download_directory, thumb=thumb, duration=duration, length=width, reply_to_message_id=update.message.reply_to_message.id, progress=progress_for_pyrogram, progress_args=(Translation.UPLOAD_START, update.message, start_time))
131
+
132
+ else:
133
+ await bot.send_document(chat_id=update.message.chat.id, document=download_directory, thumb=thumb, caption=description, reply_to_message_id=update.message.reply_to_message.id, progress=progress_for_pyrogram, progress_args=(Translation.UPLOAD_START, update.message, start_time))
134
+
135
+ end_two = datetime.now()
136
+ try:
137
+ os.remove(download_directory)
138
+ except Exception:
139
+ pass
140
+ time_taken_for_download = (end_one - start).seconds
141
+ time_taken_for_upload = (end_two - end_one).seconds
142
+ await bot.edit_message_text(text=Translation.AFTER_SUCCESSFUL_UPLOAD_MSG_WITH_TS.format(time_taken_for_download, time_taken_for_upload), chat_id=update.message.chat.id, message_id=update.message.id, disable_web_page_preview=True)
143
+
144
+ logger.info(f"Downloaded in: {str(time_taken_for_download)}")
145
+ logger.info(f"Uploaded in: {str(time_taken_for_upload)}")
146
+ else:
147
+ await bot.edit_message_text(text=Translation.NO_VOID_FORMAT_FOUND.format("Incorrect Link"), chat_id=update.message.chat.id, message_id=update.message.id, disable_web_page_preview=True)
148
+
149
+
150
+ async def download_coroutine(bot, session, url, file_name, chat_id, message_id, start):
151
+ downloaded = 0
152
+ display_message = ""
153
+ async with session.get(url, timeout=Config.PROCESS_MAX_TIMEOUT) as response:
154
+ total_length = int(response.headers["Content-Length"])
155
+ content_type = response.headers["Content-Type"]
156
+ if "text" in content_type and total_length < 500:
157
+ return await response.release()
158
+ with open(file_name, "wb") as f_handle:
159
+ while True:
160
+ chunk = await response.content.read(Config.CHUNK_SIZE)
161
+ if not chunk:
162
+ break
163
+ f_handle.write(chunk)
164
+ downloaded += Config.CHUNK_SIZE
165
+ now = time.time()
166
+ diff = now - start
167
+ if round(diff % 5.0) == 0 or downloaded == total_length:
168
+ percentage = downloaded * 100 / total_length
169
+ speed = downloaded / diff
170
+ elapsed_time = round(diff) * 1000
171
+ time_to_completion = (
172
+ round((total_length - downloaded) / speed) * 1000)
173
+ estimated_total_time = elapsed_time + time_to_completion
174
+ try:
175
+ current_message = """**Download Status**
176
+ URL: {}
177
+ File Size: {}
178
+ Downloaded: {}
179
+ ETA: {}""".format(url, humanbytes(total_length), humanbytes(downloaded), TimeFormatter(estimated_total_time))
180
+
181
+ if current_message != display_message:
182
+ await bot.edit_message_text(chat_id, message_id, text=current_message)
183
+ display_message = current_message
184
+ except Exception as e:
185
+ logger.info(str(e))
186
+ return await response.release()
Uploader/echo.py ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+ import time
25
+ import json
26
+ import asyncio
27
+ import logging
28
+
29
+ from opencc import OpenCC
30
+
31
+ from pyrogram.types import Thumbnail
32
+ from pyrogram import Client, filters
33
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
34
+
35
+ if bool(os.environ.get("WEBHOOK")):
36
+ from Uploader.config import Config
37
+ else:
38
+ from sample_config import Config
39
+ from Uploader.script import Translation
40
+ from Uploader.functions.ran_text import random_char
41
+ from Uploader.functions.display_progress import humanbytes
42
+ from Uploader.functions.display_progress import humanbytes
43
+
44
+ logging.basicConfig(level=logging.DEBUG,
45
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
46
+ logger = logging.getLogger(__name__)
47
+ logging.getLogger("pyrogram").setLevel(logging.WARNING)
48
+
49
+ s2tw = OpenCC('s2tw.json').convert
50
+
51
+
52
+ @Client.on_message(filters.private & filters.regex(pattern=".*http.*"))
53
+ async def echo(bot, update):
54
+ logger.info(update.from_user)
55
+ url = update.text
56
+ youtube_dl_username = None
57
+ youtube_dl_password = None
58
+ file_name = None
59
+
60
+ if "youtu.be" in url:
61
+ return await update.reply_text(
62
+ "**Choose Download type**",
63
+ reply_markup=InlineKeyboardMarkup(
64
+ [
65
+ [
66
+ InlineKeyboardButton(
67
+ "Audio 🎵", callback_data="ytdl_audio"),
68
+ InlineKeyboardButton(
69
+ "Video 🎬", callback_data="ytdl_video")
70
+ ]
71
+ ]
72
+ ),
73
+ quote=True
74
+ )
75
+
76
+ if "|" in url:
77
+ url_parts = url.split("|")
78
+ if len(url_parts) == 2:
79
+ url = url_parts[0]
80
+ file_name = url_parts[1]
81
+ elif len(url_parts) == 4:
82
+ url = url_parts[0]
83
+ file_name = url_parts[1]
84
+ youtube_dl_username = url_parts[2]
85
+ youtube_dl_password = url_parts[3]
86
+ else:
87
+ for entity in update.entities:
88
+ if entity.type == "text_link":
89
+ url = entity.url
90
+ elif entity.type == "url":
91
+ o = entity.offset
92
+ l = entity.length
93
+ url = url[o:o + l]
94
+ if url is not None:
95
+ url = url.strip()
96
+ if file_name is not None:
97
+ file_name = file_name.strip()
98
+ # https://stackoverflow.com/a/761825/4723940
99
+ if youtube_dl_username is not None:
100
+ youtube_dl_username = youtube_dl_username.strip()
101
+ if youtube_dl_password is not None:
102
+ youtube_dl_password = youtube_dl_password.strip()
103
+ logger.info(url)
104
+ logger.info(file_name)
105
+ else:
106
+ for entity in update.entities:
107
+ if entity.type == "text_link":
108
+ url = entity.url
109
+ elif entity.type == "url":
110
+ o = entity.offset
111
+ l = entity.length
112
+ url = url[o:o + l]
113
+ if Config.HTTP_PROXY != "":
114
+ command_to_exec = [
115
+ "yt-dlp",
116
+ "--no-warnings",
117
+ "--allow-dynamic-mpd",
118
+ "-j",
119
+ url,
120
+ "--proxy", Config.HTTP_PROXY
121
+ ]
122
+ else:
123
+ command_to_exec = [
124
+ "yt-dlp",
125
+ "--no-warnings",
126
+ "--allow-dynamic-mpd",
127
+ "-j",
128
+ url
129
+ ]
130
+ if youtube_dl_username is not None:
131
+ command_to_exec.append("--username")
132
+ command_to_exec.append(youtube_dl_username)
133
+ if youtube_dl_password is not None:
134
+ command_to_exec.append("--password")
135
+ command_to_exec.append(youtube_dl_password)
136
+ logger.info(command_to_exec)
137
+ chk = await bot.send_message(
138
+ chat_id=update.chat.id,
139
+ text='Proccesing your ⌛',
140
+ disable_web_page_preview=True,
141
+ reply_to_message_id=update.id
142
+
143
+ )
144
+ if update.from_user.id not in Config.AUTH_USERS:
145
+
146
+ if str(update.from_user.id) in Config.ADL_BOT_RQ:
147
+ current_time = time.time()
148
+ previous_time = Config.ADL_BOT_RQ[str(update.from_user.id)]
149
+ process_max_timeout = round(Config.PROCESS_MAX_TIMEOUT/60)
150
+ present_time = round(Config.PROCESS_MAX_TIMEOUT -
151
+ (current_time - previous_time))
152
+ Config.ADL_BOT_RQ[str(update.from_user.id)] = time.time()
153
+ if round(current_time - previous_time) < Config.PROCESS_MAX_TIMEOUT:
154
+ await bot.edit_message_text(chat_id=update.chat.id, text=Translation.FREE_USER_LIMIT_Q_SZE.format(process_max_timeout, present_time), disable_web_page_preview=True, message_id=chk.id)
155
+ return
156
+ else:
157
+ Config.ADL_BOT_RQ[str(update.from_user.id)] = time.time()
158
+
159
+ process = await asyncio.create_subprocess_exec(
160
+ *command_to_exec,
161
+ # stdout must a pipe to be accessible as process.stdout
162
+ stdout=asyncio.subprocess.PIPE,
163
+ stderr=asyncio.subprocess.PIPE,
164
+ )
165
+ # Wait for the subprocess to finish
166
+ stdout, stderr = await process.communicate()
167
+ e_response = stderr.decode().strip()
168
+ logger.info(e_response)
169
+ t_response = stdout.decode().strip()
170
+ # logger.info(t_response)
171
+ # https://github.com/rg3/youtube-dl/issues/2630#issuecomment-38635239
172
+ if e_response and "nonnumeric port" not in e_response:
173
+ # logger.warn("Status : FAIL", exc.returncode, exc.output)
174
+ error_message = e_response.replace(
175
+ "please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see https://yt-dl.org/update on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.", "")
176
+ if "This video is only available for registered users." in error_message:
177
+ error_message += Translation.SET_CUSTOM_USERNAME_PASSWORD
178
+ await chk.delete()
179
+
180
+ time.sleep(40.5)
181
+ await bot.send_message(
182
+ chat_id=update.chat.id,
183
+ text=Translation.NO_VOID_FORMAT_FOUND.format(str(error_message)),
184
+ reply_to_message_id=update.id,
185
+
186
+ disable_web_page_preview=True
187
+ )
188
+ return False
189
+ if t_response:
190
+ # logger.info(t_response)
191
+ x_reponse = t_response
192
+ if "\n" in x_reponse:
193
+ x_reponse, _ = x_reponse.split("\n")
194
+ response_json = json.loads(x_reponse)
195
+ randem = random_char(5)
196
+ save_ytdl_json_path = Config.DOWNLOAD_LOCATION + \
197
+ "/" + str(update.from_user.id) + f'{randem}' + ".json"
198
+ with open(save_ytdl_json_path, "w", encoding="utf8") as outfile:
199
+ json.dump(response_json, outfile, ensure_ascii=False)
200
+ # logger.info(response_json)
201
+ inline_keyboard = []
202
+ duration = None
203
+ if "duration" in response_json:
204
+ duration = response_json["duration"]
205
+ if "formats" in response_json:
206
+ for formats in response_json["formats"]:
207
+ format_id = formats.get("format_id")
208
+ format_string = formats.get("format_note")
209
+ if format_string is None:
210
+ format_string = formats.get("format")
211
+ if "DASH" in format_string.upper():
212
+ continue
213
+
214
+ format_ext = formats.get("ext")
215
+
216
+ if formats.get('filesize'):
217
+ size = formats['filesize']
218
+ elif formats.get('filesize_approx'):
219
+ size = formats['filesize_approx']
220
+ else:
221
+ size = 0
222
+
223
+ cb_string_video = "{}|{}|{}|{}".format(
224
+ "video", format_id, format_ext, randem)
225
+ cb_string_file = "{}|{}|{}|{}".format(
226
+ "file", format_id, format_ext, randem)
227
+ if format_string is not None and not "audio only" in format_string:
228
+ ikeyboard = [
229
+ InlineKeyboardButton(
230
+ "🎬 " + format_string + " " + format_ext +
231
+ " " + humanbytes(size) + " ",
232
+ callback_data=(cb_string_video).encode("UTF-8")
233
+ )
234
+ ]
235
+ else:
236
+ # special weird case :\
237
+ ikeyboard = [
238
+ InlineKeyboardButton(
239
+ "🎬 [" +
240
+ "] ( " +
241
+ humanbytes(size) + " )",
242
+ callback_data=(cb_string_video).encode("UTF-8")
243
+ )
244
+ ]
245
+ inline_keyboard.append(ikeyboard)
246
+ if duration is not None:
247
+ cb_string_64 = "{}|{}|{}|{}".format(
248
+ "audio", "64k", "mp3", randem)
249
+ cb_string_128 = "{}|{}|{}|{}".format(
250
+ "audio", "128k", "mp3", randem)
251
+ cb_string = "{}|{}|{}|{}".format(
252
+ "audio", "320k", "mp3", randem)
253
+ inline_keyboard.append([
254
+ InlineKeyboardButton(
255
+ "🎼 ᴍᴘ𝟹 " + "(" + "64 ᴋʙᴘs" + ")", callback_data=cb_string_64.encode("UTF-8")),
256
+ InlineKeyboardButton(
257
+ "🎼 ᴍᴘ𝟹 " + "(" + "128 ᴋʙᴘs" + ")", callback_data=cb_string_128.encode("UTF-8"))
258
+ ])
259
+ inline_keyboard.append([
260
+ InlineKeyboardButton(
261
+ "🎼 ᴍᴘ𝟹 " + "(" + "320 ᴋʙᴘs" + ")", callback_data=cb_string.encode("UTF-8"))
262
+ ])
263
+ inline_keyboard.append([
264
+ InlineKeyboardButton(
265
+ "⛔ ᴄʟᴏsᴇ", callback_data='close')
266
+ ])
267
+ else:
268
+ format_id = response_json["format_id"]
269
+ format_ext = response_json["ext"]
270
+ cb_string_file = "{}|{}|{}|{}".format(
271
+ "file", format_id, format_ext, randem)
272
+ cb_string_video = "{}|{}|{}|{}".format(
273
+ "video", format_id, format_ext, randem)
274
+ inline_keyboard.append([
275
+ InlineKeyboardButton(
276
+ "🎬 Video",
277
+ callback_data=(cb_string_video).encode("UTF-8")
278
+ )
279
+ ])
280
+ cb_string_file = "{}={}={}".format(
281
+ "file", format_id, format_ext)
282
+ cb_string_video = "{}={}={}".format(
283
+ "video", format_id, format_ext)
284
+ inline_keyboard.append([
285
+ InlineKeyboardButton(
286
+ "📁 Document",
287
+ callback_data=(cb_string_file).encode("UTF-8")
288
+ )
289
+ ])
290
+ reply_markup = InlineKeyboardMarkup(inline_keyboard)
291
+ await chk.delete()
292
+
293
+ await bot.send_message(
294
+ chat_id=update.chat.id,
295
+ text=Translation.FORMAT_SELECTION.format(
296
+ Thumbnail) + "\n" + Translation.SET_CUSTOM_USERNAME_PASSWORD,
297
+ reply_markup=reply_markup,
298
+
299
+ reply_to_message_id=update.id
300
+ )
301
+ else:
302
+ # fallback for nonnumeric port a.k.a seedbox.io
303
+ inline_keyboard = []
304
+ cb_string_file = "{}={}={}".format(
305
+ "file", "LFO", "NONE")
306
+ cb_string_video = "{}={}={}".format(
307
+ "video", "OFL", "ENON")
308
+ inline_keyboard.append([
309
+ InlineKeyboardButton(
310
+ "🎬 ᴍᴇᴅɪᴀ",
311
+ callback_data=(cb_string_video).encode("UTF-8")
312
+ )
313
+ ])
314
+ reply_markup = InlineKeyboardMarkup(inline_keyboard)
315
+ await chk.delete(True)
316
+
317
+ await bot.send_message(
318
+ chat_id=update.chat.id,
319
+ text=Translation.FORMAT_SELECTION,
320
+ reply_markup=reply_markup,
321
+
322
+ reply_to_message_id=update.id
323
+ )
Uploader/script.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
24
+
25
+
26
+ class Translation(object):
27
+
28
+ START_TEXT = """
29
+ Hi {}
30
+
31
+ I am Powerful Url Uploader Bot
32
+
33
+ """
34
+
35
+ HELP_TEXT = """
36
+
37
+ # Send me the Google Drive | ytdl | direct links.
38
+
39
+ # Select the desired option.
40
+
41
+ # Then be relaxed your file will be uploaded soon..
42
+
43
+ """
44
+
45
+ # give credit to developer
46
+
47
+ ABOUT_TEXT = """
48
+ <b>♻️ My Name</b> : Url Uploader Bot
49
+
50
+ <b>🌀 Channel</b> : <a href="https://t.me/url_upload_bots">@URL_UPLOAD_BOT</a>
51
+
52
+ <b>🌺 Insta</b> : <a href="https://instagram.com/ims_eldrith">Instagram</a>
53
+
54
+ <b>📑 Language :</b> <a href="https://www.python.org/">Python 3.10.5</a>
55
+
56
+ <b>🇵🇲 Framework :</b> <a href="https://docs.pyrogram.org/">Pyrogram 2.0.30</a>
57
+
58
+ <b>👲 Developer :</b> <a href="https://t.me/imseldrith">@imseldrith</a>
59
+
60
+ """
61
+
62
+ PROGRESS = """
63
+ 🔰 Speed : {3}/s\n\n
64
+ 🌀 Done : {1}\n\n
65
+ 🎥 Tᴏᴛᴀʟ sɪᴢᴇ : {2}\n\n
66
+ ⏳ Tɪᴍᴇ ʟᴇғᴛ : {4}\n\n
67
+ """
68
+ ID_TEXT = """
69
+ 🆔 Your Telegram ID 𝐢𝐬 :- <code>{}</code>
70
+ """
71
+
72
+ INFO_TEXT = """
73
+
74
+ 🤹 First Name : <b>{}</b>
75
+
76
+ 🚴‍♂️ Second Name : <b>{}</b>
77
+
78
+ 🧑🏻‍🎓 Username : <b>@{}</b>
79
+
80
+ 🆔 Telegram Id : <code>{}</code>
81
+
82
+ 📇 Profile Link : <b>{}</b>
83
+
84
+ 📡 Dc : <b>{}</b>
85
+
86
+ 📑 Language : <b>{}</b>
87
+
88
+ 👲 Status : <b>{}</b>
89
+ """
90
+
91
+ START_BUTTONS = InlineKeyboardMarkup(
92
+ [[
93
+ InlineKeyboardButton('❓ Help', callback_data='help'),
94
+ InlineKeyboardButton('🦊 About', callback_data='about')
95
+ ], [
96
+ InlineKeyboardButton('📛 Close', callback_data='close')
97
+ ]]
98
+ )
99
+ HELP_BUTTONS = InlineKeyboardMarkup(
100
+ [[
101
+ InlineKeyboardButton('🏠 Home', callback_data='home'),
102
+ InlineKeyboardButton('🦊 About', callback_data='about')
103
+ ], [
104
+ InlineKeyboardButton('📛 Close', callback_data='close')
105
+ ]]
106
+ )
107
+ ABOUT_BUTTONS = InlineKeyboardMarkup(
108
+ [[
109
+ InlineKeyboardButton('🏠 Home', callback_data='home'),
110
+ InlineKeyboardButton('❓ Help', callback_data='help')
111
+ ], [
112
+ InlineKeyboardButton('📛 Close', callback_data='close')
113
+ ]]
114
+ )
115
+ BUTTONS = InlineKeyboardMarkup(
116
+ [[
117
+ InlineKeyboardButton('📛 Close', callback_data='close')
118
+ ]]
119
+ )
120
+ FORMAT_SELECTION = "Now Select the desired formats"
121
+ SET_CUSTOM_USERNAME_PASSWORD = """"""
122
+ DOWNLOAD_START = "Trying to Download ⌛\n\n <i>{} </i>"
123
+ UPLOAD_START = "<i>{} </i>\n\n📤 Uploading Please Wait "
124
+ RCHD_TG_API_LIMIT = "Downloaded in {} seconds.\nDetected File Size: {}\nSorry. But, I cannot upload files greater than 2GB due to Telegram API limitations."
125
+ AFTER_SUCCESSFUL_UPLOAD_MSG_WITH_TS = "Dᴏᴡɴʟᴏᴀᴅᴇᴅ ɪɴ {} sᴇᴄᴏɴᴅs.\n\nTʜᴀɴᴋs Fᴏʀ Usɪɴɢ Mᴇ\n\nUᴘʟᴏᴀᴅᴇᴅ ɪɴ {} sᴇᴄᴏɴᴅs"
126
+ FF_MPEG_DEL_ETED_CUSTOM_MEDIA = "✅ Media cleared succesfully."
127
+ CUSTOM_CAPTION_UL_FILE = " "
128
+ NO_VOID_FORMAT_FOUND = "ERROR... <code>{}</code>"
129
+ SLOW_URL_DECED = "Gosh that seems to be a very slow URL. Since you were screwing my home, I am in no mood to download this file. Meanwhile, why don't you try this:==> https://shrtz.me/PtsVnf6 and get me a fast URL so that I can upload to Telegram, without me slowing down for other users."
Uploader/thumbunali.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+
25
+ from pyrogram import Client, filters
26
+
27
+ if bool(os.environ.get("WEBHOOK")):
28
+ from Uploader.config import Config
29
+ else:
30
+ from sample_config import Config
31
+
32
+
33
+ @Client.on_message(filters.photo & filters.incoming & filters.private)
34
+ async def save_photo(bot, message):
35
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
36
+ await message.download(file_name=download_location)
37
+
38
+ await message.reply_text(
39
+ text="your custom thumbnail is saved",
40
+ quote=True
41
+ )
42
+
43
+
44
+ @Client.on_message(filters.command("thumb") & filters.incoming & filters.private)
45
+ async def send_photo(bot, message):
46
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
47
+
48
+ if os.path.isfile(download_location):
49
+ await message.reply_photo(
50
+ photo=download_location,
51
+ caption="your custom thumbnail",
52
+ quote=True
53
+ )
54
+ else:
55
+ await message.reply_text(text="you don't have set thumbnail yet!. send .jpg img to save as thumbnail.", quote=True)
56
+
57
+
58
+ @Client.on_message(filters.command("delthumb") & filters.incoming & filters.private)
59
+ async def delete_photo(bot, message):
60
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
61
+ if os.path.isfile(download_location):
62
+ os.remove(download_location)
63
+ await message.reply_text(text="your thumbnail removed successfully.", quote=True)
64
+ else:
65
+ await message.reply_text(text="you don't have set thumbnail yet!. send .jpg img to save as thumbnail.", quote=True)
Uploader/utitles.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import logging
24
+
25
+ from hachoir.parser import createParser
26
+ from hachoir.metadata import extractMetadata
27
+
28
+
29
+ logging.basicConfig(level=logging.DEBUG,
30
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
31
+ logger = logging.getLogger(__name__)
32
+ logging.getLogger("pyrogram").setLevel(logging.WARNING)
33
+
34
+
35
+ async def Mdata01(download_directory):
36
+ width = 0
37
+ height = 0
38
+ duration = 0
39
+ metadata = extractMetadata(createParser(download_directory))
40
+ if metadata is not None:
41
+ if metadata.has("duration"):
42
+ duration = metadata.get('duration').seconds
43
+ if metadata.has("width"):
44
+ width = metadata.get("width")
45
+ if metadata.has("height"):
46
+ height = metadata.get("height")
47
+
48
+ return width, height, duration
49
+
50
+
51
+ async def Mdata02(download_directory):
52
+ width = 0
53
+ duration = 0
54
+ metadata = extractMetadata(createParser(download_directory))
55
+ if metadata is not None:
56
+ if metadata.has("duration"):
57
+ duration = metadata.get('duration').seconds
58
+ if metadata.has("width"):
59
+ width = metadata.get("width")
60
+
61
+ return width, duration
62
+
63
+
64
+ async def Mdata03(download_directory):
65
+ metadata = extractMetadata(createParser(download_directory))
66
+ return (
67
+ metadata.get('duration').seconds
68
+ if metadata is not None and metadata.has("duration")
69
+ else 0
70
+ )
Uploader/youtube.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE
22
+
23
+ import os
24
+ from urllib.parse import urlparse
25
+ import wget
26
+ import asyncio
27
+
28
+ from opencc import OpenCC
29
+ from youtube_dl import YoutubeDL
30
+ from pyrogram import Client, filters, enums
31
+ from pyrogram.types import Message
32
+ from pyrogram import Client, filters
33
+
34
+ if bool(os.environ.get("WEBHOOK")):
35
+ from Uploader.config import Config
36
+ else:
37
+ from sample_config import Config
38
+
39
+ from Uploader.functions.help_ytdl import get_file_extension_from_url, get_resolution
40
+
41
+ YTDL_REGEX = (r"^((?:https?:)?\/\/)")
42
+ s2tw = OpenCC('s2tw.json').convert
43
+
44
+
45
+ @Client.on_callback_query(filters.regex("^ytdl_audio$"))
46
+ async def callback_query_ytdl_audio(_, callback_query):
47
+ try:
48
+ url = callback_query.message.reply_to_message.text
49
+ ydl_opts = {
50
+ 'format': 'bestaudio',
51
+ 'outtmpl': '%(title)s - %(extractor)s-%(id)s.%(ext)s',
52
+ 'writethumbnail': True
53
+ }
54
+ with YoutubeDL(ydl_opts) as ydl:
55
+ message = callback_query.message
56
+ await message.reply_chat_action(enums.ChatAction.TYPING)
57
+ info_dict = ydl.extract_info(url, download=False)
58
+ # download
59
+ await callback_query.edit_message_text("**Downloading audio...**")
60
+ ydl.process_info(info_dict)
61
+ # upload
62
+ audio_file = ydl.prepare_filename(info_dict)
63
+ task = asyncio.create_task(send_audio(message, info_dict,
64
+ audio_file))
65
+ while not task.done():
66
+ await asyncio.sleep(3)
67
+ await message.reply_chat_action(enums.ChatAction.UPLOAD_DOCUMENT)
68
+ await message.reply_chat_action(enums.ChatAction.CANCEL)
69
+ await message.delete()
70
+ except Exception as e:
71
+ await message.reply_text(e)
72
+ await callback_query.message.reply_to_message.delete()
73
+ await callback_query.message.delete()
74
+
75
+ async def send_audio(message: Message, info_dict, audio_file):
76
+ basename = audio_file.rsplit(".", 1)[-2]
77
+ if info_dict['ext'] == 'webm':
78
+ audio_file_weba = f"{basename}.weba"
79
+ os.rename(audio_file, audio_file_weba)
80
+ audio_file = audio_file_weba
81
+ thumbnail_url = info_dict['thumbnail']
82
+ thumbnail_file = f"{basename}.{get_file_extension_from_url(thumbnail_url)}"
83
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
84
+ thumb = download_location if os.path.isfile(
85
+ download_location) else None
86
+ webpage_url = info_dict['webpage_url']
87
+ title = s2tw(info_dict['title'])
88
+ caption = f'<b><a href=\"{webpage_url}\">{title}</a></b>'
89
+ duration = int(float(info_dict['duration']))
90
+ performer = s2tw(info_dict['uploader'])
91
+ await message.reply_audio(audio_file, caption=caption, duration=duration, performer=performer, title=title, parse_mode=enums.ParseMode.HTML, thumb=thumb)
92
+
93
+ os.remove(audio_file)
94
+ os.remove(thumbnail_file)
95
+
96
+
97
+ @Client.on_callback_query(filters.regex("^ytdl_video$"))
98
+ async def callback_query_ytdl_video(_, callback_query):
99
+ try:
100
+ # url = callback_query.message.text
101
+ url = callback_query.message.reply_to_message.text
102
+ ydl_opts = {
103
+ 'format': 'best[ext=mp4]',
104
+ 'outtmpl': '%(title)s - %(extractor)s-%(id)s.%(ext)s',
105
+ 'writethumbnail': True
106
+ }
107
+ with YoutubeDL(ydl_opts) as ydl:
108
+ message = callback_query.message
109
+ await message.reply_chat_action(enums.ChatAction.TYPING)
110
+ info_dict = ydl.extract_info(url, download=False)
111
+ # download
112
+ await callback_query.edit_message_text("**Downloading video...**")
113
+ ydl.process_info(info_dict)
114
+ # upload
115
+ video_file = ydl.prepare_filename(info_dict)
116
+ task = asyncio.create_task(send_video(message, info_dict,
117
+ video_file))
118
+ while not task.done():
119
+ await asyncio.sleep(3)
120
+ await message.reply_chat_action(enums.ChatAction.UPLOAD_DOCUMENT)
121
+ await message.reply_chat_action(enums.ChatAction.CANCEL)
122
+ await message.delete()
123
+ except Exception as e:
124
+ await message.reply_text(e)
125
+ await callback_query.message.reply_to_message.delete()
126
+ await callback_query.message.delete()
127
+
128
+ async def send_video(message: Message, info_dict, video_file):
129
+ basename = video_file.rsplit(".", 1)[-2]
130
+ thumbnail_url = info_dict['thumbnail']
131
+ thumbnail_file = f"{basename}.{get_file_extension_from_url(thumbnail_url)}"
132
+ download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
133
+ thumb = download_location if os.path.isfile(
134
+ download_location) else None
135
+ webpage_url = info_dict['webpage_url']
136
+ title = s2tw(info_dict['title'])
137
+ caption = f'<b><a href=\"{webpage_url}\">{title}</a></b>'
138
+ duration = int(float(info_dict['duration']))
139
+ width, height = get_resolution(info_dict)
140
+ await message.reply_video(video_file, caption=caption, duration=duration, width=width, height=height, parse_mode=enums.ParseMode.HTML, thumb=thumb)
141
+
142
+ os.remove(video_file)
143
+ os.remove(thumbnail_file)