neptune-sdk 0.0.1.dev580__py3-none-any.whl
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.
- neptune_sdk/__init__.py +69 -0
- neptune_sdk/client.py +311 -0
- neptune_sdk/exceptions.py +16 -0
- neptune_sdk/models.py +250 -0
- neptune_sdk-0.0.1.dev580.dist-info/METADATA +9 -0
- neptune_sdk-0.0.1.dev580.dist-info/RECORD +8 -0
- neptune_sdk-0.0.1.dev580.dist-info/WHEEL +4 -0
- neptune_sdk-0.0.1.dev580.dist-info/entry_points.txt +4 -0
neptune_sdk/__init__.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Neptune SDK — async Python client for the Neptune BitTorrent JSON-RPC API."""
|
|
2
|
+
|
|
3
|
+
from .client import NeptuneClient
|
|
4
|
+
from .exceptions import NeptuneConnectionError, NeptuneError, NeptuneRPCError
|
|
5
|
+
from .models import (
|
|
6
|
+
AddTorrentRequest,
|
|
7
|
+
AddTorrentResponse,
|
|
8
|
+
AddTrackerRequest,
|
|
9
|
+
DelCustomRequest,
|
|
10
|
+
InfoHashRequest,
|
|
11
|
+
ListTorrentRequest,
|
|
12
|
+
MainDataTorrent,
|
|
13
|
+
MoveTorrentRequest,
|
|
14
|
+
Peer,
|
|
15
|
+
RemoveTorrentRequest,
|
|
16
|
+
RemoveTrackerRequest,
|
|
17
|
+
ReplaceTrackersRequest,
|
|
18
|
+
SetCustomRequest,
|
|
19
|
+
SetFilePriorityRequest,
|
|
20
|
+
SetGlobalSpeedLimitRequest,
|
|
21
|
+
SetSpeedLimitRequest,
|
|
22
|
+
TagsRequest,
|
|
23
|
+
TorrentFile,
|
|
24
|
+
TorrentFilesResponse,
|
|
25
|
+
TorrentInfo,
|
|
26
|
+
TorrentListResponse,
|
|
27
|
+
TorrentPeersResponse,
|
|
28
|
+
TorrentTrackersResponse,
|
|
29
|
+
Tracker,
|
|
30
|
+
TransferSummary,
|
|
31
|
+
UpdateCustomRequest,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
# client
|
|
36
|
+
"NeptuneClient",
|
|
37
|
+
# exceptions
|
|
38
|
+
"NeptuneError",
|
|
39
|
+
"NeptuneRPCError",
|
|
40
|
+
"NeptuneConnectionError",
|
|
41
|
+
# request models
|
|
42
|
+
"AddTorrentRequest",
|
|
43
|
+
"AddTrackerRequest",
|
|
44
|
+
"DelCustomRequest",
|
|
45
|
+
"InfoHashRequest",
|
|
46
|
+
"ListTorrentRequest",
|
|
47
|
+
"MoveTorrentRequest",
|
|
48
|
+
"RemoveTorrentRequest",
|
|
49
|
+
"RemoveTrackerRequest",
|
|
50
|
+
"ReplaceTrackersRequest",
|
|
51
|
+
"SetCustomRequest",
|
|
52
|
+
"SetFilePriorityRequest",
|
|
53
|
+
"SetGlobalSpeedLimitRequest",
|
|
54
|
+
"SetSpeedLimitRequest",
|
|
55
|
+
"TagsRequest",
|
|
56
|
+
"UpdateCustomRequest",
|
|
57
|
+
# response / domain models
|
|
58
|
+
"AddTorrentResponse",
|
|
59
|
+
"MainDataTorrent",
|
|
60
|
+
"Peer",
|
|
61
|
+
"TorrentFile",
|
|
62
|
+
"TorrentFilesResponse",
|
|
63
|
+
"TorrentInfo",
|
|
64
|
+
"TorrentListResponse",
|
|
65
|
+
"TorrentPeersResponse",
|
|
66
|
+
"TorrentTrackersResponse",
|
|
67
|
+
"Tracker",
|
|
68
|
+
"TransferSummary",
|
|
69
|
+
]
|
neptune_sdk/client.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Sync Python client for the Neptune BitTorrent JSON-RPC API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import json
|
|
7
|
+
import random
|
|
8
|
+
from dataclasses import asdict
|
|
9
|
+
from functools import cache
|
|
10
|
+
from typing import Any, TypeVar
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
from pydantic import TypeAdapter
|
|
14
|
+
|
|
15
|
+
from .exceptions import NeptuneConnectionError, NeptuneRPCError
|
|
16
|
+
from .models import (
|
|
17
|
+
AddTorrentRequest,
|
|
18
|
+
AddTorrentResponse,
|
|
19
|
+
AddTrackerRequest,
|
|
20
|
+
DelCustomRequest,
|
|
21
|
+
InfoHashRequest,
|
|
22
|
+
ListTorrentRequest,
|
|
23
|
+
MoveTorrentRequest,
|
|
24
|
+
RemoveTorrentRequest,
|
|
25
|
+
RemoveTrackerRequest,
|
|
26
|
+
ReplaceTrackersRequest,
|
|
27
|
+
SetCustomRequest,
|
|
28
|
+
SetFilePriorityRequest,
|
|
29
|
+
SetGlobalSpeedLimitRequest,
|
|
30
|
+
SetSpeedLimitRequest,
|
|
31
|
+
TagsRequest,
|
|
32
|
+
TorrentFilesResponse,
|
|
33
|
+
TorrentInfo,
|
|
34
|
+
TorrentListResponse,
|
|
35
|
+
TorrentPeersResponse,
|
|
36
|
+
TorrentTrackersResponse,
|
|
37
|
+
TransferSummary,
|
|
38
|
+
UpdateCustomRequest,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
T = TypeVar("T")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _json_default(obj: Any) -> Any:
|
|
45
|
+
if isinstance(obj, (bytes, bytearray)):
|
|
46
|
+
return base64.b64encode(obj).decode()
|
|
47
|
+
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@cache
|
|
51
|
+
def _adapter(cls: type[T]) -> TypeAdapter[T]:
|
|
52
|
+
return TypeAdapter(cls)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _validate(cls: type[T], data: Any) -> T:
|
|
56
|
+
return _adapter(cls).validate_python(data)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class NeptuneClient:
|
|
60
|
+
"""Sync JSON-RPC client for Neptune.
|
|
61
|
+
|
|
62
|
+
Example::
|
|
63
|
+
|
|
64
|
+
with NeptuneClient("http://127.0.0.1:8002", token="secret") as c:
|
|
65
|
+
torrents = c.torrent_list()
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
base_url: str,
|
|
71
|
+
*,
|
|
72
|
+
token: str,
|
|
73
|
+
timeout: float = 30.0,
|
|
74
|
+
) -> None:
|
|
75
|
+
self._client = httpx.Client(
|
|
76
|
+
base_url=base_url.rstrip("/"),
|
|
77
|
+
headers={"Authorization": token},
|
|
78
|
+
timeout=timeout,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# ── lifecycle ──────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
def __enter__(self) -> NeptuneClient:
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
def __exit__(self, *exc: Any) -> None:
|
|
87
|
+
self.close()
|
|
88
|
+
|
|
89
|
+
def close(self) -> None:
|
|
90
|
+
self._client.close()
|
|
91
|
+
|
|
92
|
+
# ── transport ──────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
def _call(self, method: str, params: Any = None) -> Any:
|
|
95
|
+
"""Send a JSON-RPC request and return the result field."""
|
|
96
|
+
payload: dict[str, Any] = {
|
|
97
|
+
"jsonrpc": "2.0",
|
|
98
|
+
"method": method,
|
|
99
|
+
"id": random.randint(1, 2**31),
|
|
100
|
+
}
|
|
101
|
+
if params is not None:
|
|
102
|
+
payload["params"] = asdict(params)
|
|
103
|
+
|
|
104
|
+
body_bytes = json.dumps(payload, default=_json_default).encode()
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
resp = self._client.post(
|
|
108
|
+
"/json_rpc",
|
|
109
|
+
content=body_bytes,
|
|
110
|
+
headers={"Content-Type": "application/json"},
|
|
111
|
+
)
|
|
112
|
+
resp.raise_for_status()
|
|
113
|
+
except httpx.HTTPError as exc:
|
|
114
|
+
raise NeptuneConnectionError(str(exc)) from exc
|
|
115
|
+
|
|
116
|
+
body = resp.json()
|
|
117
|
+
|
|
118
|
+
if "error" in body and body["error"] is not None:
|
|
119
|
+
err = body["error"]
|
|
120
|
+
raise NeptuneRPCError(
|
|
121
|
+
code=err.get("code", -1),
|
|
122
|
+
message=err.get("message", ""),
|
|
123
|
+
data=err.get("data"),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return body.get("result")
|
|
127
|
+
|
|
128
|
+
# ── system ─────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
def ping(self) -> None:
|
|
131
|
+
"""system.ping — health check."""
|
|
132
|
+
self._call("system.ping")
|
|
133
|
+
|
|
134
|
+
# ── transfer ───────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
def transfer_summary(self) -> TransferSummary:
|
|
137
|
+
"""Global download/upload rates and totals."""
|
|
138
|
+
return _validate(TransferSummary, self._call("transfer_summary"))
|
|
139
|
+
|
|
140
|
+
# ── torrent — queries ──────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
def torrent_list(self, keys: list[str] | None = None) -> TorrentListResponse:
|
|
143
|
+
"""List all torrents. Optionally filter custom keys returned."""
|
|
144
|
+
return _validate(
|
|
145
|
+
TorrentListResponse,
|
|
146
|
+
self._call("torrent.list", ListTorrentRequest(keys=keys or None)),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def torrent_get(self, info_hash: str) -> TorrentInfo:
|
|
150
|
+
"""Get basic info for a single torrent."""
|
|
151
|
+
return _validate(
|
|
152
|
+
TorrentInfo,
|
|
153
|
+
self._call("torrent.get", InfoHashRequest(info_hash=info_hash)),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def torrent_files(self, info_hash: str) -> TorrentFilesResponse:
|
|
157
|
+
"""List files in a torrent."""
|
|
158
|
+
return _validate(
|
|
159
|
+
TorrentFilesResponse,
|
|
160
|
+
self._call("torrent.files", InfoHashRequest(info_hash=info_hash)),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def torrent_peers(self, info_hash: str) -> TorrentPeersResponse:
|
|
164
|
+
"""List connected peers for a torrent."""
|
|
165
|
+
return _validate(
|
|
166
|
+
TorrentPeersResponse,
|
|
167
|
+
self._call("torrent.peers", InfoHashRequest(info_hash=info_hash)),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def torrent_trackers(self, info_hash: str) -> TorrentTrackersResponse:
|
|
171
|
+
"""List trackers for a torrent."""
|
|
172
|
+
return _validate(
|
|
173
|
+
TorrentTrackersResponse,
|
|
174
|
+
self._call("torrent.trackers", InfoHashRequest(info_hash=info_hash)),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def torrent_add_tracker(self, info_hash: str, url: str, *, tier: int = 0) -> None:
|
|
178
|
+
"""Add a tracker to a torrent."""
|
|
179
|
+
self._call(
|
|
180
|
+
"torrent.add_tracker",
|
|
181
|
+
AddTrackerRequest(info_hash=info_hash, url=url, tier=tier),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def torrent_remove_tracker(self, info_hash: str, url: str) -> None:
|
|
185
|
+
"""Remove a tracker from a torrent."""
|
|
186
|
+
self._call(
|
|
187
|
+
"torrent.remove_tracker",
|
|
188
|
+
RemoveTrackerRequest(info_hash=info_hash, url=url),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def torrent_replace_trackers(
|
|
192
|
+
self, info_hash: str, replacements: dict[str, str]
|
|
193
|
+
) -> None:
|
|
194
|
+
"""Replace tracker URLs. Keys are old URLs, values are new URLs."""
|
|
195
|
+
self._call(
|
|
196
|
+
"torrent.replace_trackers",
|
|
197
|
+
ReplaceTrackersRequest(info_hash=info_hash, replacements=replacements),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# ── torrent — mutations ────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
def torrent_add(self, req: AddTorrentRequest) -> AddTorrentResponse:
|
|
203
|
+
"""Add a torrent from raw .torrent bytes."""
|
|
204
|
+
return _validate(AddTorrentResponse, self._call("torrent.add", req))
|
|
205
|
+
|
|
206
|
+
def torrent_move(self, info_hash: str, target_base_path: str) -> None:
|
|
207
|
+
"""Move torrent data to a new directory."""
|
|
208
|
+
self._call(
|
|
209
|
+
"torrent.move",
|
|
210
|
+
MoveTorrentRequest(info_hash=info_hash, target_base_path=target_base_path),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def torrent_remove(
|
|
214
|
+
self, info_hash: str, *, delete_data: bool = False
|
|
215
|
+
) -> TorrentListResponse:
|
|
216
|
+
"""Remove a torrent and return updated list."""
|
|
217
|
+
return _validate(
|
|
218
|
+
TorrentListResponse,
|
|
219
|
+
self._call(
|
|
220
|
+
"torrent.remove",
|
|
221
|
+
RemoveTorrentRequest(info_hash=info_hash, delete_data=delete_data),
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def torrent_start(self, info_hash: str) -> None:
|
|
226
|
+
"""Start a torrent."""
|
|
227
|
+
self._call("torrent.start", InfoHashRequest(info_hash=info_hash))
|
|
228
|
+
|
|
229
|
+
def torrent_stop(self, info_hash: str) -> None:
|
|
230
|
+
"""Stop a torrent."""
|
|
231
|
+
self._call("torrent.stop", InfoHashRequest(info_hash=info_hash))
|
|
232
|
+
|
|
233
|
+
def torrent_recheck(self, info_hash: str) -> None:
|
|
234
|
+
"""Recheck torrent data integrity."""
|
|
235
|
+
self._call("torrent.recheck", InfoHashRequest(info_hash=info_hash))
|
|
236
|
+
|
|
237
|
+
# ── torrent — custom ───────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
def torrent_custom_set(self, info_hash: str, key: str, value: str) -> None:
|
|
240
|
+
"""Set a custom key-value pair on a torrent."""
|
|
241
|
+
self._call(
|
|
242
|
+
"torrent.custom.set",
|
|
243
|
+
SetCustomRequest(info_hash=info_hash, key=key, value=value),
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def torrent_custom_update(self, info_hash: str, custom: dict[str, str]) -> None:
|
|
247
|
+
"""Update multiple custom key-value pairs on a torrent."""
|
|
248
|
+
self._call(
|
|
249
|
+
"torrent.custom.update",
|
|
250
|
+
UpdateCustomRequest(info_hash=info_hash, custom=custom),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def torrent_custom_del(self, info_hash: str, key: str) -> None:
|
|
254
|
+
"""Delete a custom key from a torrent."""
|
|
255
|
+
self._call(
|
|
256
|
+
"torrent.custom.del",
|
|
257
|
+
DelCustomRequest(info_hash=info_hash, key=key),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def torrent_add_tags(self, info_hash: str, tags: list[str]) -> None:
|
|
261
|
+
"""Add tags to a torrent."""
|
|
262
|
+
self._call("torrent.add_tags", TagsRequest(info_hash=info_hash, tags=tags))
|
|
263
|
+
|
|
264
|
+
def torrent_remove_tags(self, info_hash: str, tags: list[str]) -> None:
|
|
265
|
+
"""Remove tags from a torrent."""
|
|
266
|
+
self._call("torrent.remove_tags", TagsRequest(info_hash=info_hash, tags=tags))
|
|
267
|
+
|
|
268
|
+
# ── torrent — limits ───────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
def torrent_set_download_limit(self, info_hash: str, limit: int) -> None:
|
|
271
|
+
"""Set per-torrent download speed limit (bytes/s, <=0 = unlimited)."""
|
|
272
|
+
self._call(
|
|
273
|
+
"torrent.set_download_limit",
|
|
274
|
+
SetSpeedLimitRequest(info_hash=info_hash, limit=limit),
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def torrent_set_upload_limit(self, info_hash: str, limit: int) -> None:
|
|
278
|
+
"""Set per-torrent upload speed limit (bytes/s, <=0 = unlimited)."""
|
|
279
|
+
self._call(
|
|
280
|
+
"torrent.set_upload_limit",
|
|
281
|
+
SetSpeedLimitRequest(info_hash=info_hash, limit=limit),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
def client_set_download_limit(self, limit: int) -> None:
|
|
285
|
+
"""Set global download speed limit (bytes/s, <=0 = unlimited)."""
|
|
286
|
+
self._call(
|
|
287
|
+
"client.set_download_limit",
|
|
288
|
+
SetGlobalSpeedLimitRequest(limit=limit),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
def client_set_upload_limit(self, limit: int) -> None:
|
|
292
|
+
"""Set global upload speed limit (bytes/s, <=0 = unlimited)."""
|
|
293
|
+
self._call(
|
|
294
|
+
"client.set_upload_limit",
|
|
295
|
+
SetGlobalSpeedLimitRequest(limit=limit),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# ── torrent — file priority ────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
def torrent_set_file_priority(
|
|
301
|
+
self, info_hash: str, file_ids: list[int], priority: int
|
|
302
|
+
) -> None:
|
|
303
|
+
"""Set file priority (0 = skip, 1 = download)."""
|
|
304
|
+
self._call(
|
|
305
|
+
"torrent.set_file_priority",
|
|
306
|
+
SetFilePriorityRequest(
|
|
307
|
+
info_hash=info_hash,
|
|
308
|
+
file_ids=file_ids,
|
|
309
|
+
priority=priority,
|
|
310
|
+
),
|
|
311
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class NeptuneError(Exception):
|
|
2
|
+
"""Base exception for Neptune SDK."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class NeptuneRPCError(NeptuneError):
|
|
6
|
+
"""Raised when the JSON-RPC server returns an error response."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, code: int, message: str, data: object = None) -> None:
|
|
9
|
+
self.code = code
|
|
10
|
+
self.message = message
|
|
11
|
+
self.data = data
|
|
12
|
+
super().__init__(f"RPC error {code}: {message}")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NeptuneConnectionError(NeptuneError):
|
|
16
|
+
"""Raised when the HTTP connection to Neptune fails."""
|
neptune_sdk/models.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
# ── Shared domain types ──────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
9
|
+
class MainDataTorrent:
|
|
10
|
+
"""A torrent entry returned by torrent.list / torrent.remove."""
|
|
11
|
+
|
|
12
|
+
hash: str
|
|
13
|
+
name: str
|
|
14
|
+
state: str
|
|
15
|
+
comment: str
|
|
16
|
+
directory_base: str
|
|
17
|
+
message: str
|
|
18
|
+
tracker_errors: dict[str, str]
|
|
19
|
+
tags: list[str]
|
|
20
|
+
custom: dict[str, str]
|
|
21
|
+
download_rate: int
|
|
22
|
+
download_total: int
|
|
23
|
+
upload_rate: int
|
|
24
|
+
upload_total: int
|
|
25
|
+
connection_count: int
|
|
26
|
+
completed: int
|
|
27
|
+
total_length: int
|
|
28
|
+
selected_size: int
|
|
29
|
+
add_at: int
|
|
30
|
+
private: bool
|
|
31
|
+
total_seeding: int
|
|
32
|
+
total_downloading: int
|
|
33
|
+
connected_seeding: int
|
|
34
|
+
connected_downloading: int
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
38
|
+
class TransferSummary:
|
|
39
|
+
"""Global transfer rates and totals."""
|
|
40
|
+
|
|
41
|
+
download_rate: int
|
|
42
|
+
download_total: int
|
|
43
|
+
upload_rate: int
|
|
44
|
+
upload_total: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
48
|
+
class TorrentFile:
|
|
49
|
+
"""A single file inside a torrent."""
|
|
50
|
+
|
|
51
|
+
path: list[str]
|
|
52
|
+
index: int
|
|
53
|
+
progress: float
|
|
54
|
+
size: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
58
|
+
class Peer:
|
|
59
|
+
"""A connected peer."""
|
|
60
|
+
|
|
61
|
+
address: str
|
|
62
|
+
client: str
|
|
63
|
+
progress: float
|
|
64
|
+
download_rate: int
|
|
65
|
+
upload_rate: int
|
|
66
|
+
is_incoming: bool
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
70
|
+
class Tracker:
|
|
71
|
+
"""A tracker entry."""
|
|
72
|
+
|
|
73
|
+
url: str
|
|
74
|
+
tier: int
|
|
75
|
+
message: str
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
79
|
+
class TorrentInfo:
|
|
80
|
+
"""Basic torrent metadata from torrent.get."""
|
|
81
|
+
|
|
82
|
+
name: str
|
|
83
|
+
tags: list[str]
|
|
84
|
+
custom: dict[str, str]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ── Request types ─────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
91
|
+
class AddTorrentRequest:
|
|
92
|
+
"""Parameters for torrent.add."""
|
|
93
|
+
|
|
94
|
+
torrent_file: bytes
|
|
95
|
+
download_dir: str | None = None
|
|
96
|
+
tags: list[str] | None = None
|
|
97
|
+
custom: dict[str, str] | None = None
|
|
98
|
+
selected_files: list[int] | None = None
|
|
99
|
+
is_base_dir: bool = False
|
|
100
|
+
skip_hash_check: bool = False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
104
|
+
class InfoHashRequest:
|
|
105
|
+
"""Common request that only needs an info_hash."""
|
|
106
|
+
|
|
107
|
+
info_hash: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
111
|
+
class MoveTorrentRequest:
|
|
112
|
+
"""Parameters for torrent.move."""
|
|
113
|
+
|
|
114
|
+
info_hash: str
|
|
115
|
+
target_base_path: str
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
119
|
+
class RemoveTorrentRequest:
|
|
120
|
+
"""Parameters for torrent.remove."""
|
|
121
|
+
|
|
122
|
+
info_hash: str
|
|
123
|
+
delete_data: bool = False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
127
|
+
class TagsRequest:
|
|
128
|
+
"""Parameters for torrent.add_tags / torrent.remove_tags."""
|
|
129
|
+
|
|
130
|
+
info_hash: str
|
|
131
|
+
tags: list[str]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
135
|
+
class SetFilePriorityRequest:
|
|
136
|
+
"""Parameters for torrent.set_file_priority."""
|
|
137
|
+
|
|
138
|
+
info_hash: str
|
|
139
|
+
file_ids: list[int]
|
|
140
|
+
priority: int = 0
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
144
|
+
class SetSpeedLimitRequest:
|
|
145
|
+
"""Parameters for torrent.set_download_limit / torrent.set_upload_limit."""
|
|
146
|
+
|
|
147
|
+
info_hash: str
|
|
148
|
+
limit: int = 0
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
152
|
+
class SetGlobalSpeedLimitRequest:
|
|
153
|
+
"""Parameters for client.set_download_limit / client.set_upload_limit."""
|
|
154
|
+
|
|
155
|
+
limit: int = 0
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
159
|
+
class ListTorrentRequest:
|
|
160
|
+
"""Parameters for torrent.list."""
|
|
161
|
+
|
|
162
|
+
keys: list[str] | None = None
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
166
|
+
class SetCustomRequest:
|
|
167
|
+
"""Parameters for torrent.custom.set."""
|
|
168
|
+
|
|
169
|
+
info_hash: str
|
|
170
|
+
key: str
|
|
171
|
+
value: str
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
175
|
+
class UpdateCustomRequest:
|
|
176
|
+
"""Parameters for torrent.custom.update."""
|
|
177
|
+
|
|
178
|
+
info_hash: str
|
|
179
|
+
custom: dict[str, str]
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
183
|
+
class DelCustomRequest:
|
|
184
|
+
"""Parameters for torrent.custom.del."""
|
|
185
|
+
|
|
186
|
+
info_hash: str
|
|
187
|
+
key: str
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
191
|
+
class AddTrackerRequest:
|
|
192
|
+
"""Parameters for torrent.add_tracker."""
|
|
193
|
+
|
|
194
|
+
info_hash: str
|
|
195
|
+
url: str
|
|
196
|
+
tier: int = 0
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
200
|
+
class RemoveTrackerRequest:
|
|
201
|
+
"""Parameters for torrent.remove_tracker."""
|
|
202
|
+
|
|
203
|
+
info_hash: str
|
|
204
|
+
url: str
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
208
|
+
class ReplaceTrackersRequest:
|
|
209
|
+
"""Parameters for torrent.replace_trackers."""
|
|
210
|
+
|
|
211
|
+
info_hash: str
|
|
212
|
+
replacements: dict[str, str]
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ── Response types ────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
219
|
+
class TorrentListResponse:
|
|
220
|
+
"""Response for torrent.list and torrent.remove."""
|
|
221
|
+
|
|
222
|
+
torrents: list[MainDataTorrent]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
226
|
+
class AddTorrentResponse:
|
|
227
|
+
"""Response for torrent.add."""
|
|
228
|
+
|
|
229
|
+
info_hash: str
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
233
|
+
class TorrentFilesResponse:
|
|
234
|
+
"""Response for torrent.files."""
|
|
235
|
+
|
|
236
|
+
files: list[TorrentFile]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
240
|
+
class TorrentPeersResponse:
|
|
241
|
+
"""Response for torrent.peers."""
|
|
242
|
+
|
|
243
|
+
peers: list[Peer]
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
247
|
+
class TorrentTrackersResponse:
|
|
248
|
+
"""Response for torrent.trackers."""
|
|
249
|
+
|
|
250
|
+
trackers: list[Tracker]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
neptune_sdk-0.0.1.dev580.dist-info/METADATA,sha256=Z5S1QlqDPFkAurZ1W6iMx99bUHoz8Nh5SxvXppaqZgg,237
|
|
2
|
+
neptune_sdk-0.0.1.dev580.dist-info/WHEEL,sha256=VP-D4TPS230sME9Z3vb3INXvo1yt0924YRm5AOsk_dE,90
|
|
3
|
+
neptune_sdk-0.0.1.dev580.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
4
|
+
neptune_sdk/__init__.py,sha256=LJ3Ll8OXefHysJ6PZM4s8mjhQFt1hCO-S7BTwa522g4,1655
|
|
5
|
+
neptune_sdk/client.py,sha256=5P6eFWU4q9oLihmb_oAmrGnVJ_u32r7WxdroFVc-rIw,11279
|
|
6
|
+
neptune_sdk/exceptions.py,sha256=7nPIjKEzsODbXrxyeD3d4jkbTiIJOEVa2KEwzCrEBUE,504
|
|
7
|
+
neptune_sdk/models.py,sha256=W7RdwGIQ5uiW-iEpJ0WxlXWHEdjNggU4fFhY3be_R7Q,5706
|
|
8
|
+
neptune_sdk-0.0.1.dev580.dist-info/RECORD,,
|