Skip to content

Commit 80a375f

Browse files
committedOct 17, 2020
Bring back Mega support.
This reverts commit 0096b63.
1 parent 146140d commit 80a375f

File tree

9 files changed

+250
-7
lines changed

9 files changed

+250
-7
lines changed
 

‎Dockerfile

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM ubuntu:20.04
1+
FROM lzzy12/mega-sdk-python:latest
22

33
WORKDIR /usr/src/app
44
RUN chmod 777 /usr/src/app
@@ -22,3 +22,5 @@ COPY netrc /root/.netrc
2222
RUN chmod +x aria.sh
2323

2424
CMD ["bash","start.sh"]
25+
26+

‎README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,18 @@ python3 telegraph_token.py
5959
```
6060
python3 generate_string_session.py
6161
```
62+
- **MEGA_API_KEY**: Mega.nz api key to mirror mega.nz links. Get it from [Mega SDK Page](https://mega.nz/sdk)
63+
- **MEGA_EMAIL_ID**: Your email id you used to sign up on mega.nz for using premium accounts (Leave th)
64+
- **MEGA_PASSWORD**: Your password for your mega.nz account
65+
6266
Note: You can limit maximum concurrent downloads by changing the value of MAX_CONCURRENT_DOWNLOADS in aria.sh. By default, it's set to 2
6367

6468
## Getting Google OAuth API credential file
6569

6670
- Visit the [Google Cloud Console](https://console.developers.google.com/apis/credentials)
6771
- Go to the OAuth Consent tab, fill it, and save.
6872
- Go to the Credentials tab and click Create Credentials -> OAuth Client ID
69-
- Choose Other and Create.
73+
- Choose Desktop and Create.
7074
- Use the download button to download your credentials.
7175
- Move that file to the root of mirror-bot, and rename it to credentials.json
7276
- Visit [Google API page](https://console.developers.google.com/apis/library)

‎bot/__init__.py

+15
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ def mktable():
114114
cur.close()
115115
conn.close()
116116

117+
try:
118+
MEGA_API_KEY = getConfig('MEGA_API_KEY')
119+
except KeyError:
120+
LOGGER.warning('MEGA API KEY not provided!')
121+
MEGA_API_KEY = None
122+
try:
123+
MEGA_EMAIL_ID = getConfig('MEGA_EMAIL_ID')
124+
MEGA_PASSWORD = getConfig('MEGA_PASSWORD')
125+
if len(MEGA_EMAIL_ID) == 0 or len(MEGA_PASSWORD) == 0:
126+
raise KeyError
127+
except KeyError:
128+
LOGGER.warning('MEGA Credentials not provided!')
129+
MEGA_EMAIL_ID = None
130+
MEGA_PASSWORD = None
131+
117132
try:
118133
INDEX_URL = getConfig('INDEX_URL')
119134
if len(INDEX_URL) == 0:

‎bot/helper/ext_utils/bot_utils.py

+5
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ def is_magnet(url: str):
140140
return True
141141
return False
142142

143+
144+
def is_mega_link(url: str):
145+
return "mega.nz" in url
146+
147+
143148
def new_thread(fn):
144149
"""To use as decorator to make a function call threaded.
145150
Needs import
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
from bot import LOGGER, MEGA_API_KEY, download_dict_lock, download_dict, MEGA_EMAIL_ID, MEGA_PASSWORD
2+
import threading
3+
from mega import (MegaApi, MegaListener, MegaRequest, MegaTransfer, MegaError)
4+
from bot.helper.telegram_helper.message_utils import update_all_messages
5+
import os
6+
from bot.helper.mirror_utils.status_utils.mega_download_status import MegaDownloadStatus
7+
import random
8+
import string
9+
10+
class MegaDownloaderException(Exception):
11+
pass
12+
13+
14+
class MegaAppListener(MegaListener):
15+
_NO_EVENT_ON = (MegaRequest.TYPE_LOGIN,
16+
MegaRequest.TYPE_FETCH_NODES)
17+
18+
def __init__(self, continue_event: threading.Event, listener):
19+
self.continue_event = continue_event
20+
self.node = None
21+
self.listener = listener
22+
self.uid = listener.uid
23+
self.__bytes_transferred = 0
24+
self.is_cancelled = False
25+
self.__speed = 0
26+
self.__name = ''
27+
self.__size = 0
28+
self.error = None
29+
self.gid = ""
30+
super(MegaAppListener, self).__init__()
31+
32+
@property
33+
def speed(self):
34+
"""Returns speed of the download in bytes/second"""
35+
return self.__speed
36+
37+
@property
38+
def name(self):
39+
"""Returns name of the download"""
40+
return self.__name
41+
42+
def setValues(self, name, size, gid):
43+
self.__name = name
44+
self.__size = size
45+
self.gid = gid
46+
47+
@property
48+
def size(self):
49+
"""Size of download in bytes"""
50+
return self.__size
51+
52+
@property
53+
def downloaded_bytes(self):
54+
return self.__bytes_transferred
55+
56+
def onRequestStart(self, api, request):
57+
LOGGER.info('Request start ({})'.format(request))
58+
59+
def onRequestFinish(self, api, request, error):
60+
LOGGER.info('Mega Request finished ({}); Result: {}'
61+
.format(request, error))
62+
63+
request_type = request.getType()
64+
if request_type == MegaRequest.TYPE_LOGIN:
65+
api.fetchNodes()
66+
elif request_type == MegaRequest.TYPE_GET_PUBLIC_NODE:
67+
self.node = request.getPublicMegaNode()
68+
elif request_type == MegaRequest.TYPE_FETCH_NODES:
69+
LOGGER.info("Fetching Root Node.")
70+
self.node = api.getRootNode()
71+
if request_type not in self._NO_EVENT_ON:
72+
self.continue_event.set()
73+
74+
def onRequestTemporaryError(self, api, request, error: MegaError):
75+
self.listener.onDownloadError(error.toString())
76+
self.error = error.toString()
77+
self.continue_event.set()
78+
79+
def onTransferStart(self, api: MegaApi, transfer: MegaTransfer):
80+
LOGGER.info(f"Transfer Started: {transfer.getFileName()}")
81+
82+
def onTransferUpdate(self, api: MegaApi, transfer: MegaTransfer):
83+
if self.is_cancelled:
84+
api.cancelTransfer(transfer, None)
85+
self.__speed = transfer.getSpeed()
86+
self.__bytes_transferred = transfer.getTransferredBytes()
87+
88+
def onTransferFinish(self, api: MegaApi, transfer: MegaTransfer, error):
89+
try:
90+
LOGGER.info(f'Transfer finished ({transfer}); Result: {transfer.getFileName()}')
91+
if str(error) != "No error" and self.is_cancelled:
92+
self.is_cancelled = False
93+
return self.listener.onDownloadError(error.toString())
94+
if transfer.isFolderTransfer() and transfer.isFinished() and not self.is_cancelled or transfer.getFileName() == self.name and not self.is_cancelled:
95+
self.listener.onDownloadComplete()
96+
except Exception as e:
97+
LOGGER.error(e)
98+
99+
def onTransferTemporaryError(self, api, transfer, error):
100+
LOGGER.info(f'Mega download error in file {transfer} {transfer.getFileName()}: {error}')
101+
self.listener.onDownloadError(error.toString())
102+
self.error = error.toString()
103+
self.continue_event.set()
104+
105+
def cancel_download(self):
106+
self.is_cancelled = True
107+
108+
109+
class AsyncExecutor:
110+
111+
def __init__(self):
112+
self.continue_event = threading.Event()
113+
114+
def do(self, function, args):
115+
self.continue_event.clear()
116+
function(*args)
117+
self.continue_event.wait()
118+
119+
120+
class MegaDownloadHelper:
121+
def __init__(self):
122+
pass
123+
124+
@staticmethod
125+
def add_download(mega_link: str, path: str, listener):
126+
if MEGA_API_KEY is None:
127+
raise MegaDownloaderException('Mega API KEY not provided! Cannot mirror mega links')
128+
executor = AsyncExecutor()
129+
api = MegaApi(MEGA_API_KEY, None, None, 'telegram-mirror-bot')
130+
mega_listener = MegaAppListener(executor.continue_event, listener)
131+
os.makedirs(path)
132+
api.addListener(mega_listener)
133+
if MEGA_EMAIL_ID is not None and MEGA_PASSWORD is not None:
134+
executor.do(api.login, (MEGA_EMAIL_ID, MEGA_PASSWORD))
135+
executor.do(api.getPublicNode, (mega_link,))
136+
node = mega_listener.node
137+
if node is None:
138+
executor.do(api.loginToFolder, (mega_link,))
139+
node = mega_listener.node
140+
if mega_listener.error is not None:
141+
return listener.onDownloadError(str(mega_listener.error))
142+
gid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=8))
143+
mega_listener.setValues(node.getName(), api.getSize(node), gid)
144+
with download_dict_lock:
145+
download_dict[listener.uid] = MegaDownloadStatus(mega_listener, listener)
146+
threading.Thread(target=executor.do, args=(api.startDownload, (node, path))).start()
147+
update_all_messages()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from bot.helper.ext_utils.bot_utils import get_readable_file_size,MirrorStatus, get_readable_time
2+
from bot import DOWNLOAD_DIR
3+
from .status import Status
4+
5+
6+
class MegaDownloadStatus(Status):
7+
8+
def __init__(self, obj, listener):
9+
self.uid = obj.uid
10+
self.listener = listener
11+
self.obj = obj
12+
13+
def name(self) -> str:
14+
return self.obj.name
15+
16+
def progress_raw(self):
17+
try:
18+
return round(self.processed_bytes() / self.obj.size * 100,2)
19+
except ZeroDivisionError:
20+
return 0.0
21+
22+
def progress(self):
23+
"""Progress of download in percentage"""
24+
return f"{self.progress_raw()}%"
25+
26+
def status(self) -> str:
27+
return MirrorStatus.STATUS_DOWNLOADING
28+
29+
def processed_bytes(self):
30+
return self.obj.downloaded_bytes
31+
32+
def eta(self):
33+
try:
34+
seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
35+
return f'{get_readable_time(seconds)}'
36+
except ZeroDivisionError:
37+
return '-'
38+
39+
def size_raw(self):
40+
return self.obj.size
41+
42+
def size(self) -> str:
43+
return get_readable_file_size(self.size_raw())
44+
45+
def downloaded(self) -> str:
46+
return get_readable_file_size(self.obj.downloadedBytes)
47+
48+
def speed_raw(self):
49+
return self.obj.speed
50+
51+
def speed(self) -> str:
52+
return f'{get_readable_file_size(self.speed_raw())}/s'
53+
54+
def gid(self) -> str:
55+
return self.obj.gid
56+
57+
def path(self) -> str:
58+
return f"{DOWNLOAD_DIR}{self.uid}"
59+
60+
def download(self):
61+
return self.obj

‎bot/modules/mirror.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
from telegram.ext import CommandHandler, run_async
33
from telegram import InlineKeyboardMarkup
44

5-
from bot import Interval, INDEX_URL,LOGGER
5+
from bot import Interval, INDEX_URL
66
from bot import dispatcher, DOWNLOAD_DIR, DOWNLOAD_STATUS_UPDATE_INTERVAL, download_dict, download_dict_lock
77
from bot.helper.ext_utils import fs_utils, bot_utils
88
from bot.helper.ext_utils.bot_utils import setInterval
99
from bot.helper.ext_utils.exceptions import DirectDownloadLinkException, NotSupportedExtractionArchive
1010
from bot.helper.mirror_utils.download_utils.aria2_download import AriaDownloadHelper
11+
from bot.helper.mirror_utils.download_utils.mega_downloader import MegaDownloadHelper
1112
from bot.helper.mirror_utils.download_utils.direct_link_generator import direct_link_generator
1213
from bot.helper.mirror_utils.download_utils.telegram_downloader import TelegramDownloadHelper
1314
from bot.helper.mirror_utils.status_utils import listeners
@@ -28,7 +29,7 @@
2829
ariaDlManager.start_listener()
2930

3031
class MirrorListener(listeners.MirrorListeners):
31-
def __init__(self, bot, update, isTar=False,tag=None, extract=False):
32+
def __init__(self, bot, update, isTar=False, tag=None, extract=False):
3233
super().__init__(bot, update)
3334
self.isTar = isTar
3435
self.tag = tag
@@ -179,6 +180,7 @@ def onUploadError(self, error):
179180
else:
180181
update_all_messages()
181182

183+
182184
def _mirror(bot, update, isTar=False, extract=False):
183185
message_args = update.message.text.split(' ')
184186
try:
@@ -221,7 +223,11 @@ def _mirror(bot, update, isTar=False, extract=False):
221223
LOGGER.info(f'{link}: {e}')
222224

223225
listener = MirrorListener(bot, update, isTar, tag, extract)
224-
ariaDlManager.add_download(link, f'{DOWNLOAD_DIR}/{listener.uid}/',listener)
226+
if bot_utils.is_mega_link(link):
227+
mega_dl = MegaDownloadHelper()
228+
mega_dl.add_download(link, f'{DOWNLOAD_DIR}/{listener.uid}/', listener)
229+
else:
230+
ariaDlManager.add_download(link, f'{DOWNLOAD_DIR}/{listener.uid}/', listener)
225231
sendStatusMessage(update, bot)
226232
if len(Interval) == 0:
227233
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
@@ -239,7 +245,7 @@ def tar_mirror(update, context):
239245

240246
@run_async
241247
def unzip_mirror(update, context):
242-
_mirror(context.bot,update, extract=True)
248+
_mirror(context.bot, update, extract=True)
243249

244250

245251
mirror_handler = CommandHandler(BotCommands.MirrorCommand, mirror,

‎config_sample.env

+3
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ USER_SESSION_STRING = ""
1515
TELEGRAM_API =
1616
TELEGRAM_HASH = ""
1717
USE_SERVICE_ACCOUNTS = ""
18+
MEGA_API_KEY = ""
19+
MEGA_EMAIL_ID = ""
20+
MEGA_PASSWORD = ""

‎extract

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,4 @@ extract() {
191191
exit $code
192192
}
193193

194-
extract "$1"
194+
extract "$1"

0 commit comments

Comments
 (0)
Please sign in to comment.