Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit d2173c9

Browse files
authored
tar/unzip G-Drive Links (#220)
* tar/unzip G-Drive Links Signed-off-by: anas <[email protected]> * Fix overall download speed for mega and others Signed-off-by: anas <[email protected]>
1 parent 2c35781 commit d2173c9

File tree

6 files changed

+207
-11
lines changed

6 files changed

+207
-11
lines changed

bot/helper/ext_utils/bot_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ def is_url(url: str):
139139
return True
140140
return False
141141

142+
def is_gdrive_link(url: str):
143+
return "drive.google.com" in url
142144

143145
def is_mega_link(url: str):
144146
return "mega.nz" in url

bot/helper/mirror_utils/download_utils/direct_link_generator.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ def direct_link_generator(link: str):
3434
raise DirectDownloadLinkException("`No links found!`")
3535
elif 'youtube.com' in link or 'youtu.be' in link:
3636
raise DirectDownloadLinkException(f"Youtube Link use /{BotCommands.WatchCommand} or /{BotCommands.TarWatchCommand}")
37-
elif 'drive.google.com' in link:
38-
raise DirectDownloadLinkException(f"G-Drive Link use /{BotCommands.CloneCommand}")
3937
elif 'zippyshare.com' in link:
4038
return zippy_share(link)
4139
elif 'yadi.sk' in link:
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from .status import Status
2+
from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
3+
from bot import DOWNLOAD_DIR
4+
5+
6+
class DownloadStatus(Status):
7+
def __init__(self, obj, size, listener, gid):
8+
self.dobj = obj
9+
self.__dsize = size
10+
self.uid = listener.uid
11+
self.message = listener.message
12+
self.__dgid = gid
13+
14+
def path(self):
15+
return f"{DOWNLOAD_DIR}{self.uid}"
16+
17+
def processed_bytes(self):
18+
return self.dobj.downloaded_bytes
19+
20+
def size_raw(self):
21+
return self.__dsize
22+
23+
def size(self):
24+
return get_readable_file_size(self.__dsize)
25+
26+
def status(self):
27+
return MirrorStatus.STATUS_DOWNLOADING
28+
29+
def name(self):
30+
return self.dobj.name
31+
32+
def gid(self) -> str:
33+
return self.__dgid
34+
35+
def progress_raw(self):
36+
try:
37+
return self.dobj.downloaded_bytes / self.__dsize * 100
38+
except ZeroDivisionError:
39+
return 0
40+
41+
def progress(self):
42+
return f'{round(self.progress_raw(), 2)}%'
43+
44+
def speed_raw(self):
45+
"""
46+
:return: Download speed in Bytes/Seconds
47+
"""
48+
return self.dobj.dspeed()
49+
50+
def speed(self):
51+
return f'{get_readable_file_size(self.speed_raw())}/s'
52+
53+
def eta(self):
54+
try:
55+
seconds = (self.__dsize - self.dobj.downloaded_bytes) / self.speed_raw()
56+
return f'{get_readable_time(seconds)}'
57+
except ZeroDivisionError:
58+
return '-'
59+
60+
def download(self):
61+
return self.dobj

bot/helper/mirror_utils/upload_utils/gdriveTools.py

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import io
23
import pickle
34
import urllib.parse as urlparse
45
from urllib.parse import parse_qs
@@ -13,7 +14,7 @@
1314
from google_auth_oauthlib.flow import InstalledAppFlow
1415
from googleapiclient.discovery import build
1516
from googleapiclient.errors import HttpError
16-
from googleapiclient.http import MediaFileUpload
17+
from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload
1718
from tenacity import *
1819

1920
from telegram import InlineKeyboardMarkup
@@ -43,14 +44,18 @@ def __init__(self, name=None, listener=None):
4344
self.__service = self.authorize()
4445
self.__listener = listener
4546
self._file_uploaded_bytes = 0
47+
self._file_downloaded_bytes = 0
4648
self.uploaded_bytes = 0
49+
self.downloaded_bytes = 0
4750
self.UPDATE_INTERVAL = 5
4851
self.start_time = 0
4952
self.total_time = 0
53+
self.dtotal_time = 0
5054
self._should_update = True
5155
self.is_uploading = True
5256
self.is_cancelled = False
5357
self.status = None
58+
self.dstatus = None
5459
self.updater = None
5560
self.name = name
5661
self.update_interval = 3
@@ -74,6 +79,12 @@ def speed(self):
7479
except ZeroDivisionError:
7580
return 0
7681

82+
def dspeed(self):
83+
try:
84+
return self.downloaded_bytes / self.dtotal_time
85+
except ZeroDivisionError:
86+
return 0
87+
7788
@staticmethod
7889
def getIdFromUrl(link: str):
7990
if "folders" in link or "file" in link:
@@ -728,3 +739,106 @@ def clonehelper(self, link):
728739
return msg, "", ""
729740
return "", clonesize, name
730741

742+
def download(self, link):
743+
self.is_downloading = True
744+
file_id = self.getIdFromUrl(link)
745+
if USE_SERVICE_ACCOUNTS:
746+
self.service_account_count = len(os.listdir("accounts"))
747+
self.start_time = time.time()
748+
self.updater = setInterval(self.update_interval, self._on_download_progress)
749+
try:
750+
meta = self.getFileMetadata(file_id)
751+
path = f"{DOWNLOAD_DIR}{self.__listener.uid}/"
752+
if meta.get("mimeType") == self.__G_DRIVE_DIR_MIME_TYPE:
753+
self.download_folder(file_id, path, meta.get('name'))
754+
else:
755+
os.makedirs(path)
756+
self.download_file(file_id, path, meta.get('name'), meta.get('mimeType'))
757+
except Exception as err:
758+
if isinstance(err, RetryError):
759+
LOGGER.info(f"Total Attempts: {err.last_attempt.attempt_number}")
760+
err = err.last_attempt.exception()
761+
err = str(err).replace('>', '').replace('<', '')
762+
LOGGER.error(err)
763+
self.is_cancelled = True
764+
self.__listener.onDownloadError(err)
765+
return
766+
finally:
767+
self.updater.cancel()
768+
if self.is_cancelled:
769+
return
770+
self.__listener.onDownloadComplete()
771+
772+
def download_folder(self, folder_id, path, folder_name):
773+
if not os.path.exists(path + folder_name):
774+
os.makedirs(path + folder_name)
775+
path += folder_name + '/'
776+
result = []
777+
page_token = None
778+
while True:
779+
files = self.__service.files().list(
780+
supportsTeamDrives=True,
781+
includeTeamDriveItems=True,
782+
q=f"'{folder_id}' in parents",
783+
fields='nextPageToken, files(id, name, mimeType, size, shortcutDetails)',
784+
pageToken=page_token,
785+
pageSize=1000).execute()
786+
result.extend(files['files'])
787+
page_token = files.get("nextPageToken")
788+
if not page_token:
789+
break
790+
791+
result = sorted(result, key=lambda k: k['name'])
792+
for item in result:
793+
file_id = item['id']
794+
filename = item['name']
795+
mime_type = item['mimeType']
796+
shortcut_details = item.get('shortcutDetails', None)
797+
if shortcut_details != None:
798+
file_id = shortcut_details['targetId']
799+
mime_type = shortcut_details['targetMimeType']
800+
if mime_type == 'application/vnd.google-apps.folder':
801+
self.download_folder(file_id, path, filename)
802+
elif not os.path.isfile(path + filename):
803+
self.download_file(file_id, path, filename, mime_type)
804+
if self.is_cancelled:
805+
break
806+
return
807+
808+
def download_file(self, file_id, path, filename, mime_type):
809+
request = self.__service.files().get_media(fileId=file_id)
810+
fh = io.FileIO('{}{}'.format(path, filename), 'wb')
811+
downloader = MediaIoBaseDownload(fh, request, chunksize = 50 * 1024 * 1024)
812+
done = False
813+
while done is False:
814+
if self.is_cancelled:
815+
fh.close()
816+
break
817+
return
818+
try:
819+
self.dstatus, done = downloader.next_chunk()
820+
except HttpError as err:
821+
if err.resp.get('content-type', '').startswith('application/json'):
822+
reason = json.loads(err.content).get('error').get('errors')[0].get('reason')
823+
if reason == 'userRateLimitExceeded' or reason == 'dailyLimitExceeded':
824+
if USE_SERVICE_ACCOUNTS:
825+
if not self.switchServiceAccount():
826+
raise err
827+
LOGGER.info(f"Got: {reason}, Trying Again...")
828+
return self.download_file(file_id, path, filename, mime_type)
829+
else:
830+
raise err
831+
else:
832+
raise err
833+
self._file_downloaded_bytes = 0
834+
835+
def _on_download_progress(self):
836+
if self.dstatus is not None:
837+
chunk_size = self.dstatus.total_size * self.dstatus.progress() - self._file_downloaded_bytes
838+
self._file_downloaded_bytes = self.dstatus.total_size * self.dstatus.progress()
839+
self.downloaded_bytes += chunk_size
840+
self.dtotal_time += self.update_interval
841+
842+
def cancel_download(self):
843+
self.is_cancelled = True
844+
self.__listener.onDownloadError('Download stopped by user!')

bot/helper/telegram_helper/message_utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ def update_all_messages():
8080
for download in list(download_dict.values()):
8181
speedy = download.speed()
8282
if download.status() == MirrorStatus.STATUS_DOWNLOADING:
83-
if 'KiB/s' in speedy:
83+
if 'K' in speedy:
8484
dlspeed_bytes += float(speedy.split('K')[0]) * 1024
85-
elif 'MiB/s' in speedy:
85+
elif 'M' in speedy:
8686
dlspeed_bytes += float(speedy.split('M')[0]) * 1048576
8787
if download.status() == MirrorStatus.STATUS_UPLOADING:
8888
if 'KB/s' in speedy:
@@ -118,9 +118,9 @@ def sendStatusMessage(msg, bot):
118118
for download in list(download_dict.values()):
119119
speedy = download.speed()
120120
if download.status() == MirrorStatus.STATUS_DOWNLOADING:
121-
if 'KiB/s' in speedy:
121+
if 'K' in speedy:
122122
dlspeed_bytes += float(speedy.split('K')[0]) * 1024
123-
elif 'MiB/s' in speedy:
123+
elif 'M' in speedy:
124124
dlspeed_bytes += float(speedy.split('M')[0]) * 1048576
125125
if download.status() == MirrorStatus.STATUS_UPLOADING:
126126
if 'KB/s' in speedy:

bot/modules/mirror.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from bot.helper.mirror_utils.status_utils.extract_status import ExtractStatus
1616
from bot.helper.mirror_utils.status_utils.tar_status import TarStatus
1717
from bot.helper.mirror_utils.status_utils.upload_status import UploadStatus
18+
from bot.helper.mirror_utils.status_utils.gdownload_status import DownloadStatus
1819
from bot.helper.mirror_utils.upload_utils import gdriveTools
1920
from bot.helper.telegram_helper.bot_commands import BotCommands
2021
from bot.helper.telegram_helper.filters import CustomFilters
@@ -26,6 +27,8 @@
2627
import subprocess
2728
import threading
2829
import re
30+
import random
31+
import string
2932

3033
ariaDlManager = AriaDownloadHelper()
3134
ariaDlManager.start_listener()
@@ -291,14 +294,32 @@ def _mirror(bot, update, isTar=False, extract=False):
291294
if "ERROR:" in str(e):
292295
sendMessage(f"{e}", bot, update)
293296
return
294-
if "G-Drive" in str(e):
295-
sendMessage(f"ERROR: {e}", bot, update)
296-
return
297297
if "Youtube" in str(e):
298298
sendMessage(f"ERROR: {e}", bot, update)
299299
return
300+
300301
listener = MirrorListener(bot, update, pswd, isTar, tag, extract)
301-
if bot_utils.is_mega_link(link):
302+
303+
if bot_utils.is_gdrive_link(link):
304+
if not isTar and not extract:
305+
sendMessage(f"Use /{BotCommands.CloneCommand} To Copy File/Folder", bot, update)
306+
return
307+
res, size, name = gdriveTools.GoogleDriveHelper().clonehelper(link)
308+
if res != "":
309+
sendMessage(res, bot, update)
310+
return
311+
LOGGER.info(f"Download Name : {name}")
312+
drive = gdriveTools.GoogleDriveHelper(name, listener)
313+
gid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=12))
314+
download_status = DownloadStatus(drive, size, listener, gid)
315+
with download_dict_lock:
316+
download_dict[listener.uid] = download_status
317+
if len(Interval) == 0:
318+
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
319+
sendStatusMessage(update, bot)
320+
drive.download(link)
321+
322+
elif bot_utils.is_mega_link(link):
302323
link_type = get_mega_link_type(link)
303324
if link_type == "folder" and BLOCK_MEGA_FOLDER:
304325
sendMessage("Mega folder are blocked!", bot, update)

0 commit comments

Comments
 (0)