diff --git a/README.md b/README.md index a45189ef..434b4916 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ `transmission-rpc` is a python wrapper on top of [transmission](https://github.com/transmission/transmission) JSON RPC protocol, hosted on GitHub at [github.com/trim21/transmission-rpc](https://github.com/trim21/transmission-rpc) -Support 2.40 (released 2011-10-05) <= transmission version <= 4.0.6 (released 2024-05-29), +Support 2.40 (released 2011-10-05) <= transmission version <= 4.1.0-beta.1 (released 2024-12-13), should works fine with newer rpc version but some new feature may be missing. ## versioning diff --git a/docs/client.rst b/docs/client.rst index a871b62c..e9294e00 100644 --- a/docs/client.rst +++ b/docs/client.rst @@ -4,7 +4,7 @@ Client Client is the class handling the Transmission JSON-RPC client protocol. Torrent ids ------------- +----------- Many functions in Client takes torrent id. You can find torrent-ids spec in `official docs @@ -14,6 +14,8 @@ You can find torrent-ids spec in `official docs It's recommended that you use torrent's ``info_hash`` as torrent id. The torrent's ``info_hash`` will never change. +Client +------ .. automodule:: transmission_rpc @@ -22,6 +24,11 @@ You can find torrent-ids spec in `official docs .. autoclass:: Client :members: +.. autoclass:: PortTestResult + :members: + :undoc-members: + :inherited-members: + :exclude-members: __init__, __new__ Timeouts -------- diff --git a/transmission_rpc/__init__.py b/transmission_rpc/__init__.py index 28acf244..f0a47184 100644 --- a/transmission_rpc/__init__.py +++ b/transmission_rpc/__init__.py @@ -11,7 +11,7 @@ ) from transmission_rpc.session import Session, SessionStats, Stats from transmission_rpc.torrent import FileStat, Status, Torrent, Tracker, TrackerStats -from transmission_rpc.types import File, Group +from transmission_rpc.types import File, Group, PortTestResult __all__ = [ "DEFAULT_TIMEOUT", @@ -21,6 +21,7 @@ "FileStat", "Group", "IdleMode", + "PortTestResult", "Priority", "RatioLimitMode", "Session", diff --git a/transmission_rpc/client.py b/transmission_rpc/client.py index 37b09a6f..6f687c2c 100644 --- a/transmission_rpc/client.py +++ b/transmission_rpc/client.py @@ -25,7 +25,7 @@ ) from transmission_rpc.session import Session, SessionStats from transmission_rpc.torrent import Torrent -from transmission_rpc.types import Group +from transmission_rpc.types import Group, PortTestResult from transmission_rpc.utils import _try_read_torrent, get_torrent_arguments try: @@ -657,10 +657,11 @@ def change_torrent( seed_idle_mode: int | None = None, seed_ratio_limit: float | None = None, seed_ratio_mode: int | None = None, - tracker_add: Iterable[str] | None = None, labels: Iterable[str] | None = None, group: str | None = None, tracker_list: Iterable[Iterable[str]] | None = None, + sequential_download: bool | None = None, + tracker_add: Iterable[str] | None = None, tracker_replace: Iterable[tuple[int, str]] | None = None, tracker_remove: Iterable[int] | None = None, **kwargs: Any, @@ -692,35 +693,32 @@ def change_torrent( Valid options are :py:class:`transmission_rpc.IdleMode` labels: Array of string labels. Add in rpc 16. group: The name of this torrent's bandwidth group. Add in rpc 17. - tracker_list: - A ``Iterable[Iterable[str]]``, each ``Iterable[str]`` for a tracker tier. + + tracker_list: A ``Iterable[Iterable[str]]``, each ``Iterable[str]`` for a tracker tier. Add in rpc 17. Example: ``[['https://tracker1/announce', 'https://tracker2/announce'], ['https://backup1.example.com/announce'], ['https://backup2.example.com/announce']]``. - tracker_add: - Array of string with announce URLs to add. - - Warnings: - since transmission daemon 4.0.0, this argument is deprecated, use ``tracker_list`` instead. - - tracker_remove: - Array of ids of trackers to remove. + sequential_download: download torrent pieces sequentially. Add in Transmission 4.1.0, rpc-version 18. - Warnings: - since transmission daemon 4.0.0, this argument is deprecated, use ``tracker_list`` instead. + tracker_add: Array of string with announce URLs to add. + **Deprecated** since transmission daemon 4.0.0, this argument is deprecated, + use ``tracker_list`` instead. - tracker_replace: - Array of (id, url) tuples where the announcement URL should be replaced. + tracker_remove: Array of ids of trackers to remove. + **Deprecated** since transmission daemon 4.0.0, this argument is deprecated, + use ``tracker_list`` instead. - Warning: - since transmission daemon 4.0.0, this argument is deprecated, use ``tracker_list`` instead. + tracker_replace: Array of (id, url) tuples where the announcement URL should be replaced. + **Deprecated** since transmission daemon 4.0.0, this argument is deprecated, + use ``tracker_list`` instead. Warnings: ``kwargs`` is for the future features not supported yet, it's not compatibility promising. - It will be bypassed to request arguments **as-is**, the underline in the key will not be replaced, so you should use kwargs like ``{'a-argument': 'value'}`` + It will be bypassed to request arguments **as-is**, + the underline in the key will not be replaced, so you should use kwargs like ``{'a-argument': 'value'}`` """ if labels is not None: self._rpc_version_warning(16) @@ -757,6 +755,7 @@ def change_torrent( "labels": list_or_none(_single_str_as_list(labels)), "trackerList": None if tracker_list is None else "\n\n".join("\n".join(tier) for tier in tracker_list), "group": group, + "sequentialDownload": sequential_download, } ) @@ -779,7 +778,8 @@ def move_torrent_data( Move torrent data to the new location. See Also: - `RPC Spec: moving-a-torrent `_ + `RPC Spec: moving-a-torrent + `_ """ args = {"location": ensure_location_str(location), "move": bool(move)} self._request(RpcMethod.TorrentSetLocation, args, ids, True, timeout=timeout) @@ -799,7 +799,8 @@ def rename_torrent_path( This is not the method to move torrent data directory, See Also: - `RPC Spec: renaming-a-torrents-path `_ + `RPC Spec: renaming-a-torrents-path + `_ """ self._rpc_version_warning(15) torrent_id = _parse_torrent_id(torrent_id) @@ -1094,13 +1095,23 @@ def blocklist_update(self, timeout: _Timeout | None = None) -> int | None: result = self._request(RpcMethod.BlocklistUpdate, timeout=timeout) return result.get("blocklist-size") - def port_test(self, timeout: _Timeout | None = None) -> bool | None: + def port_test( + self, timeout: _Timeout | None = None, *, ip_protocol: Literal["ipv4", "ipv6"] | None = None + ) -> PortTestResult: """ Tests to see if your incoming peer port is accessible from the outside world. + + https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#44-port-checking + + Parameters: + ip_protocol: ``ipv4`` or ``ipv6``. + Available in Transmission 4.1.0 (rpc-version-semver 5.4.0, rpc-version: 18) + timeout: request timeout """ - result = self._request(RpcMethod.PortTest, timeout=timeout) - return result.get("port-is-open") + return PortTestResult( + fields=self._request(RpcMethod.PortTest, remove_unset_value({"ipProtocol": ip_protocol}), timeout=timeout) + ) def free_space(self, path: str | pathlib.Path, timeout: _Timeout | None = None) -> int | None: """ diff --git a/transmission_rpc/constants.py b/transmission_rpc/constants.py index cc9b95a9..17673727 100644 --- a/transmission_rpc/constants.py +++ b/transmission_rpc/constants.py @@ -147,6 +147,7 @@ class Type: "seedIdleMode": Args(Type.number, 10, description="Use global (0), torrent (1), or unlimited (2) limit."), "seedRatioLimit": Args(Type.double, 5, None, None, None, "Seed ratio limit."), "seedRatioMode": Args(Type.number, 5, description="Use global (0), torrent (1), or unlimited (2) limit."), + "sequentialDownload": Args(Type.boolean, 18, description="download torrent pieces sequentially"), "sizeWhenDone": Args(Type.number, 1, description="Size of the torrent download in bytes."), "startDate": Args(Type.number, 1, description="The date when the torrent was last started."), "status": Args(Type.number, 1, None, None, None, "Current status, see source"), diff --git a/transmission_rpc/torrent.py b/transmission_rpc/torrent.py index 204b6c46..2fe6c420 100644 --- a/transmission_rpc/torrent.py +++ b/transmission_rpc/torrent.py @@ -868,6 +868,15 @@ def seed_ratio_mode(self) -> RatioLimitMode: """ return RatioLimitMode(self.fields["seedRatioMode"]) + @property + def sequential_download(self) -> bool: + """ + download torrent pieces sequentially + + add in Transmission 4.1.0 (rpc-version-semver 5.4.0, rpc-version: 18) + """ + return self.fields["sequentialDownload"] + def __repr__(self) -> str: return f"" diff --git a/transmission_rpc/types.py b/transmission_rpc/types.py index 54e3db21..baf4ac19 100644 --- a/transmission_rpc/types.py +++ b/transmission_rpc/types.py @@ -21,6 +21,7 @@ def get(self, key: str, default: T | None = None) -> Any: class File(NamedTuple): name: str """file name""" + size: int """file size in bytes""" @@ -36,6 +37,12 @@ class File(NamedTuple): id: int """id of the file of this torrent, not should not be used outside the torrent scope""" + begin_piece: int | None = None + """add in Transmission 4.1.0 rpc-version-semver 5.4.0, rpc-version 18""" + + end_piece: int | None = None + """add in Transmission 4.1.0 rpc-version-semver 5.4.0, rpc-version 18""" + class Group(Container): """ @@ -73,6 +80,29 @@ def speed_limit_up(self) -> int: return self.fields["speed-limit-up"] +class PortTestResult(Container): + """ + api response of :meth:`transmission_rpc.Client.port_test` + + https://github.com/transmission/transmission/blob/5d159e0/docs/rpc-spec.md#44-port-checking + """ + + @property + def port_is_open(self) -> bool: + """available on all transmission version""" + return self.fields["port-is-open"] + + @property + def ip_protocol(self) -> str: + """``ipv4`` if the test was carried out on IPv4, + ``ipv6`` if the test was carried out on IPv6, + unset if it cannot be determined + + Available in Transmission 4.1.0 (rpc-version-semver 5.4.0, rpc-version: 18) + """ + return self.fields["ipProtocol"] + + class BitMap: __value: bytes __slots__ = ("__value",)