update

swadhinbiswas committed 2 years ago
e61a084
1 parent 2e0fc11
commit e61a084
14 files changed
+1085 -1
▼app/bot/helper/ext_utils/dbhandelar.py
+1-1
14
14
15
15
def connect(self):
16
16
try:
17
- self._conn = AsyncIOMotorClient(DATABASE_URL)
17
+ self._conn = AsyncIOMotorClient(MONGO_DB_URI)
18
18
self._db = self._conn.mltb
19
19
except PyMongoError as e:
20
20
LOGGER.error(f"Error in DB connection: {e}")
21
21
▼app/bot/helper/ext_utils/exceptions.py
+22-0
1
+class DirectDownloadLinkException(Exception):
2
+ """Not method found for extracting direct download link from the http link"""
3
+
4
+ pass
5
+
6
+
7
+class NotSupportedExtractionArchive(Exception):
8
+ """The archive format use is trying to extract is not supported"""
9
+
10
+ pass
11
+
12
+
13
+class RssShutdownException(Exception):
14
+ """This exception should be raised when shutdown is called to stop the montior"""
15
+
16
+ pass
17
+
18
+
19
+class TgLinkException(Exception):
20
+ """No Access granted for this chat"""
21
+
22
+ pass
0
23
▼app/bot/helper/ext_utils/files_utils.py
+206-0
1
+from aiofiles.os import remove, path as aiopath, listdir, rmdir
2
+from aioshutil import rmtree as aiormtree
3
+from magic import Magic
4
+from os import walk, path as ospath, makedirs
5
+from re import split as re_split, I, search as re_search, escape
6
+from shutil import rmtree
7
+from subprocess import run as srun
8
+from sys import exit as sexit
9
+
10
+from bot import aria2, LOGGER, DOWNLOAD_DIR, get_qb_client
11
+from bot.helper.ext_utils.bot_utils import sync_to_async, cmd_exec
12
+from .exceptions import NotSupportedExtractionArchive
13
+
14
+ARCH_EXT = [
15
+ ".tar.bz2",
16
+ ".tar.gz",
17
+ ".bz2",
18
+ ".gz",
19
+ ".tar.xz",
20
+ ".tar",
21
+ ".tbz2",
22
+ ".tgz",
23
+ ".lzma2",
24
+ ".zip",
25
+ ".7z",
26
+ ".z",
27
+ ".rar",
28
+ ".iso",
29
+ ".wim",
30
+ ".cab",
31
+ ".apm",
32
+ ".arj",
33
+ ".chm",
34
+ ".cpio",
35
+ ".cramfs",
36
+ ".deb",
37
+ ".dmg",
38
+ ".fat",
39
+ ".hfs",
40
+ ".lzh",
41
+ ".lzma",
42
+ ".mbr",
43
+ ".msi",
44
+ ".mslz",
45
+ ".nsis",
46
+ ".ntfs",
47
+ ".rpm",
48
+ ".squashfs",
49
+ ".udf",
50
+ ".vhd",
51
+ ".xar",
52
+]
53
+
54
+FIRST_SPLIT_REGEX = r"(\.|_)part0*1\.rar$|(\.|_)7z\.0*1$|(\.|_)zip\.0*1$|^(?!.*(\.|_)part\d+\.rar$).*\.rar$"
55
+
56
+SPLIT_REGEX = r"\.r\d+$|\.7z\.\d+$|\.z\d+$|\.zip\.\d+$"
57
+
58
+
59
+def is_first_archive_split(file):
60
+ return bool(re_search(FIRST_SPLIT_REGEX, file))
61
+
62
+
63
+def is_archive(file):
64
+ return file.endswith(tuple(ARCH_EXT))
65
+
66
+
67
+def is_archive_split(file):
68
+ return bool(re_search(SPLIT_REGEX, file))
69
+
70
+
71
+async def clean_target(path):
72
+ if await aiopath.exists(path):
73
+ LOGGER.info(f"Cleaning Target: {path}")
74
+ try:
75
+ if await aiopath.isdir(path):
76
+ await aiormtree(path, ignore_errors=True)
77
+ else:
78
+ await remove(path)
79
+ except Exception as e:
80
+ LOGGER.error(str(e))
81
+
82
+
83
+async def clean_download(path):
84
+ if await aiopath.exists(path):
85
+ LOGGER.info(f"Cleaning Download: {path}")
86
+ try:
87
+ await aiormtree(path, ignore_errors=True)
88
+ except Exception as e:
89
+ LOGGER.error(str(e))
90
+
91
+
92
+def clean_all():
93
+ aria2.remove_all(True)
94
+ get_qb_client().torrents_delete(torrent_hashes="all")
95
+ try:
96
+ LOGGER.info("Cleaning Download Directory...")
97
+ rmtree(DOWNLOAD_DIR, ignore_errors=True)
98
+ except:
99
+ pass
100
+ makedirs(DOWNLOAD_DIR, exist_ok=True)
101
+
102
+
103
+def exit_clean_up(signal, frame):
104
+ try:
105
+ LOGGER.info("Please wait, while we clean up and stop the running downloads")
106
+ clean_all()
107
+ srun(["pkill", "-9", "-f", "gunicorn|aria2c|qbittorrent-nox|ffmpeg|java"])
108
+ sexit(0)
109
+ except KeyboardInterrupt:
110
+ LOGGER.warning("Force Exiting before the cleanup finishes!")
111
+ sexit(1)
112
+
113
+
114
+async def clean_unwanted(path, custom_list=None):
115
+ if custom_list is None:
116
+ custom_list = []
117
+ LOGGER.info(f"Cleaning unwanted files/folders: {path}")
118
+ for dirpath, _, files in await sync_to_async(walk, path, topdown=False):
119
+ for filee in files:
120
+ f_path = ospath.join(dirpath, filee)
121
+ if (
122
+ filee.endswith(".!qB")
123
+ or f_path in custom_list
124
+ or filee.endswith(".parts")
125
+ and filee.startswith(".")
126
+ ):
127
+ await remove(f_path)
128
+ if dirpath.endswith((".unwanted", "splited_files_mltb", "copied_mltb")):
129
+ await aiormtree(dirpath, ignore_errors=True)
130
+ for dirpath, _, files in await sync_to_async(walk, path, topdown=False):
131
+ if not await listdir(dirpath):
132
+ await rmdir(dirpath)
133
+
134
+
135
+async def get_path_size(path):
136
+ if await aiopath.isfile(path):
137
+ return await aiopath.getsize(path)
138
+ total_size = 0
139
+ for root, _, files in await sync_to_async(walk, path):
140
+ for f in files:
141
+ abs_path = ospath.join(root, f)
142
+ total_size += await aiopath.getsize(abs_path)
143
+ return total_size
144
+
145
+
146
+async def count_files_and_folders(path, extension_filter, unwanted_files=None):
147
+ if unwanted_files is None:
148
+ unwanted_files = []
149
+ total_files = 0
150
+ total_folders = 0
151
+ for dirpath, dirs, files in await sync_to_async(walk, path):
152
+ total_files += len(files)
153
+ for f in files:
154
+ if f.endswith(tuple(extension_filter)):
155
+ total_files -= 1
156
+ elif unwanted_files:
157
+ f_path = ospath.join(dirpath, f)
158
+ if f_path in unwanted_files:
159
+ total_files -= 1
160
+ total_folders += len(dirs)
161
+ return total_folders, total_files
162
+
163
+
164
+def get_base_name(orig_path):
165
+ extension = next((ext for ext in ARCH_EXT if orig_path.lower().endswith(ext)), "")
166
+ if extension != "":
167
+ return re_split(f"{extension}$", orig_path, maxsplit=1, flags=I)[0]
168
+ else:
169
+ raise NotSupportedExtractionArchive("File format not supported for extraction")
170
+
171
+
172
+def get_mime_type(file_path):
173
+ mime = Magic(mime=True)
174
+ mime_type = mime.from_file(file_path)
175
+ mime_type = mime_type or "text/plain"
176
+ return mime_type
177
+
178
+
179
+async def join_files(path):
180
+ files = await listdir(path)
181
+ results = []
182
+ exists = False
183
+ for file_ in files:
184
+ if re_search(r"\.0+2$", file_) and await sync_to_async(
185
+ get_mime_type, f"{path}/{file_}"
186
+ ) not in ["application/x-7z-compressed", "application/zip"]:
187
+ exists = True
188
+ final_name = file_.rsplit(".", 1)[0]
189
+ fpath = f"{path}/{final_name}"
190
+ cmd = f'cat "{fpath}."* > "{fpath}"'
191
+ _, stderr, code = await cmd_exec(cmd, True)
192
+ if code != 0:
193
+ LOGGER.error(f"Failed to join {final_name}, stderr: {stderr}")
194
+ if await aiopath.isfile(fpath):
195
+ await remove(fpath)
196
+ else:
197
+ results.append(final_name)
198
+
199
+ if not exists:
200
+ LOGGER.warning("No files to join!")
201
+ elif results:
202
+ LOGGER.info("Join Completed!")
203
+ for res in results:
204
+ for file_ in files:
205
+ if re_search(rf"{escape(res)}\.0[0-9]+$", file_):
206
+ await remove(f"{path}/{file_}")
0
207
▼app/bot/helper/ext_utils/link_utils.py
+53-0
1
+from re import match as re_match
2
+
3
+
4
+def is_magnet(url: str):
5
+ return bool(re_match(r"magnet:\?xt=urn:(btih|btmh):[a-zA-Z0-9]*\s*", url))
6
+
7
+
8
+def is_url(url: str):
9
+ return bool(
10
+ re_match(
11
+ r"^(?!\/)(rtmps?:\/\/|mms:\/\/|rtsp:\/\/|https?:\/\/|ftp:\/\/)?([^\/:]+:[^\/@]+@)?(www\.)?(?=[^\/:\s]+\.[^\/:\s]+)([^\/:\s]+\.[^\/:\s]+)(:\d+)?(\/[^#\s]*[\s\S]*)?(\?[^#\s]*)?(#.*)?$",
12
+ url,
13
+ )
14
+ )
15
+
16
+
17
+def is_gdrive_link(url: str):
18
+ return "drive.google.com" in url or "drive.usercontent.google.com" in url
19
+
20
+
21
+def is_telegram_link(url: str):
22
+ return url.startswith(("https://t.me/", "tg://openmessage?user_id="))
23
+
24
+
25
+def is_share_link(url: str):
26
+ return bool(
27
+ re_match(
28
+ r"https?:\/\/.+\.gdtot\.\S+|https?:\/\/(filepress|filebee|appdrive|gdflix)\.\S+",
29
+ url,
30
+ )
31
+ )
32
+
33
+
34
+def is_rclone_path(path: str):
35
+ return bool(
36
+ re_match(
37
+ r"^(mrcc:)?(?!(magnet:|mtp:|sa:|tp:))(?![- ])[a-zA-Z0-9_\. -]+(?<! ):(?!.*\/\/).*$|^rcl$",
38
+ path,
39
+ )
40
+ )
41
+
42
+
43
+def is_gdrive_id(id_: str):
44
+ return bool(
45
+ re_match(
46
+ r"^(tp:|sa:|mtp:)?(?:[a-zA-Z0-9-_]{33}|[a-zA-Z0-9_-]{19})$|^gdl$|^(tp:|mtp:)?root$",
47
+ id_,
48
+ )
49
+ )
50
+
51
+
52
+def get_mega_link_type(url: str):
53
+ return "folder" if "folder" in url or "/#F!" in url else "file"
0
54
▼app/bot/helper/ext_utils/media_utils.py
+567-0
1
+from PIL import Image
2
+from aiofiles.os import remove, path as aiopath, makedirs
3
+from asyncio import create_subprocess_exec, gather, wait_for
4
+from asyncio.subprocess import PIPE
5
+from os import path as ospath, cpu_count
6
+from re import search as re_search
7
+from time import time
8
+from aioshutil import rmtree
9
+
10
+from bot import LOGGER, subprocess_lock
11
+from bot.helper.ext_utils.bot_utils import cmd_exec
12
+from bot.helper.ext_utils.bot_utils import sync_to_async
13
+from bot.helper.ext_utils.files_utils import ARCH_EXT, get_mime_type
14
+
15
+
16
+async def convert_video(listener, video_file, ext, retry=False):
17
+ base_name = ospath.splitext(video_file)[0]
18
+ output = f"{base_name}.{ext}"
19
+ if retry:
20
+ cmd = [
21
+ "ffmpeg",
22
+ "-i",
23
+ video_file,
24
+ "-c:v",
25
+ "libx264",
26
+ "-c:a",
27
+ "aac",
28
+ "-threads",
29
+ f"{cpu_count() // 2}",
30
+ output,
31
+ ]
32
+ if ext == "mp4":
33
+ cmd[7:7] = ["-c:s", "mov_text"]
34
+ elif ext == "mkv":
35
+ cmd[7:7] = ["-c:s", "ass"]
36
+ else:
37
+ cmd[7:7] = ["-c:s", "copy"]
38
+ else:
39
+ cmd = ["ffmpeg", "-i", video_file, "-map", "0", "-c", "copy", output]
40
+ if listener.isCancelled:
41
+ return False
42
+ listener.suproc = await create_subprocess_exec(*cmd, stderr=PIPE)
43
+ _, stderr = await listener.suproc.communicate()
44
+ if listener.isCancelled:
45
+ return False
46
+ code = listener.suproc.returncode
47
+ if code == 0:
48
+ return output
49
+ elif code == -9:
50
+ listener.isCancelled = True
51
+ return False
52
+ else:
53
+ if not retry:
54
+ if await aiopath.exists(output):
55
+ await remove(output)
56
+ return await convert_video(listener, video_file, ext, True)
57
+ else:
58
+ try:
59
+ stderr = stderr.decode().strip()
60
+ except:
61
+ stderr = "Unable to decode the error!"
62
+ LOGGER.error(
63
+ f"{stderr}. Something went wrong while converting video, mostly file need specific codec. Path: {video_file}"
64
+ )
65
+ return False
66
+
67
+
68
+async def convert_audio(listener, audio_file, ext):
69
+ base_name = ospath.splitext(audio_file)[0]
70
+ output = f"{base_name}.{ext}"
71
+ cmd = [
72
+ "ffmpeg",
73
+ "-i",
74
+ audio_file,
75
+ "-threads",
76
+ f"{cpu_count() // 2}",
77
+ output,
78
+ ]
79
+ if listener.isCancelled:
80
+ return False
81
+ listener.suproc = await create_subprocess_exec(*cmd, stderr=PIPE)
82
+ _, stderr = await listener.suproc.communicate()
83
+ if listener.isCancelled:
84
+ return False
85
+ code = listener.suproc.returncode
86
+ if code == 0:
87
+ return output
88
+ elif code == -9:
89
+ listener.isCancelled = True
90
+ return False
91
+ else:
92
+ try:
93
+ stderr = stderr.decode().strip()
94
+ except:
95
+ stderr = "Unable to decode the error!"
96
+ LOGGER.error(
97
+ f"{stderr}. Something went wrong while converting audio, mostly file need specific codec. Path: {audio_file}"
98
+ )
99
+ if await aiopath.exists(output):
100
+ await remove(output)
101
+ return False
102
+
103
+
104
+async def createThumb(msg, _id=""):
105
+ if not _id:
106
+ _id = msg.id
107
+ path = "Thumbnails/"
108
+ await makedirs(path, exist_ok=True)
109
+ photo_dir = await msg.download()
110
+ des_dir = f"{path}{_id}.jpg"
111
+ await sync_to_async(Image.open(photo_dir).convert("RGB").save, des_dir, "JPEG")
112
+ await remove(photo_dir)
113
+ return des_dir
114
+
115
+
116
+async def is_multi_streams(path):
117
+ try:
118
+ result = await cmd_exec(
119
+ [
120
+ "ffprobe",
121
+ "-hide_banner",
122
+ "-loglevel",
123
+ "error",
124
+ "-print_format",
125
+ "json",
126
+ "-show_streams",
127
+ path,
128
+ ]
129
+ )
130
+ except Exception as e:
131
+ LOGGER.error(f"Get Video Streams: {e}. Mostly File not found! - File: {path}")
132
+ return False
133
+ if result[0] and result[2] == 0:
134
+ fields = eval(result[0]).get("streams")
135
+ if fields is None:
136
+ LOGGER.error(f"get_video_streams: {result}")
137
+ return False
138
+ videos = 0
139
+ audios = 0
140
+ for stream in fields:
141
+ if stream.get("codec_type") == "video":
142
+ videos += 1
143
+ elif stream.get("codec_type") == "audio":
144
+ audios += 1
145
+ return videos > 1 or audios > 1
146
+ return False
147
+
148
+
149
+async def get_media_info(path):
150
+ try:
151
+ result = await cmd_exec(
152
+ [
153
+ "ffprobe",
154
+ "-hide_banner",
155
+ "-loglevel",
156
+ "error",
157
+ "-print_format",
158
+ "json",
159
+ "-show_format",
160
+ path,
161
+ ]
162
+ )
163
+ except Exception as e:
164
+ LOGGER.error(f"Get Media Info: {e}. Mostly File not found! - File: {path}")
165
+ return 0, None, None
166
+ if result[0] and result[2] == 0:
167
+ fields = eval(result[0]).get("format")
168
+ if fields is None:
169
+ LOGGER.error(f"get_media_info: {result}")
170
+ return 0, None, None
171
+ duration = round(float(fields.get("duration", 0)))
172
+ tags = fields.get("tags", {})
173
+ artist = tags.get("artist") or tags.get("ARTIST") or tags.get("Artist")
174
+ title = tags.get("title") or tags.get("TITLE") or tags.get("Title")
175
+ return duration, artist, title
176
+ return 0, None, None
177
+
178
+
179
+async def get_document_type(path):
180
+ is_video, is_audio, is_image = False, False, False
181
+ if path.endswith(tuple(ARCH_EXT)) or re_search(
182
+ r".+(\.|_)(rar|7z|zip|bin)(\.0*\d+)?$", path
183
+ ):
184
+ return is_video, is_audio, is_image
185
+ mime_type = await sync_to_async(get_mime_type, path)
186
+ if mime_type.startswith("image"):
187
+ return False, False, True
188
+ if mime_type.startswith("audio"):
189
+ return False, True, False
190
+ if not mime_type.startswith("video") and not mime_type.endswith("octet-stream"):
191
+ return is_video, is_audio, is_image
192
+ try:
193
+ result = await cmd_exec(
194
+ [
195
+ "ffprobe",
196
+ "-hide_banner",
197
+ "-loglevel",
198
+ "error",
199
+ "-print_format",
200
+ "json",
201
+ "-show_streams",
202
+ path,
203
+ ]
204
+ )
205
+ if res := result[1]:
206
+ if mime_type.startswith("video"):
207
+ is_video = True
208
+ except Exception as e:
209
+ LOGGER.error(f"Get Document Type: {e}. Mostly File not found! - File: {path}")
210
+ if mime_type.startswith("video"):
211
+ is_video = True
212
+ return is_video, is_audio, is_image
213
+ if result[0] and result[2] == 0:
214
+ fields = eval(result[0]).get("streams")
215
+ if fields is None:
216
+ LOGGER.error(f"get_document_type: {result}")
217
+ return is_video, is_audio, is_image
218
+ is_video = False
219
+ for stream in fields:
220
+ if stream.get("codec_type") == "video":
221
+ is_video = True
222
+ elif stream.get("codec_type") == "audio":
223
+ is_audio = True
224
+ return is_video, is_audio, is_image
225
+
226
+
227
+async def take_ss(video_file, ss_nb) -> bool:
228
+ ss_nb = min(ss_nb, 10)
229
+ duration = (await get_media_info(video_file))[0]
230
+ if duration != 0:
231
+ dirpath, name = video_file.rsplit("/", 1)
232
+ name, _ = ospath.splitext(name)
233
+ dirpath = f"{dirpath}/{name}_mltbss/"
234
+ await makedirs(dirpath, exist_ok=True)
235
+ interval = duration // (ss_nb + 1)
236
+ cap_time = interval
237
+ cmds = []
238
+ for i in range(ss_nb):
239
+ output = f"{dirpath}SS.{name}_{i:02}.png"
240
+ cmd = [
241
+ "ffmpeg",
242
+ "-hide_banner",
243
+ "-loglevel",
244
+ "error",
245
+ "-ss",
246
+ f"{cap_time}",
247
+ "-i",
248
+ video_file,
249
+ "-q:v",
250
+ "1",
251
+ "-frames:v",
252
+ "1",
253
+ output,
254
+ ]
255
+ cap_time += interval
256
+ cmds.append(cmd_exec(cmd))
257
+ try:
258
+ resutls = await wait_for(gather(*cmds), timeout=60)
259
+ if resutls[0][2] != 0:
260
+ LOGGER.error(
261
+ f"Error while creating sreenshots from video. Path: {video_file}. stderr: {resutls[0][1]}"
262
+ )
263
+ await rmtree(dirpath, ignore_errors=True)
264
+ return False
265
+ except:
266
+ LOGGER.error(
267
+ f"Error while creating sreenshots from video. Path: {video_file}. Error: Timeout some issues with ffmpeg with specific arch!"
268
+ )
269
+ await rmtree(dirpath, ignore_errors=True)
270
+ return False
271
+ return dirpath
272
+ else:
273
+ LOGGER.error("take_ss: Can't get the duration of video")
274
+ await rmtree(dirpath, ignore_errors=True)
275
+ return False
276
+
277
+
278
+async def get_audio_thumb(audio_file):
279
+ des_dir = "Thumbnails/"
280
+ await makedirs(des_dir, exist_ok=True)
281
+ des_dir = f"Thumbnails/{time()}.jpg"
282
+ cmd = [
283
+ "ffmpeg",
284
+ "-hide_banner",
285
+ "-loglevel",
286
+ "error",
287
+ "-i",
288
+ audio_file,
289
+ "-an",
290
+ "-vcodec",
291
+ "copy",
292
+ des_dir,
293
+ ]
294
+ _, err, code = await cmd_exec(cmd)
295
+ if code != 0 or not await aiopath.exists(des_dir):
296
+ LOGGER.error(
297
+ f"Error while extracting thumbnail from audio. Name: {audio_file} stderr: {err}"
298
+ )
299
+ return None
300
+ return des_dir
301
+
302
+
303
+async def create_thumbnail(video_file, duration):
304
+ des_dir = "Thumbnails"
305
+ await makedirs(des_dir, exist_ok=True)
306
+ des_dir = ospath.join(des_dir, f"{time()}.jpg")
307
+ if duration is None:
308
+ duration = (await get_media_info(video_file))[0]
309
+ if duration == 0:
310
+ duration = 3
311
+ duration = duration // 2
312
+ cmd = [
313
+ "ffmpeg",
314
+ "-hide_banner",
315
+ "-loglevel",
316
+ "error",
317
+ "-ss",
318
+ f"{duration}",
319
+ "-i",
320
+ video_file,
321
+ "-vf",
322
+ "thumbnail",
323
+ "-frames:v",
324
+ "1",
325
+ des_dir,
326
+ ]
327
+ try:
328
+ _, err, code = await wait_for(cmd_exec(cmd), timeout=60)
329
+ if code != 0 or not await aiopath.exists(des_dir):
330
+ LOGGER.error(
331
+ f"Error while extracting thumbnail from video. Name: {video_file} stderr: {err}"
332
+ )
333
+ return None
334
+ except:
335
+ LOGGER.error(
336
+ f"Error while extracting thumbnail from video. Name: {video_file}. Error: Timeout some issues with ffmpeg with specific arch!"
337
+ )
338
+ return None
339
+ return des_dir
340
+
341
+
342
+async def split_file(
343
+ path,
344
+ size,
345
+ dirpath,
346
+ file_,
347
+ split_size,
348
+ listener,
349
+ start_time=0,
350
+ i=1,
351
+ inLoop=False,
352
+ multi_streams=True,
353
+):
354
+ if listener.seed and not listener.newDir:
355
+ dirpath = f"{dirpath}/splited_files_mltb"
356
+ await makedirs(dirpath, exist_ok=True)
357
+ parts = -(-size // listener.splitSize)
358
+ if listener.equalSplits and not inLoop:
359
+ split_size = (size // parts) + (size % parts)
360
+ if not listener.asDoc and (await get_document_type(path))[0]:
361
+ if multi_streams:
362
+ multi_streams = await is_multi_streams(path)
363
+ duration = (await get_media_info(path))[0]
364
+ base_name, extension = ospath.splitext(file_)
365
+ split_size -= 5000000
366
+ while i <= parts or start_time < duration - 4:
367
+ out_path = f"{dirpath}/{base_name}.part{i:03}{extension}"
368
+ cmd = [
369
+ "ffmpeg",
370
+ "-hide_banner",
371
+ "-loglevel",
372
+ "error",
373
+ "-ss",
374
+ str(start_time),
375
+ "-i",
376
+ path,
377
+ "-fs",
378
+ str(split_size),
379
+ "-map",
380
+ "0",
381
+ "-map_chapters",
382
+ "-1",
383
+ "-async",
384
+ "1",
385
+ "-strict",
386
+ "-2",
387
+ "-c",
388
+ "copy",
389
+ out_path,
390
+ ]
391
+ if not multi_streams:
392
+ del cmd[10]
393
+ del cmd[10]
394
+ if listener.isCancelled:
395
+ return False
396
+ async with subprocess_lock:
397
+ listener.suproc = await create_subprocess_exec(*cmd, stderr=PIPE)
398
+ _, stderr = await listener.suproc.communicate()
399
+ if listener.isCancelled:
400
+ return False
401
+ code = listener.suproc.returncode
402
+ if code == -9:
403
+ listener.isCancelled = True
404
+ return False
405
+ elif code != 0:
406
+ try:
407
+ stderr = stderr.decode().strip()
408
+ except:
409
+ stderr = "Unable to decode the error!"
410
+ try:
411
+ await remove(out_path)
412
+ except:
413
+ pass
414
+ if multi_streams:
415
+ LOGGER.warning(
416
+ f"{stderr}. Retrying without map, -map 0 not working in all situations. Path: {path}"
417
+ )
418
+ return await split_file(
419
+ path,
420
+ size,
421
+ dirpath,
422
+ file_,
423
+ split_size,
424
+ listener,
425
+ start_time,
426
+ i,
427
+ True,
428
+ False,
429
+ )
430
+ else:
431
+ LOGGER.warning(
432
+ f"{stderr}. Unable to split this video, if it's size less than {listener.maxSplitSize} will be uploaded as it is. Path: {path}"
433
+ )
434
+ return False
435
+ out_size = await aiopath.getsize(out_path)
436
+ if out_size > listener.maxSplitSize:
437
+ dif = out_size - listener.maxSplitSize
438
+ split_size -= dif + 5000000
439
+ await remove(out_path)
440
+ return await split_file(
441
+ path,
442
+ size,
443
+ dirpath,
444
+ file_,
445
+ split_size,
446
+ listener,
447
+ start_time,
448
+ i,
449
+ True,
450
+ multi_streams,
451
+ )
452
+ lpd = (await get_media_info(out_path))[0]
453
+ if lpd == 0:
454
+ LOGGER.error(
455
+ f"Something went wrong while splitting, mostly file is corrupted. Path: {path}"
456
+ )
457
+ break
458
+ elif duration == lpd:
459
+ LOGGER.warning(
460
+ f"This file has been splitted with default stream and audio, so you will only see one part with less size from orginal one because it doesn't have all streams and audios. This happens mostly with MKV videos. Path: {path}"
461
+ )
462
+ break
463
+ elif lpd <= 3:
464
+ await remove(out_path)
465
+ break
466
+ start_time += lpd - 3
467
+ i += 1
468
+ else:
469
+ out_path = f"{dirpath}/{file_}."
470
+ async with subprocess_lock:
471
+ if listener.isCancelled:
472
+ return False
473
+ listener.suproc = await create_subprocess_exec(
474
+ "split",
475
+ "--numeric-suffixes=1",
476
+ "--suffix-length=3",
477
+ f"--bytes={split_size}",
478
+ path,
479
+ out_path,
480
+ stderr=PIPE,
481
+ )
482
+ _, stderr = await listener.suproc.communicate()
483
+ if listener.isCancelled:
484
+ return False
485
+ code = listener.suproc.returncode
486
+ if code == -9:
487
+ listener.isCancelled = True
488
+ return False
489
+ elif code != 0:
490
+ try:
491
+ stderr = stderr.decode().strip()
492
+ except:
493
+ stderr = "Unable to decode the error!"
494
+ LOGGER.error(f"{stderr}. Split Document: {path}")
495
+ return True
496
+
497
+
498
+async def createSampleVideo(listener, video_file, sample_duration, part_duration):
499
+ filter_complex = ""
500
+ dir, name = video_file.rsplit("/", 1)
501
+ output_file = f"{dir}/SAMPLE.{name}"
502
+ segments = [(0, part_duration)]
503
+ duration = (await get_media_info(video_file))[0]
504
+ remaining_duration = duration - (part_duration * 2)
505
+ parts = (sample_duration - (part_duration * 2)) // part_duration
506
+ time_interval = remaining_duration // parts
507
+ next_segment = time_interval
508
+ for _ in range(parts):
509
+ segments.append((next_segment, next_segment + part_duration))
510
+ next_segment += time_interval
511
+ segments.append((duration - part_duration, duration))
512
+
513
+ for i, (start, end) in enumerate(segments):
514
+ filter_complex += (
515
+ f"[0:v]trim=start={start}:end={end},setpts=PTS-STARTPTS[v{i}]; "
516
+ )
517
+ filter_complex += (
518
+ f"[0:a]atrim=start={start}:end={end},asetpts=PTS-STARTPTS[a{i}]; "
519
+ )
520
+
521
+ for i in range(len(segments)):
522
+ filter_complex += f"[v{i}][a{i}]"
523
+
524
+ filter_complex += f"concat=n={len(segments)}:v=1:a=1[vout][aout]"
525
+
526
+ cmd = [
527
+ "ffmpeg",
528
+ "-i",
529
+ video_file,
530
+ "-filter_complex",
531
+ filter_complex,
532
+ "-map",
533
+ "[vout]",
534
+ "-map",
535
+ "[aout]",
536
+ "-c:v",
537
+ "libx264",
538
+ "-c:a",
539
+ "aac",
540
+ "-threads",
541
+ f"{cpu_count() // 2}",
542
+ output_file,
543
+ ]
544
+
545
+ if listener.isCancelled:
546
+ return False
547
+ listener.suproc = await create_subprocess_exec(*cmd, stderr=PIPE)
548
+ _, stderr = await listener.suproc.communicate()
549
+ if listener.isCancelled:
550
+ return False
551
+ code = listener.suproc.returncode
552
+ if code == -9:
553
+ listener.isCancelled = True
554
+ return False
555
+ elif code == 0:
556
+ return output_file
557
+ else:
558
+ try:
559
+ stderr = stderr.decode().strip()
560
+ except:
561
+ stderr = "Unable to decode the error!"
562
+ LOGGER.error(
563
+ f"{stderr}. Something went wrong while creating sample video, mostly file is corrupted. Path: {video_file}"
564
+ )
565
+ if await aiopath.exists(output_file):
566
+ await remove(output_file)
567
+ return False
0
568
▼app/bot/helper/ext_utils/taskmanager.py
+162-0
1
+from asyncio import Event, sleep
2
+
3
+from bot import (
4
+ config_dict,
5
+ queued_dl,
6
+ queued_up,
7
+ non_queued_up,
8
+ non_queued_dl,
9
+ queue_dict_lock,
10
+ LOGGER,
11
+)
12
+from bot.helper.ext_utils.bot_utils import (
13
+ sync_to_async,
14
+ get_telegraph_list,
15
+)
16
+from bot.helper.ext_utils.files_utils import get_base_name
17
+from bot.helper.ext_utils.links_utils import is_gdrive_id
18
+from bot.helper.mirror_leech_utils.gdrive_utils.search import gdSearch
19
+
20
+
21
+async def stop_duplicate_check(listener):
22
+ if (
23
+ isinstance(listener.upDest, int)
24
+ or listener.isLeech
25
+ or listener.select
26
+ or not is_gdrive_id(listener.upDest)
27
+ or (listener.upDest.startswith("mtp:") and listener.stopDuplicate)
28
+ or not listener.stopDuplicate
29
+ or listener.sameDir
30
+ ):
31
+ return False, None
32
+
33
+ name = listener.name
34
+ LOGGER.info(f"Checking File/Folder if already in Drive: {name}")
35
+
36
+ if listener.compress:
37
+ name = f"{name}.zip"
38
+ elif listener.extract:
39
+ try:
40
+ name = get_base_name(name)
41
+ except:
42
+ name = None
43
+
44
+ if name is not None:
45
+ telegraph_content, contents_no = await sync_to_async(
46
+ gdSearch(stopDup=True, noMulti=listener.isClone).drive_list,
47
+ name,
48
+ listener.upDest,
49
+ listener.userId,
50
+ )
51
+ if telegraph_content:
52
+ msg = f"File/Folder is already available in Drive.\nHere are {contents_no} list results:"
53
+ button = await get_telegraph_list(telegraph_content)
54
+ return msg, button
55
+
56
+ return False, None
57
+
58
+
59
+async def check_running_tasks(listener, state="dl"):
60
+ all_limit = config_dict["QUEUE_ALL"]
61
+ state_limit = (
62
+ config_dict["QUEUE_DOWNLOAD"] if state == "dl" else config_dict["QUEUE_UPLOAD"]
63
+ )
64
+ event = None
65
+ is_over_limit = False
66
+ async with queue_dict_lock:
67
+ if state == "up" and listener.mid in non_queued_dl:
68
+ non_queued_dl.remove(listener.mid)
69
+ if (
70
+ (all_limit or state_limit)
71
+ and not listener.forceRun
72
+ and not (listener.forceUpload and state == "up")
73
+ and not (listener.forceDownload and state == "dl")
74
+ ):
75
+ dl_count = len(non_queued_dl)
76
+ up_count = len(non_queued_up)
77
+ t_count = dl_count if state == "dl" else up_count
78
+ is_over_limit = (
79
+ all_limit
80
+ and dl_count + up_count >= all_limit
81
+ and (not state_limit or t_count >= state_limit)
82
+ ) or (state_limit and t_count >= state_limit)
83
+ if is_over_limit:
84
+ event = Event()
85
+ if state == "dl":
86
+ queued_dl[listener.mid] = event
87
+ else:
88
+ queued_up[listener.mid] = event
89
+ if not is_over_limit:
90
+ if state == "up":
91
+ non_queued_up.add(listener.mid)
92
+ else:
93
+ non_queued_dl.add(listener.mid)
94
+
95
+ return is_over_limit, event
96
+
97
+
98
+async def start_dl_from_queued(mid: int):
99
+ queued_dl[mid].set()
100
+ del queued_dl[mid]
101
+ await sleep(0.7)
102
+
103
+
104
+async def start_up_from_queued(mid: int):
105
+ queued_up[mid].set()
106
+ del queued_up[mid]
107
+ await sleep(0.7)
108
+
109
+
110
+async def start_from_queued():
111
+ if all_limit := config_dict["QUEUE_ALL"]:
112
+ dl_limit = config_dict["QUEUE_DOWNLOAD"]
113
+ up_limit = config_dict["QUEUE_UPLOAD"]
114
+ async with queue_dict_lock:
115
+ dl = len(non_queued_dl)
116
+ up = len(non_queued_up)
117
+ all_ = dl + up
118
+ if all_ < all_limit:
119
+ f_tasks = all_limit - all_
120
+ if queued_up and (not up_limit or up < up_limit):
121
+ for index, mid in enumerate(list(queued_up.keys()), start=1):
122
+ f_tasks = all_limit - all_
123
+ await start_up_from_queued(mid)
124
+ f_tasks -= 1
125
+ if f_tasks == 0 or (up_limit and index >= up_limit - up):
126
+ break
127
+ if queued_dl and (not dl_limit or dl < dl_limit) and f_tasks != 0:
128
+ for index, mid in enumerate(list(queued_dl.keys()), start=1):
129
+ await start_dl_from_queued(mid)
130
+ if (dl_limit and index >= dl_limit - dl) or index == f_tasks:
131
+ break
132
+ return
133
+
134
+ if up_limit := config_dict["QUEUE_UPLOAD"]:
135
+ async with queue_dict_lock:
136
+ up = len(non_queued_up)
137
+ if queued_up and up < up_limit:
138
+ f_tasks = up_limit - up
139
+ for index, mid in enumerate(list(queued_up.keys()), start=1):
140
+ await start_up_from_queued(mid)
141
+ if index == f_tasks:
142
+ break
143
+ else:
144
+ async with queue_dict_lock:
145
+ if queued_up:
146
+ for mid in list(queued_up.keys()):
147
+ await start_up_from_queued(mid)
148
+
149
+ if dl_limit := config_dict["QUEUE_DOWNLOAD"]:
150
+ async with queue_dict_lock:
151
+ dl = len(non_queued_dl)
152
+ if queued_dl and dl < dl_limit:
153
+ f_tasks = dl_limit - dl
154
+ for index, mid in enumerate(list(queued_dl.keys()), start=1):
155
+ await start_dl_from_queued(mid)
156
+ if index == f_tasks:
157
+ break
158
+ else:
159
+ async with queue_dict_lock:
160
+ if queued_dl:
161
+ for mid in list(queued_dl.keys()):
162
+ await start_dl_from_queued(mid)
0
163
▼app/__init__.py → app/doodfunction/__init__.py
+0-0
0
0
▼app/doodfunction/upload.py
+23-0
1
+from settings import config as config
2
+# ,STREAMTAPEAPIKEY,STREAMTAPEUSERAGENT
3
+import requests
4
+from fastapi import APIRouter, File, UploadFile, Form,FastAPI
5
+from fastapi.responses import JSONResponse
6
+from fastapi.encoders import jsonable_encoder
7
+
8
+app=FastAPI()
9
+router = APIRouter()
10
+
11
+@app.get("/userinfo")
12
+async def userinfo():
13
+ url=f"https://doodapi.com/api/account/info?key=383661fid15w8jz8w50qza"
14
+ response = requests.get(url)
15
+ return response.json()
16
+
17
+
18
+
19
+
20
+
21
+if __name__ == "__main__":
22
+ import uvicorn
23
+ uvicorn.run(app, host="localhost", port=8000)
0
24
▼app/main.py
+23-0
1
+from app import setting
2
+# ,STREAMTAPEAPIKEY,STREAMTAPEUSERAGENT
3
+import requests
4
+from fastapi import APIRouter, File, UploadFile, Form,FastAPI
5
+from fastapi.responses import JSONResponse
6
+from fastapi.encoders import jsonable_encoder
7
+
8
+app=FastAPI()
9
+router = APIRouter()
10
+
11
+@app.get("userinfo")
12
+async def userinfo():
13
+ url=f"https://doodapi.com/api/account/info?key={DOODAPIKEY}"
14
+ response = requests.get(url)
15
+ return response.json()
16
+
17
+
18
+
19
+
20
+
21
+if __name__ == "__main__":
22
+ import uvicorn
23
+ uvicorn.run(app, host="localhost", port=8000)
0
24
▼app/schemas/test.py
+2-0
1
+def printx():
2
+ print("Hello World!")
0
3
▼app/settings/__init__.py → app/setting/__init__.py
+0-0
0
0
▼app/setting/config.py
+5-0
1
+DOODAPIKEY="383661fid15w8jz8w50qza"
2
+
3
+from app.schemas.test import printx
4
+
5
+printx()
0
6
▼app/settings/config.py
+0-0
0
0
▼setup.py
+21-0
1
+from setuptools import setup, find_packages
2
+
3
+setup(
4
+ name='stremaz',
5
+ version='0.1',
6
+ packages=find_packages(),
7
+ install_requires=[
8
+ 'fastapi',
9
+ 'uvicorn',
10
+ 'requests'
11
+ ],
12
+
13
+ entry_points={
14
+ 'console_scripts': [
15
+ 'stremaz = app.main:app'
16
+ ]
17
+ }
18
+
19
+)
20
+# Path: app/settings/config.py
21
+
0
22