async-mega-py 2.1.1__tar.gz → 2.2.0__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.1.1 → async_mega_py-2.2.0}/PKG-INFO +1 -1
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/pyproject.toml +1 -1
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/__main__.py +5 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/api.py +57 -17
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/client.py +2 -2
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/data_structures.py +3 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/transfer_it.py +2 -2
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/LICENSE +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/README.md +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/__init__.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/auth.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/chunker.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/core.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/crypto.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/download.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/env.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/errors.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/filesystem.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/progress/__init__.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/progress/rich_progress.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/py.typed +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/upload.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/utils.py +0 -0
- {async_mega_py-2.1.1 → async_mega_py-2.2.0}/src/mega/vault.py +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ruff: noqa: E402
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
|
|
3
4
|
import contextlib
|
|
@@ -6,6 +7,10 @@ import logging
|
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import TYPE_CHECKING, Annotated
|
|
8
9
|
|
|
10
|
+
from rich.traceback import install as install_rich_traceback
|
|
11
|
+
|
|
12
|
+
_ = install_rich_traceback(width=None, max_frames=2)
|
|
13
|
+
|
|
9
14
|
import yarl
|
|
10
15
|
from cyclopts import App, Parameter
|
|
11
16
|
|
|
@@ -3,17 +3,19 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import contextlib
|
|
5
5
|
import dataclasses
|
|
6
|
+
import json
|
|
6
7
|
import logging
|
|
8
|
+
import uuid
|
|
7
9
|
from collections.abc import Mapping, Sequence
|
|
8
10
|
from contextvars import ContextVar
|
|
9
11
|
from functools import wraps
|
|
10
|
-
from types import MappingProxyType
|
|
11
12
|
from typing import TYPE_CHECKING, Any, ClassVar, Literal, ParamSpec, Self, TypeVar
|
|
12
13
|
|
|
13
14
|
import aiohttp
|
|
14
15
|
import yarl
|
|
15
16
|
from aiolimiter import AsyncLimiter
|
|
16
17
|
|
|
18
|
+
from mega import __version__, _package_name_
|
|
17
19
|
from mega.crypto import generate_hashcash
|
|
18
20
|
from mega.errors import RequestError, RetryRequestError
|
|
19
21
|
from mega.utils import random_id, random_u32int
|
|
@@ -25,11 +27,8 @@ if TYPE_CHECKING:
|
|
|
25
27
|
_R = TypeVar("_R")
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0"
|
|
29
|
-
_DEFAULT_HEADERS: MappingProxyType[str, str] = MappingProxyType({"User-Agent": _UA})
|
|
30
|
-
|
|
31
|
-
|
|
32
30
|
LOG_HTTP_TRAFFIC: ContextVar[bool] = ContextVar("LOG_HTTP_TRAFFIC", default=False)
|
|
31
|
+
|
|
33
32
|
logger = logging.getLogger(__name__)
|
|
34
33
|
|
|
35
34
|
|
|
@@ -78,14 +77,16 @@ class MegaAPI:
|
|
|
78
77
|
_rate_limiter: AsyncLimiter
|
|
79
78
|
|
|
80
79
|
_entrypoint: ClassVar[yarl.URL] = yarl.URL("https://g.api.mega.co.nz/cs")
|
|
80
|
+
user_agent: str
|
|
81
81
|
|
|
82
|
-
def __init__(self, session: aiohttp.ClientSession | None = None) -> None:
|
|
82
|
+
def __init__(self, session: aiohttp.ClientSession | None = None, user_agent: str | None = None) -> None:
|
|
83
83
|
self.session_id = None
|
|
84
84
|
self._request_id = random_u32int()
|
|
85
85
|
self._client_id = random_id(10)
|
|
86
86
|
self.__session = session
|
|
87
87
|
self._auto_close_session = session is None
|
|
88
88
|
self._rate_limiter = AsyncLimiter(100, 60)
|
|
89
|
+
self.user_agent = user_agent or f"{_package_name_}/{__version__}"
|
|
89
90
|
|
|
90
91
|
@property
|
|
91
92
|
def entrypoint(self) -> yarl.URL:
|
|
@@ -174,19 +175,37 @@ class MegaAPI:
|
|
|
174
175
|
headers: Mapping[str, str] | None = None,
|
|
175
176
|
**kwargs: Any,
|
|
176
177
|
) -> AsyncGenerator[aiohttp.ClientResponse]:
|
|
177
|
-
kwargs["headers"] =
|
|
178
|
+
kwargs["headers"] = {"User-Agent": self.user_agent} | (headers or {})
|
|
179
|
+
request_id = str(uuid.uuid4())
|
|
178
180
|
if LOG_HTTP_TRAFFIC.get():
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
logger.debug(
|
|
182
|
+
"Starting %s request [id=%s] to %s \n%s",
|
|
183
|
+
method,
|
|
184
|
+
request_id,
|
|
185
|
+
url,
|
|
186
|
+
kwargs,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
resp = None
|
|
190
|
+
try:
|
|
191
|
+
async with self._rate_limiter, self._session.request(method, url, **kwargs) as resp:
|
|
192
|
+
yield resp
|
|
193
|
+
except RetryRequestError:
|
|
194
|
+
logger.warning("Request [id=%s] failed, retrying", request_id)
|
|
195
|
+
raise
|
|
196
|
+
finally:
|
|
197
|
+
if resp and LOG_HTTP_TRAFFIC.get():
|
|
198
|
+
logger.debug(
|
|
199
|
+
"Finished %s request [id=%s]\n%s",
|
|
200
|
+
method,
|
|
201
|
+
request_id,
|
|
202
|
+
_LazyResponseLog(resp),
|
|
203
|
+
)
|
|
183
204
|
|
|
184
205
|
@staticmethod
|
|
185
206
|
async def _parse_response(response: aiohttp.ClientResponse) -> Any:
|
|
186
207
|
json_resp = await response.json()
|
|
187
208
|
resp = json_resp
|
|
188
|
-
if LOG_HTTP_TRAFFIC.get():
|
|
189
|
-
logger.debug(f"Got response [{response.status}] json={json_resp!r}")
|
|
190
209
|
|
|
191
210
|
if isinstance(json_resp, list) and len(json_resp) == 1:
|
|
192
211
|
resp = json_resp[0]
|
|
@@ -196,19 +215,40 @@ class MegaAPI:
|
|
|
196
215
|
return resp
|
|
197
216
|
|
|
198
217
|
if resp == -3:
|
|
199
|
-
msg = "Request failed, retrying"
|
|
200
|
-
logger.warning(msg)
|
|
201
218
|
raise RetryRequestError
|
|
202
219
|
raise RequestError(resp)
|
|
203
220
|
|
|
204
221
|
return resp
|
|
205
222
|
|
|
206
223
|
|
|
224
|
+
class _LazyResponseLog:
|
|
225
|
+
def __init__(self, resp: aiohttp.ClientResponse) -> None:
|
|
226
|
+
self.resp = resp
|
|
227
|
+
|
|
228
|
+
def __json__(self) -> dict[str, Any]:
|
|
229
|
+
me = {
|
|
230
|
+
"url": str(self.resp.url),
|
|
231
|
+
"status_code": self.resp.status,
|
|
232
|
+
"response_headers": dict(self.resp.headers),
|
|
233
|
+
"content": None,
|
|
234
|
+
}
|
|
235
|
+
if self.resp._body:
|
|
236
|
+
stripped = self.resp._body.strip()
|
|
237
|
+
if stripped:
|
|
238
|
+
content = json.loads(stripped.decode(self.resp.get_encoding()))
|
|
239
|
+
me.update(content=content)
|
|
240
|
+
|
|
241
|
+
return me
|
|
242
|
+
|
|
243
|
+
def __str__(self) -> str:
|
|
244
|
+
return str(self.__json__())
|
|
245
|
+
|
|
246
|
+
|
|
207
247
|
class APIContextManager:
|
|
208
248
|
__slots__ = ("_api",)
|
|
209
249
|
|
|
210
|
-
def __init__(self, session: aiohttp.ClientSession | None = None) -> None:
|
|
211
|
-
self._api: MegaAPI = MegaAPI(session)
|
|
250
|
+
def __init__(self, session: aiohttp.ClientSession | None = None, *, user_agent: str | None = None) -> None:
|
|
251
|
+
self._api: MegaAPI = MegaAPI(session, user_agent)
|
|
212
252
|
|
|
213
253
|
async def __aenter__(self) -> Self:
|
|
214
254
|
return self
|
|
@@ -43,8 +43,8 @@ class MegaNzClient(APIContextManager):
|
|
|
43
43
|
|
|
44
44
|
__slots__ = ("_core",)
|
|
45
45
|
|
|
46
|
-
def __init__(self, session: aiohttp.ClientSession | None = None) -> None:
|
|
47
|
-
super().__init__(session)
|
|
46
|
+
def __init__(self, session: aiohttp.ClientSession | None = None, *, user_agent: str | None = None) -> None:
|
|
47
|
+
super().__init__(session, user_agent=user_agent)
|
|
48
48
|
self._core: MegaCore = MegaCore(self._api)
|
|
49
49
|
if hasattr(sys, "ps1"):
|
|
50
50
|
setup_logger(logging.DEBUG)
|
|
@@ -134,6 +134,9 @@ class _DictDumper:
|
|
|
134
134
|
"""Get a JSONable dict representation of this object"""
|
|
135
135
|
return dataclasses.asdict(self)
|
|
136
136
|
|
|
137
|
+
def __json__(self) -> dict[str, Any]:
|
|
138
|
+
return self.dump()
|
|
139
|
+
|
|
137
140
|
def _shallow_dump(self) -> dict[str, Any]:
|
|
138
141
|
return {name: getattr(self, name) for name in _fields(type(self))}
|
|
139
142
|
|
|
@@ -46,8 +46,8 @@ class TransferItAPI(MegaAPI):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class TransferItClient(APIContextManager):
|
|
49
|
-
def __init__(self, session: aiohttp.ClientSession | None = None) -> None: # pyright: ignore[reportMissingSuperCall]
|
|
50
|
-
self._api = TransferItAPI(session)
|
|
49
|
+
def __init__(self, session: aiohttp.ClientSession | None = None, *, user_agent: str | None = None) -> None: # pyright: ignore[reportMissingSuperCall]
|
|
50
|
+
self._api = TransferItAPI(session, user_agent=user_agent)
|
|
51
51
|
|
|
52
52
|
@property
|
|
53
53
|
def progress_bar(self) -> _GeneratorContextManager[None, None, None]:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|