diff --git a/libs/qbittorrent/__init__.py b/libs/qbittorrent/__init__.py new file mode 100644 index 00000000..5e3048b2 --- /dev/null +++ b/libs/qbittorrent/__init__.py @@ -0,0 +1 @@ +__version__ = '0.1' \ No newline at end of file diff --git a/libs/qbittorrent/base.py b/libs/qbittorrent/base.py new file mode 100644 index 00000000..328e008a --- /dev/null +++ b/libs/qbittorrent/base.py @@ -0,0 +1,62 @@ +from urlparse import urljoin +import logging + +log = logging.getLogger(__name__) + + +class Base(object): + properties = {} + + def __init__(self, url, session, client=None): + self._client = client + self._url = url + self._session = session + + @staticmethod + def _convert(response, response_type): + if response_type == 'json': + try: + return response.json() + except ValueError: + pass + + return response + + def _get(self, path='', response_type='json', **kwargs): + r = self._session.get(urljoin(self._url, path), **kwargs) + return self._convert(r, response_type) + + def _post(self, path='', response_type='json', data=None, **kwargs): + r = self._session.post(urljoin(self._url, path), data, **kwargs) + return self._convert(r, response_type) + + def _fill(self, data): + for key, value in data.items(): + if self.set_property(self, key, value): + continue + + log.debug('%s is missing item with key "%s" and value %s', self.__class__, key, repr(value)) + + @classmethod + def parse(cls, client, data): + obj = cls(client._url, client._session, client) + obj._fill(data) + + return obj + + @classmethod + def set_property(cls, obj, key, value): + prop = cls.properties.get(key, {}) + + if prop.get('key'): + key = prop['key'] + + if not hasattr(obj, key): + return False + + + if prop.get('parse'): + value = prop['parse'](value) + + setattr(obj, key, value) + return True diff --git a/libs/qbittorrent/client.py b/libs/qbittorrent/client.py new file mode 100644 index 00000000..0365a1a8 --- /dev/null +++ b/libs/qbittorrent/client.py @@ -0,0 +1,33 @@ +from qbittorrent.base import Base +from qbittorrent.torrent import Torrent +from requests import Session +from requests.auth import HTTPDigestAuth + + +class QBittorrentClient(Base): + def __init__(self, url, username=None, password=None): + super(QBittorrentClient, self).__init__(url, Session()) + + if username and password: + self._session.auth = HTTPDigestAuth(username, password) + + def test_connection(self): + r = self._get(response_type='response') + + return r.status_code == 200 + + def add_file(self, file): + self._post('command/upload', files={'torrent': file}) + + def add_url(self, urls): + if type(urls) is not list: + urls = [urls] + + urls = '%0A'.join(urls) + + self._post('command/download', data={'urls': urls}) + + def get_torrents(self): + r = self._get('json/torrents') + + return [Torrent.parse(self, x) for x in r] diff --git a/libs/qbittorrent/file.py b/libs/qbittorrent/file.py new file mode 100644 index 00000000..0c020572 --- /dev/null +++ b/libs/qbittorrent/file.py @@ -0,0 +1,13 @@ +from qbittorrent.base import Base + + +class File(Base): + def __init__(self, url, session, client=None): + super(File, self).__init__(url, session, client) + + self.name = None + + self.progress = None + self.priority = None + + self.is_seed = None diff --git a/libs/qbittorrent/helpers.py b/libs/qbittorrent/helpers.py new file mode 100644 index 00000000..253f03e8 --- /dev/null +++ b/libs/qbittorrent/helpers.py @@ -0,0 +1,7 @@ +def try_convert(value, to_type, default=None): + try: + return to_type(value) + except ValueError: + return default + except TypeError: + return default diff --git a/libs/qbittorrent/torrent.py b/libs/qbittorrent/torrent.py new file mode 100644 index 00000000..f7efdb5c --- /dev/null +++ b/libs/qbittorrent/torrent.py @@ -0,0 +1,64 @@ +from qbittorrent.base import Base +from qbittorrent.file import File +from qbittorrent.helpers import try_convert + + +class Torrent(Base): + properties = { + 'num_seeds': { + 'key': 'seeds', + 'parse': lambda value: try_convert(value, int) + }, + 'num_leechs': { + 'key': 'leechs', + 'parse': lambda value: try_convert(value, int) + }, + 'ratio': { + 'parse': lambda value: try_convert(value, float) + } + } + + def __init__(self, url, session, client=None): + super(Torrent, self).__init__(url, session, client) + + self.hash = None + self.name = None + + self.state = None + self.ratio = None + self.progress = None + self.priority = None + + self.seeds = None + self.leechs = None + + # + # Commands + # + + def pause(self): + self._post('command/pause', data={'hash': self.hash}) + + def resume(self): + self._post('command/resume', data={'hash': self.hash}) + + def remove(self): + self._post('command/delete', data={'hashes': self.hash}) + + def delete(self): + self._post('command/deletePerm', data={'hashes': self.hash}) + + def recheck(self): + self._post('command/recheck', data={'hash': self.hash}) + + # + # Fetch details + # + + def get_files(self): + r = self._get('json/propertiesFiles/%s' % self.hash) + + return [File.parse(self._client, x) for x in r] + + def get_trackers(self): + pass