from __future__ import annotations import os from collections.abc import Iterable, Mapping, Sequence from json import dumps from logging import Logger, getLogger from typing import Any, AnyStr, Union from qbittorrentapi.auth import AuthAPIMixIn from qbittorrentapi.definitions import ( APIKwargsT, APINames, ClientCache, Dictionary, JsonValueT, List, ListEntry, ListInputT, ) logger: Logger = getLogger(__name__) class ApplicationPreferencesDictionary(Dictionary[JsonValueT]): """ Response for :meth:`~AppAPIMixIn.app_preferences` Definition: ``_ """ # noqa: E501 class BuildInfoDictionary(Dictionary[Union[str, int]]): """ Response for :meth:`~AppAPIMixIn.app_build_info` Definition: ``_ """ # noqa: E501 class Cookie(ListEntry): """Item in :class:`CookieList`""" class CookieList(List[Cookie]): """Response for :meth:`~AppAPIMixIn.app_cookies`""" def __init__(self, list_entries: ListInputT, client: AppAPIMixIn | None = None): super().__init__(list_entries, entry_class=Cookie) class NetworkInterface(ListEntry): """Item in :class:`NetworkInterfaceList`""" class NetworkInterfaceList(List[NetworkInterface]): """Response for :meth:`~AppAPIMixIn.app_network_interface_list`""" def __init__(self, list_entries: ListInputT, client: AppAPIMixIn | None = None): super().__init__(list_entries, entry_class=NetworkInterface) # only API response that's a list of strings...so just ignore the typing for now class NetworkInterfaceAddressList(List[str]): # type: ignore """Response for :meth:`~AppAPIMixIn.app_network_interface_address_list`""" def __init__(self, list_entries: Iterable[str], client: AppAPIMixIn | None = None): super().__init__(list_entries) # type: ignore class DirectoryContentList(List[Union[str, ListEntry]]): # type: ignore[type-var] """Response for :meth:`~AppAPIMixIn.app_get_directory_content`""" def __init__(self, list_entries: Iterable[str], client: AppAPIMixIn | None = None): super().__init__(list_entries) # type: ignore class AppAPIMixIn(AuthAPIMixIn): """ Implementation of all ``Application`` API methods. :Usage: >>> from qbittorrentapi import Client >>> client = Client(host="localhost:8080", username="admin", password="adminadmin") >>> client.app_version() >>> client.app_preferences() """ # noqa: E501 @property def app(self) -> Application: """ Allows for transparent interaction with Application endpoints. See Application class for usage. """ if self._application is None: self._application = Application(client=self) return self._application @property def application(self) -> Application: return self.app def app_version(self, **kwargs: APIKwargsT) -> str: """qBittorrent application version.""" return self._get_cast( _name=APINames.Application, _method="version", response_class=str, **kwargs, ) def app_web_api_version(self, **kwargs: APIKwargsT) -> str: """qBittorrent Web API version.""" return self._get_cast( _name=APINames.Application, _method="webapiVersion", response_class=str, **kwargs, ) app_webapiVersion = app_web_api_version def app_build_info(self, **kwargs: APIKwargsT) -> BuildInfoDictionary: """ qBittorrent build info. This method was introduced with qBittorrent v4.2.0 (Web API v2.3). """ return self._get_cast( _name=APINames.Application, _method="buildInfo", response_class=BuildInfoDictionary, version_introduced="2.3", **kwargs, ) app_buildInfo = app_build_info def app_shutdown(self, **kwargs: APIKwargsT) -> None: """Shutdown qBittorrent.""" self._post(_name=APINames.Application, _method="shutdown", **kwargs) def app_preferences(self, **kwargs: APIKwargsT) -> ApplicationPreferencesDictionary: """Retrieve qBittorrent application preferences.""" return self._get_cast( _name=APINames.Application, _method="preferences", response_class=ApplicationPreferencesDictionary, **kwargs, ) def app_set_preferences( self, prefs: ApplicationPreferencesDictionary | Mapping[str, Any] | None = None, **kwargs: APIKwargsT, ) -> None: """ Set one or more preferences in qBittorrent application. :param prefs: dictionary of preferences to set """ data = {"json": dumps(prefs, separators=(",", ":"))} self._post( _name=APINames.Application, _method="setPreferences", data=data, **kwargs, ) app_setPreferences = app_set_preferences def app_default_save_path(self, **kwargs: APIKwargsT) -> str: """The default path where torrents are saved.""" return self._get_cast( _name=APINames.Application, _method="defaultSavePath", response_class=str, **kwargs, ) app_defaultSavePath = app_default_save_path def app_cookies(self, **kwargs: APIKwargsT) -> CookieList: """Retrieve current cookies.""" return self._get_cast( _name=APINames.Application, _method="cookies", response_class=CookieList, version_introduced="2.11.3", **kwargs, ) def app_set_cookies( self, cookies: CookieList | Sequence[Mapping[str, str | int]] | None = None, **kwargs: APIKwargsT, ) -> None: """ Set cookies. .. highlight:: python .. code-block:: python cookies = [ { 'domain': 'example.com', 'path': '/example/path', 'name': 'cookie name', 'value': 'cookie value', 'expirationDate': 1729366667, }, ] :param cookies: list of cookies to set """ data = { # coerce to types that are known to be JSON serializable "cookies": dumps(list(map(dict, cookies or ())), separators=(",", ":")), } self._post( _name=APINames.Application, _method="setCookies", data=data, version_introduced="2.11.3", **kwargs, ) app_setCookies = app_set_cookies def app_network_interface_list(self, **kwargs: APIKwargsT) -> NetworkInterfaceList: """ The list of network interfaces on the host. This method was introduced with qBittorrent v4.2.0 (Web API v2.3). """ return self._get_cast( _name=APINames.Application, _method="networkInterfaceList", response_class=NetworkInterfaceList, version_introduced="2.3", **kwargs, ) app_networkInterfaceList = app_network_interface_list def app_network_interface_address_list( self, interface_name: str = "", **kwargs: APIKwargsT, ) -> NetworkInterfaceAddressList: """ The addresses for a network interface; omit name for all addresses. This method was introduced with qBittorrent v4.2.0 (Web API v2.3). :param interface_name: Name of interface to retrieve addresses for """ data = {"iface": interface_name} return self._post_cast( _name=APINames.Application, _method="networkInterfaceAddressList", data=data, response_class=NetworkInterfaceAddressList, version_introduced="2.3", **kwargs, ) app_networkInterfaceAddressList = app_network_interface_address_list def app_send_test_email(self) -> None: """Sends a test email using the configured email address.""" self._post( _name=APINames.Application, _method="sendTestEmail", version_introduced="2.10.4", ) app_sendTestEmail = app_send_test_email def app_get_directory_content( self, directory_path: str | os.PathLike[AnyStr] | None = None, with_metadata: bool | None = None, ) -> DirectoryContentList: """ The contents of a directory file path. :raises NotFound404Error: file path not found or not a directory :param directory_path: file system path to directory :param with_metadata: include file/directory metadata """ data = { "dirPath": ( os.fsdecode(directory_path) if directory_path is not None else None ), "withMetadata": with_metadata, } return self._post_cast( _name=APINames.Application, _method="getDirectoryContent", data=data, response_class=DirectoryContentList, version_introduced="2.11", ) app_getDirectoryContent = app_get_directory_content class Application(ClientCache[AppAPIMixIn]): """ Allows interaction with ``Application`` API endpoints. :Usage: >>> from qbittorrentapi import Client >>> client = Client(host="localhost:8080", username="admin", password="adminadmin") >>> # these are all the same attributes that are available as named in the >>> # endpoints or the more pythonic names in Client (with or without 'app_' prepended) >>> webapiVersion = client.application.webapiVersion >>> web_api_version = client.application.web_api_version >>> app_web_api_version = client.application.web_api_version >>> # access and set preferences as attributes >>> is_dht_enabled = client.application.preferences.dht >>> # supports sending a just subset of preferences to update >>> client.application.preferences = dict(dht=(not is_dht_enabled)) >>> prefs = client.application.preferences >>> prefs["web_ui_clickjacking_protection_enabled"] = True >>> client.app.preferences = prefs >>> >>> client.application.shutdown() """ # noqa: E501 @property def version(self) -> str: """Implements :meth:`~AppAPIMixIn.app_version`.""" return self._client.app_version() @property def web_api_version(self) -> str: """Implements :meth:`~AppAPIMixIn.app_web_api_version`.""" return self._client.app_web_api_version() webapiVersion = web_api_version @property def build_info(self) -> BuildInfoDictionary: """Implements :meth:`~AppAPIMixIn.app_build_info`.""" return self._client.app_build_info() buildInfo = build_info def shutdown(self, **kwargs: APIKwargsT) -> None: """Implements :meth:`~AppAPIMixIn.app_shutdown`.""" self._client.app_shutdown(**kwargs) @property def preferences(self) -> ApplicationPreferencesDictionary: """Implements :meth:`~AppAPIMixIn.app_preferences`.""" return self._client.app_preferences() @preferences.setter def preferences(self, value: Mapping[str, Any]) -> None: """Implements :meth:`~AppAPIMixIn.app_set_preferences`.""" self.set_preferences(prefs=value) def set_preferences( self, prefs: Mapping[str, Any] | None = None, **kwargs: APIKwargsT, ) -> None: """Implements :meth:`~AppAPIMixIn.app_set_preferences`.""" self._client.app_set_preferences(prefs=prefs, **kwargs) setPreferences = set_preferences @property def default_save_path(self) -> str: """Implements :meth:`~AppAPIMixIn.app_default_save_path`.""" return self._client.app_default_save_path() defaultSavePath = default_save_path @property def cookies(self) -> CookieList: """Implements :meth:`~AppAPIMixIn.app_cookies`.""" return self._client.app_cookies() def set_cookies( self, cookies: CookieList | Sequence[Mapping[str, str | int]] | None = None, **kwargs: APIKwargsT, ) -> None: """Implements :meth:`~AppAPIMixIn.app_set_cookies`.""" self._client.app_set_cookies(cookies=cookies, **kwargs) setCookies = set_cookies @property def network_interface_list(self) -> NetworkInterfaceList: """Implements :meth:`~AppAPIMixIn.app_network_interface_list`.""" return self._client.app_network_interface_list() networkInterfaceList = network_interface_list def network_interface_address_list( self, interface_name: str = "", **kwargs: APIKwargsT, ) -> NetworkInterfaceAddressList: """Implements :meth:`~AppAPIMixIn.app_network_interface_address_list`.""" return self._client.app_network_interface_address_list( interface_name=interface_name, **kwargs, ) networkInterfaceAddressList = network_interface_address_list def send_test_email(self) -> None: """Implements :meth:`~AppAPIMixIn.app_send_test_email`.""" self._client.app_send_test_email() sendTestEmail = send_test_email def get_directory_content( self, directory_path: str | os.PathLike[AnyStr] | None = None, with_metadata: bool | None = None, ) -> DirectoryContentList: """Implements :meth:`~AppAPIMixIn.app_get_directory_content`.""" return self._client.app_get_directory_content( directory_path=directory_path, with_metadata=with_metadata, ) getDirectoryContent = get_directory_content