async-mega-py 2.3.0__tar.gz → 2.4.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.3.0 → async_mega_py-2.4.0}/PKG-INFO +1 -1
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/pyproject.toml +1 -1
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/data_structures.py +4 -6
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/filesystem.py +47 -113
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/vault.py +9 -4
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/LICENSE +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/README.md +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/__init__.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/__main__.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/api.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/auth.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/chunker.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/client.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/core.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/crypto.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/download.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/env.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/errors.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/progress/__init__.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/progress/rich_progress.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/py.typed +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/transfer_it.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/upload.py +0 -0
- {async_mega_py-2.3.0 → async_mega_py-2.4.0}/src/mega/utils.py +0 -0
|
@@ -29,7 +29,6 @@ SharedKeys: TypeAlias = dict[NodeID, tuple[int, ...]]
|
|
|
29
29
|
|
|
30
30
|
NodeKey = tuple[int, int, int, int]
|
|
31
31
|
ComposedFileKey = tuple[int, int, int, int, int, int, int, int]
|
|
32
|
-
ComposedFolderKey = tuple[int, int, int, int]
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
class ByteSize(int):
|
|
@@ -182,7 +181,7 @@ class Crypto(_DictDumper):
|
|
|
182
181
|
iv: tuple[int, int]
|
|
183
182
|
meta_mac: tuple[int, int]
|
|
184
183
|
|
|
185
|
-
full_key: ComposedFileKey |
|
|
184
|
+
full_key: ComposedFileKey | NodeKey
|
|
186
185
|
share_key: NodeKey | None
|
|
187
186
|
|
|
188
187
|
@classmethod
|
|
@@ -206,19 +205,18 @@ class Crypto(_DictDumper):
|
|
|
206
205
|
raise RuntimeError(f"Invalid key, expected key len for files is 8, got {len(composed_key) = }")
|
|
207
206
|
|
|
208
207
|
else:
|
|
209
|
-
if len(key) != 4:
|
|
210
|
-
raise RuntimeError(f"Invalid key, expected key len for folders is 4, got {len(key) = }")
|
|
211
|
-
|
|
212
208
|
iv = ()
|
|
213
209
|
meta_mac = ()
|
|
214
210
|
composed_key = key
|
|
211
|
+
if len(composed_key) != 4:
|
|
212
|
+
raise RuntimeError(f"Invalid key, expected key len for folders is 4, got {len(composed_key) = }")
|
|
215
213
|
|
|
216
214
|
return Crypto(key, iv, meta_mac, composed_key, None) # pyright: ignore[reportArgumentType]
|
|
217
215
|
|
|
218
216
|
@classmethod
|
|
219
217
|
def decompose(
|
|
220
218
|
cls,
|
|
221
|
-
composed_key: ComposedFileKey |
|
|
219
|
+
composed_key: ComposedFileKey | NodeKey,
|
|
222
220
|
node_type: NodeType = NodeType.FILE,
|
|
223
221
|
share_key: NodeKey | None = None,
|
|
224
222
|
) -> Crypto:
|
|
@@ -12,7 +12,7 @@ from types import MappingProxyType
|
|
|
12
12
|
from typing import TYPE_CHECKING, Any, Final, NamedTuple, Self
|
|
13
13
|
|
|
14
14
|
from mega.data_structures import Node, NodeID, NodeType, _DictDumper
|
|
15
|
-
from mega.errors import MultipleNodesFoundError
|
|
15
|
+
from mega.errors import MultipleNodesFoundError, ValidationError
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from collections.abc import Generator, Iterable, Iterator
|
|
@@ -48,12 +48,15 @@ def _resolve_paths(walker: _NodeWalker, *roots: Node) -> Generator[_NodeLookup]:
|
|
|
48
48
|
|
|
49
49
|
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
|
|
50
50
|
class _NodeWalker:
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
nodes: MappingProxyType[NodeID, Node] = dataclasses.field(repr=False)
|
|
52
|
+
"A mapping of every node"
|
|
53
|
+
|
|
54
|
+
children: MappingProxyType[NodeID, tuple[NodeID, ...]] = dataclasses.field(repr=False)
|
|
55
|
+
"A mapping of nodes to their inmediate children"
|
|
53
56
|
|
|
54
57
|
def _ls(self, node_id: NodeID, *, recursive: bool) -> Iterable[NodeID]:
|
|
55
58
|
"""Get ID of every child of this node"""
|
|
56
|
-
for child_id in self.
|
|
59
|
+
for child_id in self.children.get(node_id, ()):
|
|
57
60
|
yield child_id
|
|
58
61
|
if recursive:
|
|
59
62
|
yield from self._ls(child_id, recursive=recursive)
|
|
@@ -61,14 +64,14 @@ class _NodeWalker:
|
|
|
61
64
|
def iterdir(self, node_id: NodeID, *, recursive: bool = False) -> Iterable[Node]:
|
|
62
65
|
"""Iterate over the children in this node"""
|
|
63
66
|
for child_id in self._ls(node_id, recursive=recursive):
|
|
64
|
-
yield self.
|
|
67
|
+
yield self.nodes[child_id]
|
|
65
68
|
|
|
66
69
|
def listdir(self, node_id: NodeID) -> list[Node]:
|
|
67
70
|
"""Get a list of children of this node (non recursive)"""
|
|
68
71
|
return list(self.iterdir(node_id))
|
|
69
72
|
|
|
70
73
|
|
|
71
|
-
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
|
|
74
|
+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True, repr=False)
|
|
72
75
|
class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
73
76
|
"""A simple representation of Mega.nz's file system.
|
|
74
77
|
|
|
@@ -82,6 +85,16 @@ class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
|
82
85
|
file_count: int
|
|
83
86
|
folder_count: int
|
|
84
87
|
|
|
88
|
+
def __repr__(self) -> str:
|
|
89
|
+
def fields():
|
|
90
|
+
for name, node in [("root", self.root), ("inbox", self.inbox), ("trash_bin", self.trash_bin)]:
|
|
91
|
+
yield name, node.id if node is not None else None
|
|
92
|
+
yield "files", self.file_count
|
|
93
|
+
yield "folders", self.folder_count
|
|
94
|
+
|
|
95
|
+
all_fields = ", ".join(f"{name}={value!r}" for name, value in fields())
|
|
96
|
+
return f"<{type(self).__name__}({all_fields})>"
|
|
97
|
+
|
|
85
98
|
def __len__(self) -> int:
|
|
86
99
|
return len(self.nodes)
|
|
87
100
|
|
|
@@ -95,16 +108,6 @@ class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
|
95
108
|
"""Get the node with this ID"""
|
|
96
109
|
return self.nodes[node_id]
|
|
97
110
|
|
|
98
|
-
@property
|
|
99
|
-
def nodes(self) -> MappingProxyType[NodeID, Node]:
|
|
100
|
-
"""A mapping of every node"""
|
|
101
|
-
return self._nodes
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def children(self) -> MappingProxyType[NodeID, tuple[NodeID, ...]]:
|
|
105
|
-
"""A mapping of nodes to their inmediate children"""
|
|
106
|
-
return self._children
|
|
107
|
-
|
|
108
111
|
@property
|
|
109
112
|
def deleted(self) -> Iterable[Node]:
|
|
110
113
|
"""Files or folders currently on the trash bin (Non recursive)"""
|
|
@@ -119,6 +122,11 @@ class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
|
119
122
|
nodes_map: dict[NodeID, Node] = {}
|
|
120
123
|
children: dict[NodeID, list[NodeID]] = {}
|
|
121
124
|
|
|
125
|
+
def sanity_check(current: Node | None, found: Node) -> None:
|
|
126
|
+
if current is not None:
|
|
127
|
+
ids = current.id, found.id
|
|
128
|
+
raise ValidationError(f"Multiple nodes of type {found.type.name} found: {ids}")
|
|
129
|
+
|
|
122
130
|
for node in nodes:
|
|
123
131
|
nodes_map[node.id] = node
|
|
124
132
|
if node.parent_id:
|
|
@@ -129,10 +137,13 @@ class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
|
129
137
|
case NodeType.FOLDER:
|
|
130
138
|
folder_count += 1
|
|
131
139
|
case NodeType.ROOT_FOLDER:
|
|
140
|
+
sanity_check(root, node)
|
|
132
141
|
root = node
|
|
133
142
|
case NodeType.INBOX:
|
|
143
|
+
sanity_check(inbox, node)
|
|
134
144
|
inbox = node
|
|
135
145
|
case NodeType.TRASH:
|
|
146
|
+
sanity_check(trash_bin, node)
|
|
136
147
|
trash_bin = node
|
|
137
148
|
case _:
|
|
138
149
|
raise RuntimeError # pyright: ignore[reportUnreachable]
|
|
@@ -143,8 +154,8 @@ class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
|
143
154
|
trash_bin=trash_bin,
|
|
144
155
|
file_count=file_count,
|
|
145
156
|
folder_count=folder_count,
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
nodes=MappingProxyType(nodes_map),
|
|
158
|
+
children=MappingProxyType({node_id: tuple(nodes) for node_id, nodes in children.items()}),
|
|
148
159
|
)
|
|
149
160
|
|
|
150
161
|
def dump(self) -> dict[str, Any]:
|
|
@@ -163,7 +174,7 @@ class SimpleFileSystem(_NodeWalker, _DictDumper):
|
|
|
163
174
|
return cls.build(Node.from_dump(node) for node in dump["nodes"].values())
|
|
164
175
|
|
|
165
176
|
|
|
166
|
-
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
|
|
177
|
+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True, repr=False)
|
|
167
178
|
class FileSystem(SimpleFileSystem):
|
|
168
179
|
"""Mega.nz's file system.
|
|
169
180
|
|
|
@@ -173,9 +184,13 @@ class FileSystem(SimpleFileSystem):
|
|
|
173
184
|
NOTE: Mega's filesystem is **not POSIX-compliant**: multiple nodes may have the same path
|
|
174
185
|
"""
|
|
175
186
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
paths: MappingProxyType[NodeID, PurePosixPath]
|
|
188
|
+
"""A mapping of every node to its absolute path within the filesystem"""
|
|
189
|
+
|
|
190
|
+
inv_paths: MappingProxyType[PurePosixPath, tuple[NodeID, ...]]
|
|
191
|
+
"""A mapping of paths to every node located at that path"""
|
|
192
|
+
|
|
193
|
+
_deleted: frozenset[NodeID]
|
|
179
194
|
|
|
180
195
|
@classmethod
|
|
181
196
|
def build(cls, nodes: Iterable[Node]) -> Self:
|
|
@@ -209,26 +224,13 @@ class FileSystem(SimpleFileSystem):
|
|
|
209
224
|
trash_bin=self.trash_bin,
|
|
210
225
|
file_count=self.file_count,
|
|
211
226
|
folder_count=self.folder_count,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
nodes=self.nodes,
|
|
228
|
+
children=self.children,
|
|
229
|
+
paths=MappingProxyType(paths),
|
|
230
|
+
inv_paths=MappingProxyType({path: tuple(nodes) for path, nodes in inv_paths.items()}),
|
|
216
231
|
_deleted=frozenset(deleted_ids),
|
|
217
232
|
)
|
|
218
233
|
|
|
219
|
-
@property
|
|
220
|
-
def paths(self) -> MappingProxyType[NodeID, PurePosixPath]:
|
|
221
|
-
"""A mapping of every node to its absolute path within the filesystem"""
|
|
222
|
-
return self._paths
|
|
223
|
-
|
|
224
|
-
@property
|
|
225
|
-
def inv_paths(self) -> MappingProxyType[PurePosixPath, tuple[NodeID, ...]]:
|
|
226
|
-
"""A mapping of paths to every node located at that path
|
|
227
|
-
|
|
228
|
-
Mega's filesystem is **not POSIX-compliant**: multiple nodes may have the same path
|
|
229
|
-
"""
|
|
230
|
-
return self._inv_paths
|
|
231
|
-
|
|
232
234
|
@property
|
|
233
235
|
def files(self) -> Iterable[Node]:
|
|
234
236
|
"""All files that are NOT deleted (recursive)"""
|
|
@@ -260,7 +262,7 @@ class FileSystem(SimpleFileSystem):
|
|
|
260
262
|
|
|
261
263
|
def absolute_path(self, node_id: NodeID) -> PurePosixPath:
|
|
262
264
|
"""Get the absolute path of this node"""
|
|
263
|
-
return self.
|
|
265
|
+
return self.paths[node_id]
|
|
264
266
|
|
|
265
267
|
def search(
|
|
266
268
|
self,
|
|
@@ -271,7 +273,7 @@ class FileSystem(SimpleFileSystem):
|
|
|
271
273
|
"""Returns nodes that have "query" as a substring on their path"""
|
|
272
274
|
query = PurePosixPath(query).as_posix()
|
|
273
275
|
|
|
274
|
-
for node_id, path in self.
|
|
276
|
+
for node_id, path in self.paths.items():
|
|
275
277
|
if query not in path.as_posix():
|
|
276
278
|
continue
|
|
277
279
|
|
|
@@ -292,7 +294,7 @@ class FileSystem(SimpleFileSystem):
|
|
|
292
294
|
"""
|
|
293
295
|
path = POSIX_ROOT / PurePosixPath(path)
|
|
294
296
|
try:
|
|
295
|
-
nodes = self.
|
|
297
|
+
nodes = self.inv_paths[path]
|
|
296
298
|
except LookupError:
|
|
297
299
|
msg = f'A node with path "{path!s}" does not exists'
|
|
298
300
|
raise FileNotFoundError(errno.ENOENT, msg) from None
|
|
@@ -332,84 +334,18 @@ class FileSystem(SimpleFileSystem):
|
|
|
332
334
|
|
|
333
335
|
return dump | dict( # noqa: C408
|
|
334
336
|
deleted=sorted(self._deleted),
|
|
335
|
-
paths={node_id: str(path) for node_id, path in self.
|
|
336
|
-
inv_paths={str(path): node_id for path, node_id in self.
|
|
337
|
+
paths={node_id: str(path) for node_id, path in self.paths.items()},
|
|
338
|
+
inv_paths={str(path): node_id for path, node_id in self.inv_paths.items()},
|
|
337
339
|
children=dict(self.children),
|
|
338
340
|
)
|
|
339
341
|
|
|
340
342
|
|
|
341
|
-
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True)
|
|
343
|
+
@dataclasses.dataclass(slots=True, frozen=True, kw_only=True, weakref_slot=True, repr=False)
|
|
342
344
|
class UserFileSystem(FileSystem):
|
|
343
345
|
root: Node
|
|
344
346
|
inbox: Node
|
|
345
347
|
trash_bin: Node
|
|
346
348
|
|
|
347
|
-
@classmethod
|
|
348
|
-
def build_unsafe(cls, nodes: Iterable[Node]) -> Self:
|
|
349
|
-
"""Build a filesystem from a pre-ordered node stream without validation
|
|
350
|
-
|
|
351
|
-
Nodes MUST be topologically sorted:
|
|
352
|
-
- The first three nodes are ROOT, INBOX and TRASH_BIN.
|
|
353
|
-
- Every parent appears before its descendants.
|
|
354
|
-
"""
|
|
355
|
-
# Build fs using only 3 loops:
|
|
356
|
-
# - 1. Create a map to all nodes and resolve their paths
|
|
357
|
-
# - 2. Freeze children (list -> tuple)
|
|
358
|
-
# - 3. Freeze inv paths (list -> tuple)
|
|
359
|
-
root = inbox = trash_bin = None
|
|
360
|
-
file_count = folder_count = 0
|
|
361
|
-
|
|
362
|
-
paths: dict[NodeID, PurePosixPath] = {}
|
|
363
|
-
inv_paths: dict[PurePosixPath, list[NodeID]] = {}
|
|
364
|
-
deleted_ids: set[NodeID] = set()
|
|
365
|
-
nodes_map: dict[NodeID, Node] = {}
|
|
366
|
-
children: dict[NodeID, list[NodeID]] = {}
|
|
367
|
-
trash_bin_id = None
|
|
368
|
-
|
|
369
|
-
for node in nodes:
|
|
370
|
-
nodes_map[node.id] = node
|
|
371
|
-
if node.parent_id:
|
|
372
|
-
children.setdefault(node.parent_id, []).append(node.id)
|
|
373
|
-
|
|
374
|
-
path = None
|
|
375
|
-
match node.type:
|
|
376
|
-
case NodeType.FILE:
|
|
377
|
-
file_count += 1
|
|
378
|
-
case NodeType.FOLDER:
|
|
379
|
-
folder_count += 1
|
|
380
|
-
case NodeType.ROOT_FOLDER:
|
|
381
|
-
path = POSIX_ROOT
|
|
382
|
-
root = node
|
|
383
|
-
case NodeType.INBOX:
|
|
384
|
-
path = POSIX_ROOT / node.attributes.name
|
|
385
|
-
inbox = node
|
|
386
|
-
case NodeType.TRASH:
|
|
387
|
-
path = POSIX_ROOT / node.attributes.name
|
|
388
|
-
trash_bin_id = node.id
|
|
389
|
-
trash_bin = node
|
|
390
|
-
case _:
|
|
391
|
-
raise RuntimeError # pyright: ignore[reportUnreachable]
|
|
392
|
-
|
|
393
|
-
path = path or (paths[node.parent_id] / node.attributes.name)
|
|
394
|
-
paths[node.id] = path
|
|
395
|
-
inv_paths.setdefault(path, []).append(node.id)
|
|
396
|
-
if node.parent_id == trash_bin_id or node.parent_id in deleted_ids:
|
|
397
|
-
deleted_ids.add(node.id)
|
|
398
|
-
|
|
399
|
-
assert root and inbox and trash_bin
|
|
400
|
-
return cls(
|
|
401
|
-
root=root,
|
|
402
|
-
inbox=inbox,
|
|
403
|
-
trash_bin=trash_bin,
|
|
404
|
-
file_count=file_count,
|
|
405
|
-
folder_count=folder_count,
|
|
406
|
-
_nodes=MappingProxyType(nodes_map),
|
|
407
|
-
_children=MappingProxyType({node_id: tuple(nodes) for node_id, nodes in children.items()}),
|
|
408
|
-
_paths=MappingProxyType(paths),
|
|
409
|
-
_inv_paths=MappingProxyType({path: tuple(nodes) for path, nodes in inv_paths.items()}),
|
|
410
|
-
_deleted=frozenset(deleted_ids),
|
|
411
|
-
)
|
|
412
|
-
|
|
413
349
|
|
|
414
350
|
if __name__ == "__main__":
|
|
415
351
|
import json
|
|
@@ -423,7 +359,5 @@ if __name__ == "__main__":
|
|
|
423
359
|
print(f"Testing filesystem build of {len(nodes):_} nodes") # noqa: T201
|
|
424
360
|
|
|
425
361
|
iterations = 1_000
|
|
426
|
-
unsafe = timeit.timeit(lambda: UserFileSystem.build_unsafe(nodes), number=iterations)
|
|
427
|
-
print(f"{iterations} [UNSAFE] calls took {unsafe:.4f}s") # noqa: T201
|
|
428
362
|
safe = timeit.timeit(lambda: UserFileSystem.build(nodes), number=iterations)
|
|
429
363
|
print(f"{iterations} [SAFE] calls took {safe:.4f}s") # noqa: T201
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
-
from typing import TYPE_CHECKING, Final, cast
|
|
4
|
+
from typing import TYPE_CHECKING, Final, NamedTuple, cast
|
|
5
5
|
|
|
6
6
|
from mega.crypto import b64_to_a32, decrypt_key
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from mega.data_structures import (
|
|
10
10
|
ComposedFileKey,
|
|
11
|
-
ComposedFolderKey,
|
|
12
11
|
GetNodesResponse,
|
|
13
12
|
Node,
|
|
14
13
|
NodeID,
|
|
14
|
+
NodeKey,
|
|
15
15
|
SharedKeys,
|
|
16
16
|
UserID,
|
|
17
17
|
)
|
|
@@ -21,6 +21,11 @@ _EXPORTED: Final = "EXP"
|
|
|
21
21
|
# An special owner used for exported (AKA public) file/folders
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class KeyLookup(NamedTuple):
|
|
25
|
+
full_key: ComposedFileKey | NodeKey
|
|
26
|
+
share_key: NodeKey | None
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
@dataclasses.dataclass(slots=True, frozen=True)
|
|
25
30
|
class MegaVault:
|
|
26
31
|
master_key: tuple[int, ...] = ()
|
|
@@ -43,7 +48,7 @@ class MegaVault:
|
|
|
43
48
|
if key := new_keys.get(node_id):
|
|
44
49
|
self._shared_keys.setdefault(owner, {})[node_id] = key
|
|
45
50
|
|
|
46
|
-
def __getitem__(self, node: Node) ->
|
|
51
|
+
def __getitem__(self, node: Node) -> KeyLookup:
|
|
47
52
|
share_key: tuple[int, ...] | None = None
|
|
48
53
|
|
|
49
54
|
# my files/folders
|
|
@@ -74,7 +79,7 @@ class MegaVault:
|
|
|
74
79
|
if len(full_key) not in (4, 8):
|
|
75
80
|
raise RuntimeError(f"Invalid key len found for node {node.id} ({len(full_key)})")
|
|
76
81
|
|
|
77
|
-
return cast("ComposedFileKey |
|
|
82
|
+
return KeyLookup(cast("ComposedFileKey | NodeKey", full_key), share_key)
|
|
78
83
|
|
|
79
84
|
def save_public_key(self, node_id: NodeID, share_key: tuple[int, ...]) -> None:
|
|
80
85
|
self._shared_keys[_EXPORTED][node_id] = share_key
|
|
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
|
|
File without changes
|
|
File without changes
|