async-mega-py 2.2.0__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/PKG-INFO +25 -26
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/README.md +24 -25
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/pyproject.toml +1 -1
- async_mega_py-2.2.2/src/mega/__init__.py +8 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/__main__.py +1 -2
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/api.py +30 -44
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/client.py +6 -3
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/data_structures.py +4 -2
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/transfer_it.py +7 -3
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/utils.py +2 -1
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/vault.py +4 -2
- async_mega_py-2.2.0/src/mega/__init__.py +0 -4
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/LICENSE +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/auth.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/chunker.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/core.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/crypto.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/download.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/env.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/errors.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/filesystem.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/progress/__init__.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/progress/rich_progress.py +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/py.typed +0 -0
- {async_mega_py-2.2.0 → async_mega_py-2.2.2}/src/mega/upload.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-mega.py
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: Python library for the Mega.nz and Transfer.it API
|
|
5
5
|
Keywords: api,downloader,mega,mega.nz,transfer.it
|
|
6
6
|
Author: NTFSvolume
|
|
@@ -79,7 +79,7 @@ Please check [`src/mega/data_structures.py`](src/mega/data_structures.py) for de
|
|
|
79
79
|
> [!TIP]
|
|
80
80
|
> You can run the commands bellow in an interactive python (the asyncio REPL). Try them in real time as is, using `async/await` keywords!
|
|
81
81
|
>
|
|
82
|
-
>
|
|
82
|
+
> `uv run -p3.12 --with async-mega-py python -m asyncio`
|
|
83
83
|
|
|
84
84
|
```python
|
|
85
85
|
from mega.client import MegaNzClient
|
|
@@ -99,7 +99,7 @@ from mega.client import MegaNzClient
|
|
|
99
99
|
# Also works without using it as a context manager, but you have the responsability to close the session at the end
|
|
100
100
|
mega = MegaNzClient()
|
|
101
101
|
await mega.login() # login using a temporary anonymous account
|
|
102
|
-
# await mega.close()
|
|
102
|
+
# await mega.close()
|
|
103
103
|
|
|
104
104
|
```
|
|
105
105
|
|
|
@@ -163,7 +163,7 @@ await mega.download(uploaded_file, output_dir)
|
|
|
163
163
|
|
|
164
164
|
# Download a public file
|
|
165
165
|
file_url = "https://mega.nz/file/vJ9EBJBC#vA1XpbngXcnN7lqXEWGQFPDc5ZlG6yltCHwGN-0O2LQ"
|
|
166
|
-
public_handle, public_key = mega.parse_file_url(file_url)
|
|
166
|
+
public_handle, public_key = mega.parse_file_url(file_url)
|
|
167
167
|
await mega.download_public_file(public_handle, public_key, output_dir)
|
|
168
168
|
|
|
169
169
|
# Download a public folder
|
|
@@ -186,7 +186,7 @@ print (result.is_folder)
|
|
|
186
186
|
> You can show a progress bar on the terminal for each download/upload by calling them within the `progress_bar` context manager (needs optional dependency `rich` to be installed):
|
|
187
187
|
|
|
188
188
|
```python
|
|
189
|
-
with mega.progress_bar:
|
|
189
|
+
with mega.progress_bar:
|
|
190
190
|
results = await mega.download_public_folder(public_handle, public_key, output_dir / "with_bar")
|
|
191
191
|
```
|
|
192
192
|
|
|
@@ -264,16 +264,14 @@ The output will be:
|
|
|
264
264
|
|
|
265
265
|
```json
|
|
266
266
|
{
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
267
|
+
"oImwb6nN": "/tests/script.js",
|
|
268
|
+
"FIXitv4F": "/tests/scripts",
|
|
269
|
+
"0fPFklV3": "/tests/scripts/notes.txt",
|
|
270
|
+
"l9zkz1GU": "/tests/scripts/script.js",
|
|
271
|
+
"E4LqT4EF": "/tests/scripts/styles.css"
|
|
272
272
|
}
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
|
|
277
275
|
```python
|
|
278
276
|
# Get the path to a node
|
|
279
277
|
fs.absolute_path("0fPFklV3") # /tests/scripts/notes.txt
|
|
@@ -294,24 +292,25 @@ for node in fs.iterdir(folder.id, recursive=True):
|
|
|
294
292
|
print(fs.absolute_path(node.id))
|
|
295
293
|
|
|
296
294
|
```
|
|
295
|
+
|
|
297
296
|
Output will be:
|
|
298
297
|
|
|
299
298
|
```json
|
|
300
299
|
[
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
300
|
+
"/tests/logo.png",
|
|
301
|
+
"/tests/logo.png",
|
|
302
|
+
"/tests/script.js",
|
|
303
|
+
"/tests/scripts",
|
|
304
|
+
"/tests/scripts/notes.txt",
|
|
305
|
+
"/tests/scripts/script.js",
|
|
306
|
+
"/tests/scripts/styles.css",
|
|
307
|
+
"/tests/setup.sh",
|
|
308
|
+
"/tests/utils.py"
|
|
310
309
|
]
|
|
311
310
|
```
|
|
312
311
|
|
|
313
312
|
> [!IMPORTANT]
|
|
314
|
-
> Mega's filesystem is
|
|
313
|
+
> Mega's filesystem is _not_ POSIX-compliant: multiple nodes may have the same path.
|
|
315
314
|
>
|
|
316
315
|
> If 2 nodes have the same path, `find` will throw an error.
|
|
317
316
|
|
|
@@ -352,10 +351,10 @@ mega-py --help
|
|
|
352
351
|
```
|
|
353
352
|
|
|
354
353
|
```powershell
|
|
355
|
-
Usage: mega-py [OPTIONS] COMMAND [ARGS]...
|
|
354
|
+
Usage: mega-py [OPTIONS] COMMAND [ARGS]...
|
|
356
355
|
|
|
357
|
-
CLI app for the Mega.nz and Transfer.it. Set
|
|
358
|
-
enviroment variables to use them as credentials for Mega
|
|
356
|
+
CLI app for the Mega.nz and Transfer.it. Set MEGA_EMAIL and MEGA_PWD
|
|
357
|
+
enviroment variables to use them as credentials for Mega
|
|
359
358
|
|
|
360
359
|
╭─ Options ──────────────────────────────────────────────────────────────────────╮
|
|
361
360
|
│ --verbose -v Increase verbosity (-v shows debug logs, -vv │
|
|
@@ -371,6 +370,6 @@ Usage: mega-py [OPTIONS] COMMAND [ARGS]...
|
|
|
371
370
|
```
|
|
372
371
|
|
|
373
372
|
> [!TIP]
|
|
374
|
-
> The CLI app does
|
|
373
|
+
> The CLI app does _not_ accept login credentials, but you can still use your account by setting up the `MEGA_EMAIL` and `MEGA_PWD` enviroment variables
|
|
375
374
|
>
|
|
376
375
|
> It will also read them from an `.env` file (if found)
|
|
@@ -46,7 +46,7 @@ Please check [`src/mega/data_structures.py`](src/mega/data_structures.py) for de
|
|
|
46
46
|
> [!TIP]
|
|
47
47
|
> You can run the commands bellow in an interactive python (the asyncio REPL). Try them in real time as is, using `async/await` keywords!
|
|
48
48
|
>
|
|
49
|
-
>
|
|
49
|
+
> `uv run -p3.12 --with async-mega-py python -m asyncio`
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
52
|
from mega.client import MegaNzClient
|
|
@@ -66,7 +66,7 @@ from mega.client import MegaNzClient
|
|
|
66
66
|
# Also works without using it as a context manager, but you have the responsability to close the session at the end
|
|
67
67
|
mega = MegaNzClient()
|
|
68
68
|
await mega.login() # login using a temporary anonymous account
|
|
69
|
-
# await mega.close()
|
|
69
|
+
# await mega.close()
|
|
70
70
|
|
|
71
71
|
```
|
|
72
72
|
|
|
@@ -130,7 +130,7 @@ await mega.download(uploaded_file, output_dir)
|
|
|
130
130
|
|
|
131
131
|
# Download a public file
|
|
132
132
|
file_url = "https://mega.nz/file/vJ9EBJBC#vA1XpbngXcnN7lqXEWGQFPDc5ZlG6yltCHwGN-0O2LQ"
|
|
133
|
-
public_handle, public_key = mega.parse_file_url(file_url)
|
|
133
|
+
public_handle, public_key = mega.parse_file_url(file_url)
|
|
134
134
|
await mega.download_public_file(public_handle, public_key, output_dir)
|
|
135
135
|
|
|
136
136
|
# Download a public folder
|
|
@@ -153,7 +153,7 @@ print (result.is_folder)
|
|
|
153
153
|
> You can show a progress bar on the terminal for each download/upload by calling them within the `progress_bar` context manager (needs optional dependency `rich` to be installed):
|
|
154
154
|
|
|
155
155
|
```python
|
|
156
|
-
with mega.progress_bar:
|
|
156
|
+
with mega.progress_bar:
|
|
157
157
|
results = await mega.download_public_folder(public_handle, public_key, output_dir / "with_bar")
|
|
158
158
|
```
|
|
159
159
|
|
|
@@ -231,16 +231,14 @@ The output will be:
|
|
|
231
231
|
|
|
232
232
|
```json
|
|
233
233
|
{
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
234
|
+
"oImwb6nN": "/tests/script.js",
|
|
235
|
+
"FIXitv4F": "/tests/scripts",
|
|
236
|
+
"0fPFklV3": "/tests/scripts/notes.txt",
|
|
237
|
+
"l9zkz1GU": "/tests/scripts/script.js",
|
|
238
|
+
"E4LqT4EF": "/tests/scripts/styles.css"
|
|
239
239
|
}
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
244
242
|
```python
|
|
245
243
|
# Get the path to a node
|
|
246
244
|
fs.absolute_path("0fPFklV3") # /tests/scripts/notes.txt
|
|
@@ -261,24 +259,25 @@ for node in fs.iterdir(folder.id, recursive=True):
|
|
|
261
259
|
print(fs.absolute_path(node.id))
|
|
262
260
|
|
|
263
261
|
```
|
|
262
|
+
|
|
264
263
|
Output will be:
|
|
265
264
|
|
|
266
265
|
```json
|
|
267
266
|
[
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
267
|
+
"/tests/logo.png",
|
|
268
|
+
"/tests/logo.png",
|
|
269
|
+
"/tests/script.js",
|
|
270
|
+
"/tests/scripts",
|
|
271
|
+
"/tests/scripts/notes.txt",
|
|
272
|
+
"/tests/scripts/script.js",
|
|
273
|
+
"/tests/scripts/styles.css",
|
|
274
|
+
"/tests/setup.sh",
|
|
275
|
+
"/tests/utils.py"
|
|
277
276
|
]
|
|
278
277
|
```
|
|
279
278
|
|
|
280
279
|
> [!IMPORTANT]
|
|
281
|
-
> Mega's filesystem is
|
|
280
|
+
> Mega's filesystem is _not_ POSIX-compliant: multiple nodes may have the same path.
|
|
282
281
|
>
|
|
283
282
|
> If 2 nodes have the same path, `find` will throw an error.
|
|
284
283
|
|
|
@@ -319,10 +318,10 @@ mega-py --help
|
|
|
319
318
|
```
|
|
320
319
|
|
|
321
320
|
```powershell
|
|
322
|
-
Usage: mega-py [OPTIONS] COMMAND [ARGS]...
|
|
321
|
+
Usage: mega-py [OPTIONS] COMMAND [ARGS]...
|
|
323
322
|
|
|
324
|
-
CLI app for the Mega.nz and Transfer.it. Set
|
|
325
|
-
enviroment variables to use them as credentials for Mega
|
|
323
|
+
CLI app for the Mega.nz and Transfer.it. Set MEGA_EMAIL and MEGA_PWD
|
|
324
|
+
enviroment variables to use them as credentials for Mega
|
|
326
325
|
|
|
327
326
|
╭─ Options ──────────────────────────────────────────────────────────────────────╮
|
|
328
327
|
│ --verbose -v Increase verbosity (-v shows debug logs, -vv │
|
|
@@ -338,6 +337,6 @@ Usage: mega-py [OPTIONS] COMMAND [ARGS]...
|
|
|
338
337
|
```
|
|
339
338
|
|
|
340
339
|
> [!TIP]
|
|
341
|
-
> The CLI app does
|
|
340
|
+
> The CLI app does _not_ accept login credentials, but you can still use your account by setting up the `MEGA_EMAIL` and `MEGA_PWD` enviroment variables
|
|
342
341
|
>
|
|
343
342
|
> It will also read them from an `.env` file (if found)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
from contextvars import ContextVar
|
|
3
|
+
|
|
4
|
+
_package_name_ = "async-mega-py"
|
|
5
|
+
__version__ = importlib.metadata.version(_package_name_)
|
|
6
|
+
|
|
7
|
+
LOG_FILE_PROGRESS: ContextVar[bool] = ContextVar("LOG_FILE_PROGRESS", default=True)
|
|
8
|
+
LOG_HTTP_TRAFFIC: ContextVar[bool] = ContextVar("LOG_HTTP_TRAFFIC", default=False)
|
|
@@ -14,8 +14,7 @@ _ = install_rich_traceback(width=None, max_frames=2)
|
|
|
14
14
|
import yarl
|
|
15
15
|
from cyclopts import App, Parameter
|
|
16
16
|
|
|
17
|
-
from mega import __version__, env
|
|
18
|
-
from mega.api import LOG_HTTP_TRAFFIC
|
|
17
|
+
from mega import LOG_HTTP_TRAFFIC, __version__, env
|
|
19
18
|
from mega.client import MegaNzClient
|
|
20
19
|
from mega.transfer_it import TransferItClient
|
|
21
20
|
from mega.utils import Site, setup_logger
|
|
@@ -7,15 +7,14 @@ import json
|
|
|
7
7
|
import logging
|
|
8
8
|
import uuid
|
|
9
9
|
from collections.abc import Mapping, Sequence
|
|
10
|
-
from contextvars import ContextVar
|
|
11
10
|
from functools import wraps
|
|
12
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Literal, ParamSpec, Self, TypeVar
|
|
11
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, ParamSpec, Self, TypeVar
|
|
13
12
|
|
|
14
13
|
import aiohttp
|
|
15
14
|
import yarl
|
|
16
15
|
from aiolimiter import AsyncLimiter
|
|
17
16
|
|
|
18
|
-
from mega import __version__, _package_name_
|
|
17
|
+
from mega import LOG_HTTP_TRAFFIC, __version__, _package_name_
|
|
19
18
|
from mega.crypto import generate_hashcash
|
|
20
19
|
from mega.errors import RequestError, RetryRequestError
|
|
21
20
|
from mega.utils import random_id, random_u32int
|
|
@@ -27,8 +26,6 @@ if TYPE_CHECKING:
|
|
|
27
26
|
_R = TypeVar("_R")
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
LOG_HTTP_TRAFFIC: ContextVar[bool] = ContextVar("LOG_HTTP_TRAFFIC", default=False)
|
|
31
|
-
|
|
32
29
|
logger = logging.getLogger(__name__)
|
|
33
30
|
|
|
34
31
|
|
|
@@ -66,27 +63,23 @@ def retry(
|
|
|
66
63
|
return wrapper
|
|
67
64
|
|
|
68
65
|
|
|
69
|
-
@dataclasses.dataclass(slots=True, weakref_slot=True
|
|
66
|
+
@dataclasses.dataclass(slots=True, weakref_slot=True)
|
|
70
67
|
class MegaAPI:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
self.
|
|
86
|
-
self.__session = session
|
|
87
|
-
self._auto_close_session = session is None
|
|
88
|
-
self._rate_limiter = AsyncLimiter(100, 60)
|
|
89
|
-
self.user_agent = user_agent or f"{_package_name_}/{__version__}"
|
|
68
|
+
_session: aiohttp.ClientSession | None = None
|
|
69
|
+
|
|
70
|
+
user_agent: str = f"{_package_name_}/{__version__}"
|
|
71
|
+
|
|
72
|
+
session_id: str | None = dataclasses.field(init=False, default=None)
|
|
73
|
+
_request_id: int = dataclasses.field(init=False, default_factory=random_u32int)
|
|
74
|
+
_client_id: str = dataclasses.field(init=False, default_factory=lambda: random_id(10))
|
|
75
|
+
|
|
76
|
+
_auto_close_session: bool = dataclasses.field(init=False)
|
|
77
|
+
_rate_limiter: AsyncLimiter = dataclasses.field(init=False, default_factory=lambda: AsyncLimiter(100, 60))
|
|
78
|
+
|
|
79
|
+
_entrypoint: ClassVar[yarl.URL] = dataclasses.field(init=False, default=yarl.URL("https://g.api.mega.co.nz/cs"))
|
|
80
|
+
|
|
81
|
+
def __post_init__(self) -> None:
|
|
82
|
+
self._auto_close_session = self._session is None
|
|
90
83
|
|
|
91
84
|
@property
|
|
92
85
|
def entrypoint(self) -> yarl.URL:
|
|
@@ -96,22 +89,12 @@ class MegaAPI:
|
|
|
96
89
|
def client_id(self) -> str:
|
|
97
90
|
return self._client_id
|
|
98
91
|
|
|
99
|
-
@property
|
|
100
|
-
def request_id(self) -> int:
|
|
101
|
-
return self._request_id
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def _session(self) -> aiohttp.ClientSession:
|
|
105
|
-
if self.__session is None:
|
|
106
|
-
self.__session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(sock_connect=160, sock_read=60))
|
|
107
|
-
return self.__session
|
|
108
|
-
|
|
109
92
|
def __repr__(self) -> str:
|
|
110
93
|
return f"<{type(self).__name__}>(session_id={self.session_id!r}, client_id={self.client_id!r}, auto_close_session={self._auto_close_session!r})"
|
|
111
94
|
|
|
112
95
|
async def aclose(self) -> None:
|
|
113
|
-
if self._auto_close_session and self.
|
|
114
|
-
await self.
|
|
96
|
+
if self._auto_close_session and self._session:
|
|
97
|
+
await self._session.close()
|
|
115
98
|
|
|
116
99
|
async def __enter__(self) -> Self:
|
|
117
100
|
return self
|
|
@@ -175,7 +158,7 @@ class MegaAPI:
|
|
|
175
158
|
headers: Mapping[str, str] | None = None,
|
|
176
159
|
**kwargs: Any,
|
|
177
160
|
) -> AsyncGenerator[aiohttp.ClientResponse]:
|
|
178
|
-
kwargs["headers"] = {"User-Agent": self.user_agent
|
|
161
|
+
kwargs["headers"] = {"User-Agent": self.user_agent, **(headers or {})}
|
|
179
162
|
request_id = str(uuid.uuid4())
|
|
180
163
|
if LOG_HTTP_TRAFFIC.get():
|
|
181
164
|
logger.debug(
|
|
@@ -187,6 +170,9 @@ class MegaAPI:
|
|
|
187
170
|
)
|
|
188
171
|
|
|
189
172
|
resp = None
|
|
173
|
+
if self._session is None:
|
|
174
|
+
self._session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(sock_connect=160, sock_read=60))
|
|
175
|
+
|
|
190
176
|
try:
|
|
191
177
|
async with self._rate_limiter, self._session.request(method, url, **kwargs) as resp:
|
|
192
178
|
yield resp
|
|
@@ -244,19 +230,19 @@ class _LazyResponseLog:
|
|
|
244
230
|
return str(self.__json__())
|
|
245
231
|
|
|
246
232
|
|
|
247
|
-
|
|
248
|
-
__slots__ = ("_api",)
|
|
233
|
+
_API_T = TypeVar("_API_T", bound=MegaAPI, covariant=True)
|
|
249
234
|
|
|
250
|
-
|
|
251
|
-
|
|
235
|
+
|
|
236
|
+
class APIContextManager(Generic[_API_T]):
|
|
237
|
+
__slots__ = ("_api",)
|
|
252
238
|
|
|
253
239
|
async def __aenter__(self) -> Self:
|
|
254
240
|
return self
|
|
255
241
|
|
|
256
242
|
async def __aexit__(self, *_) -> None:
|
|
257
|
-
await self.
|
|
243
|
+
await self.aclose()
|
|
258
244
|
|
|
259
245
|
async def aclose(self) -> None:
|
|
260
|
-
await self._api.
|
|
246
|
+
await self._api.aclose()
|
|
261
247
|
|
|
262
248
|
close = aclose
|
|
@@ -7,7 +7,7 @@ from pathlib import Path, PurePosixPath
|
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from mega import progress
|
|
10
|
-
from mega.api import APIContextManager
|
|
10
|
+
from mega.api import APIContextManager, MegaAPI
|
|
11
11
|
from mega.core import MegaCore
|
|
12
12
|
from mega.crypto import a32_to_base64, b64_to_a32, b64_url_encode, encrypt_attr, encrypt_key
|
|
13
13
|
from mega.data_structures import (
|
|
@@ -38,13 +38,16 @@ logger = logging.getLogger(__name__)
|
|
|
38
38
|
_DOMAIN = Site.MEGA.value
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
class MegaNzClient(APIContextManager):
|
|
41
|
+
class MegaNzClient(APIContextManager[MegaAPI]):
|
|
42
42
|
"""Interface with all the public methods of the API"""
|
|
43
43
|
|
|
44
44
|
__slots__ = ("_core",)
|
|
45
45
|
|
|
46
46
|
def __init__(self, session: aiohttp.ClientSession | None = None, *, user_agent: str | None = None) -> None:
|
|
47
|
-
|
|
47
|
+
self._api = MegaAPI(session)
|
|
48
|
+
if user_agent:
|
|
49
|
+
self._api.user_agent = user_agent
|
|
50
|
+
|
|
48
51
|
self._core: MegaCore = MegaCore(self._api)
|
|
49
52
|
if hasattr(sys, "ps1"):
|
|
50
53
|
setup_logger(logging.DEBUG)
|
|
@@ -13,7 +13,7 @@ import dataclasses
|
|
|
13
13
|
import time
|
|
14
14
|
from enum import IntEnum
|
|
15
15
|
from types import MappingProxyType
|
|
16
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Final, Self, TypeAlias, TypedDict
|
|
16
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Final, Literal, Self, TypeAlias, TypedDict
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from collections.abc import Mapping
|
|
@@ -67,7 +67,7 @@ class NodeSerialized(TypedDict):
|
|
|
67
67
|
h: NodeID # ID
|
|
68
68
|
p: NodeID # Parent ID
|
|
69
69
|
u: NotRequired[UserID] # Owner (user ID), not present in transfer.it nodes
|
|
70
|
-
t: ReadOnly[
|
|
70
|
+
t: ReadOnly[Literal[0, 1, 2, 3, 4, 5]] # Node type
|
|
71
71
|
a: str # Serialized attributes
|
|
72
72
|
ts: TimeStamp # creation date
|
|
73
73
|
|
|
@@ -76,6 +76,7 @@ class NodeSerialized(TypedDict):
|
|
|
76
76
|
sk: NotRequired[str] # Share key, only present present in shared (public) files / folder
|
|
77
77
|
|
|
78
78
|
s: NotRequired[int] # size (files only)
|
|
79
|
+
fa: NotRequired[str] # additional file attributes for multimedia files (ex: thumbail)
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
class OwnerKeys(TypedDict):
|
|
@@ -267,6 +268,7 @@ class Node(_DictDumper):
|
|
|
267
268
|
keys: dict[str, str] = {}
|
|
268
269
|
|
|
269
270
|
size = node.get("s")
|
|
271
|
+
|
|
270
272
|
return Node(
|
|
271
273
|
id=node["h"],
|
|
272
274
|
parent_id=node["p"],
|
|
@@ -45,9 +45,13 @@ class TransferItAPI(MegaAPI):
|
|
|
45
45
|
return await super().post(json, params)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
class TransferItClient(APIContextManager):
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
class TransferItClient(APIContextManager[TransferItAPI]):
|
|
49
|
+
__slots__ = ()
|
|
50
|
+
|
|
51
|
+
def __init__(self, session: aiohttp.ClientSession | None = None, *, user_agent: str | None = None) -> None:
|
|
52
|
+
self._api = TransferItAPI(session)
|
|
53
|
+
if user_agent:
|
|
54
|
+
self._api.user_agent = user_agent
|
|
51
55
|
|
|
52
56
|
@property
|
|
53
57
|
def progress_bar(self) -> _GeneratorContextManager[None, None, None]:
|
|
@@ -14,6 +14,7 @@ from typing import TYPE_CHECKING, Literal, TypeVar, overload
|
|
|
14
14
|
import aiohttp
|
|
15
15
|
import yarl
|
|
16
16
|
|
|
17
|
+
from mega import LOG_FILE_PROGRESS
|
|
17
18
|
from mega.errors import ValidationError
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
@@ -63,7 +64,7 @@ def setup_logger(level: int = logging.INFO) -> None:
|
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
def progress_logger(output_path: Path, file_size: int, *, download: bool) -> Callable[[float], None]:
|
|
66
|
-
if not logger.isEnabledFor(
|
|
67
|
+
if not (LOG_FILE_PROGRESS.get() and logger.isEnabledFor(logging.DEBUG)):
|
|
67
68
|
return lambda _: None
|
|
68
69
|
|
|
69
70
|
from mega.data_structures import ByteSize
|
|
@@ -50,12 +50,14 @@ class MegaVault:
|
|
|
50
50
|
|
|
51
51
|
# files shared with me
|
|
52
52
|
elif node.owner in self._shared_keys:
|
|
53
|
-
real_node_id
|
|
53
|
+
real_node_id = next(node_id for node_id in node.keys if node_id in self._shared_keys[node.owner])
|
|
54
|
+
share_key = self._shared_keys[node.owner][real_node_id]
|
|
54
55
|
full_key = decrypt_key(b64_to_a32(node.keys[real_node_id]), share_key)
|
|
55
56
|
|
|
56
57
|
# public files/folders
|
|
57
58
|
elif share_key := self._shared_keys[_EXPORTED].get(node.id):
|
|
58
|
-
|
|
59
|
+
real_node_id = next(node_id for node_id in node.keys if node_id in self._shared_keys[_EXPORTED])
|
|
60
|
+
encrypted_key = b64_to_a32(node.keys[real_node_id])
|
|
59
61
|
full_key = decrypt_key(encrypted_key, share_key)
|
|
60
62
|
|
|
61
63
|
else:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|