diff --git a/monitorrent/plugin_managers.py b/monitorrent/plugin_managers.py
index 4ef2a6ec..7909348e 100644
--- a/monitorrent/plugin_managers.py
+++ b/monitorrent/plugin_managers.py
@@ -212,6 +212,20 @@ def find_torrent(self, torrent_hash):
result = self.default_client.find_torrent(torrent_hash)
return result or False
+ def get_download_status(self, client_name):
+ client = self.get_client(client_name)
+ result = client.get_download_status()
+ return result or False
+
+ def get_download_status_by_id(self, torrent_id):
+ """
+
+ :type torrent_id: str
+ """
+ if self.default_client is None:
+ return False
+ return self.default_client.get_download_status_by_hash(torrent_id)
+
def add_torrent(self, torrent, topic_settings):
"""
:type torrent: str
diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py
index 1460e513..8131417b 100644
--- a/monitorrent/plugins/clients/__init__.py
+++ b/monitorrent/plugins/clients/__init__.py
@@ -1,6 +1,43 @@
+from enum import Enum
+
from monitorrent.plugins.trackers import Topic
+class TorrentDownloadStatus(Enum):
+ Queued = 1,
+ Preparing = 2,
+ Checking = 3,
+ Downloading = 5,
+ Verifying = 6,
+ Finished = 7,
+ Seeding = 8,
+ Paused = 9,
+ Error = 10,
+ Unknown = 20
+
+
+class DownloadStatus(dict):
+ def __init__(self, downloaded_bytes, total_bytes, download_speed, upload_speed, torrent_status, progress, ratio):
+ """
+ :type downloaded_bytes: int
+ :type total_bytes: int
+ :type download_speed: int
+ :type upload_speed: int
+ :type torrent_status: TorrentDownloadStatus
+ :type progress: float
+ :type ratio: float
+ """
+
+ super(DownloadStatus, self).__init__()
+ self.downloaded_bytes = downloaded_bytes
+ self.total_bytes = total_bytes
+ self.download_speed = download_speed
+ self.upload_speed = upload_speed
+ self.torrent_status = torrent_status
+ self.progress = progress
+ self.ratio = ratio
+
+
class TopicSettings(object):
download_dir = None
diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py
index a2224fac..f59697c8 100644
--- a/monitorrent/plugins/clients/deluge.py
+++ b/monitorrent/plugins/clients/deluge.py
@@ -1,4 +1,3 @@
-import six
import base64
import structlog
@@ -9,6 +8,8 @@
from monitorrent.plugin_managers import register_plugin
from datetime import datetime
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
+
log = structlog.get_logger()
@@ -22,6 +23,23 @@ class DelugeCredentials(Base):
password = Column(String, nullable=True)
+status_mapping = {
+ "Queued": TorrentDownloadStatus.Queued,
+ "Downloading": TorrentDownloadStatus.Downloading,
+ "Seeding": TorrentDownloadStatus.Seeding,
+ "Paused": TorrentDownloadStatus.Paused,
+ "Checking": TorrentDownloadStatus.Checking,
+ "Error": TorrentDownloadStatus.Error,
+}
+
+
+def get_status(status, is_paused):
+ if is_paused:
+ return TorrentDownloadStatus.Paused
+ else:
+ return status_mapping[status]
+
+
class DelugeClientPlugin(object):
name = "deluge"
form = [{
@@ -139,5 +157,37 @@ def remove_torrent(self, torrent_hash):
return client.call("core.remove_torrent",
torrent_hash.lower(), False)
+ def get_download_status(self):
+ client = self._get_client()
+ client.connect()
+ result = client.call("core.get_torrents_status",
+ {}, [])
+ statuses = {}
+ for key, value in result.items():
+ statuses[key.decode("utf-8")] = DownloadStatus(value[b'total_done'], value[b'total_size'],
+ value[b'download_payload_rate'],
+ value[b'upload_payload_rate'],
+ get_status(value[b'state'].decode("utf-8"),
+ value[b'paused']),
+ value[b'progress'], value[b'ratio'])
+ return statuses
+
+ def get_download_status_by_hash(self, torrent_hash):
+ client = self._get_client()
+ lower_hash = torrent_hash.lower()
+ if not client:
+ return False
+ client.connect()
+ result = client.call("core.get_torrents_status",
+ {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate',
+ 'upload_payload_rate', 'state', 'progress'])
+ value = result.popitem()[1]
+ return DownloadStatus(value[b'total_done'], value[b'total_size'],
+ value[b'download_payload_rate'],
+ value[b'upload_payload_rate'],
+ get_status(value[b'state'].decode("utf-8"),
+ value[b'paused']),
+ value[b'progress'], value[b'ratio'])
+
register_plugin('client', 'deluge', DelugeClientPlugin())
diff --git a/monitorrent/plugins/clients/downloader.py b/monitorrent/plugins/clients/downloader.py
index a0483dbf..c2ce7fc3 100644
--- a/monitorrent/plugins/clients/downloader.py
+++ b/monitorrent/plugins/clients/downloader.py
@@ -5,6 +5,7 @@
from sqlalchemy import Column, Integer, String
from monitorrent.db import Base, DBSession
from monitorrent.plugin_managers import register_plugin
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
from monitorrent.utils.bittorrent_ex import Torrent
import base64
@@ -108,4 +109,10 @@ def remove_torrent(self, torrent_hash):
except OSError:
return False
+ def get_download_status(self):
+ return {"": DownloadStatus(0, 0, 0, 0, TorrentDownloadStatus.Unknown, 0, 0)}
+
+ def get_download_status_by_hash(self, torrent_hash):
+ return DownloadStatus(0, 0, 0, 0, TorrentDownloadStatus.Unknown, 0, 0)
+
register_plugin('client', DownloaderPlugin.name, DownloaderPlugin())
diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py
index e94aed2b..6a006ea8 100644
--- a/monitorrent/plugins/clients/qbittorrent.py
+++ b/monitorrent/plugins/clients/qbittorrent.py
@@ -17,6 +17,27 @@
from datetime import datetime
import dateutil.parser
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
+
+status_mapping = {
+ "queuedUP": TorrentDownloadStatus.Queued,
+ "queuedDL": TorrentDownloadStatus.Queued,
+ "stalledDL": TorrentDownloadStatus.Downloading,
+ "metaDL": TorrentDownloadStatus.Downloading,
+ "downloading": TorrentDownloadStatus.Downloading,
+ "stalledUP": TorrentDownloadStatus.Seeding,
+ "uploading": TorrentDownloadStatus.Seeding,
+ "pausedDL": TorrentDownloadStatus.Paused,
+ "pausedUP": TorrentDownloadStatus.Paused,
+ "checkingUP": TorrentDownloadStatus.Checking,
+ "checkingDL": TorrentDownloadStatus.Checking,
+ "error": TorrentDownloadStatus.Error,
+}
+
+
+def get_status(status):
+ return status_mapping[status]
+
class QBittorrentCredentials(Base):
__tablename__ = "qbittorrent_credentials"
@@ -167,5 +188,28 @@ def remove_torrent(self, torrent_hash):
r = parameters['session'].post(parameters['target'] + "command/delete", data=payload)
return r.status_code == 200
+ def get_download_status(self):
+ parameters = self._get_params()
+ response = parameters['session'].get(parameters['target'] + "query/torrents/")
+ response.raise_for_status()
+ result = response.json()
+ torrents = {}
+ for torrent in result:
+ torrents[torrent['hash']] = DownloadStatus(torrent['progress'] * torrent['size'], torrent['size'],
+ torrent['dlspeed'],
+ torrent['upspeed'], get_status(torrent['state']),
+ torrent['progress'], torrent['ratio'])
+ return torrents
+
+ def get_download_status_by_hash(self, torrent_hash):
+ parameters = self._get_params()
+ torrent_hash = torrent_hash.lower()
+ response = parameters['session'].get(parameters['target'] + "query/propertiesGeneral/" + torrent_hash)
+ response.raise_for_status()
+ result = response.json()
+ return DownloadStatus(result['total_downloaded'], result['total_size'], result['dl_speed'],
+ result['up_speed'], TorrentDownloadStatus.Unknown,
+ result['total_downloaded'] / result['total_size'] * 100, result['share_ratio'])
+
register_plugin('client', 'qbittorrent', QBittorrentClientPlugin())
diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py
index 26fcf8a0..fb2fe071 100644
--- a/monitorrent/plugins/clients/transmission.py
+++ b/monitorrent/plugins/clients/transmission.py
@@ -6,6 +6,20 @@
from monitorrent.plugin_managers import register_plugin
import base64
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
+
+status_mapping = {
+ "stopped": TorrentDownloadStatus.Paused,
+ "check pending": TorrentDownloadStatus.Checking,
+ "checking": TorrentDownloadStatus.Checking,
+ "downloading": TorrentDownloadStatus.Downloading,
+ "seeding": TorrentDownloadStatus.Seeding
+}
+
+
+def get_status(status):
+ return status_mapping[status]
+
class TransmissionCredentials(Base):
__tablename__ = "transmission_credentials"
@@ -115,4 +129,22 @@ def remove_torrent(self, torrent_hash):
client.remove_torrent(torrent_hash.lower(), delete_data=False)
return True
+ def get_download_status(self):
+ client = self.check_connection()
+ torrents = client.get_torrents(None, [])
+ result = {}
+ for torrent in torrents:
+ result[torrent.hashString] = DownloadStatus(torrent.downloadedEver, torrent.totalSize,
+ torrent.rateDownload,
+ torrent.rateUpload,
+ get_status(torrent.status), torrent.progress, torrent.ratio)
+ return result
+
+ def get_download_status_by_hash(self, torrent_hash):
+ client = self.check_connection()
+ torrent = client.get_torrent(torrent_hash.lower(), [])
+ return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload,
+ get_status(torrent.status), torrent.progress, torrent.ratio)
+
+
register_plugin('client', 'transmission', TransmissionClientPlugin())
diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py
index 9682dd99..421cd4c1 100644
--- a/monitorrent/plugins/clients/utorrent.py
+++ b/monitorrent/plugins/clients/utorrent.py
@@ -5,15 +5,45 @@
import json
+from aenum import IntFlag
+
import requests
from io import BytesIO
from sqlalchemy import Column, Integer, String
from monitorrent.db import Base, DBSession
from monitorrent.plugin_managers import register_plugin
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
from monitorrent.utils.soup import get_soup
+class StatusFlags(IntFlag):
+ Started = 1,
+ Checking = 2,
+ StartAfterCheck = 4,
+ Checked = 8,
+ Error = 16,
+ Paused = 32,
+ Queued = 64,
+ Loaded = 128
+
+
+def get_status(status):
+ if StatusFlags.Error in status:
+ return TorrentDownloadStatus.Error
+ elif StatusFlags.Paused in status:
+ return TorrentDownloadStatus.Paused
+ elif StatusFlags.Started in status:
+ return TorrentDownloadStatus.Downloading
+ elif StatusFlags.Checking in status:
+ return TorrentDownloadStatus.Checking
+ elif StatusFlags.Queued in status:
+ return TorrentDownloadStatus.Queued
+ elif StatusFlags.Checked in status:
+ return TorrentDownloadStatus.Paused
+
+
+
class UTorrentCredentials(Base):
__tablename__ = "utorrent_credentials"
@@ -137,4 +167,30 @@ def remove_torrent(self, torrent_hash):
parameters['session'].get(parameters['target'], params=payload)
return True
+ def get_download_status(self):
+ parameters = self._get_params()
+ payload = {"list": '1', "token": parameters["token"]}
+ torrents = parameters['session'].get(parameters['target'],
+ params=payload)
+ array = json.loads(torrents.text)['torrents']
+ result = {}
+ for torrent in array:
+ result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8],
+ get_status(StatusFlags(torrent[1])),
+ float(torrent[4]) / 10.0, torrent[7])
+ return result
+
+ def get_download_status_by_hash(self, torrent_hash):
+ parameters = self._get_params()
+
+ payload = {"list": '1', "token": parameters["token"]}
+ torrents = parameters['session'].get(parameters['target'],
+ params=payload)
+ array = json.loads(torrents.text)['torrents']
+ torrent = next(torrent for torrent in array if torrent[0] == torrent_hash)
+ if torrent:
+ return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8], get_status(StatusFlags(torrent[1])),
+ float(torrent[4]) / 10.0, torrent[7])
+
+
register_plugin('client', 'utorrent', UTorrentClientPlugin())
diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py
index 6d7f2f8a..e7944b1e 100644
--- a/monitorrent/rest/clients.py
+++ b/monitorrent/rest/clients.py
@@ -1,3 +1,4 @@
+import json
from builtins import str
from builtins import object
import falcon
@@ -56,7 +57,21 @@ def on_put(self, req, resp, client):
resp.status = falcon.HTTP_NO_CONTENT
-# noinspection PyUnusedLocal
+class TorrentStatus(object):
+ def __init__(self, clients_manager):
+ """
+ :type clients_manager: ClientsManager
+ """
+ self.clients_manager = clients_manager
+
+ def on_get(self, req, resp, torrent_hash):
+ """
+
+ :type torrent_hash: str
+ """
+ resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ # noinspection PyUnusedLocal
+
+
class ClientCheck(object):
def __init__(self, clients_manager):
"""
@@ -125,4 +140,30 @@ def on_put(self, req, resp, client):
except KeyError as e:
log.error("Client could not be found", client=client, exception=str(e))
raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e))
+ except Exception as e:
+ log.error("An error has occurred", exception=str(e))
+ raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e))
resp.status = falcon.HTTP_NO_CONTENT
+
+
+class ClientStatus(object):
+ def __init__(self, clients_manager):
+ """
+ :type clients_manager: ClientsManager
+ """
+ self.clients_manager = clients_manager
+
+ def on_get(self, req, resp, client):
+ try:
+ result = self.clients_manager.get_download_status(client)
+ result_dict = {}
+ for key, value in result.items():
+ result_dict[key] = value.__dict__
+ resp.json = result_dict
+ except KeyError as e:
+ log.error("Client could not be found", client=client, exception=str(e))
+ raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e))
+ except Exception as e:
+ log.error("An error has occurred", exception=str(e))
+ raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e))
+ resp.status = falcon.HTTP_OK
diff --git a/monitorrent/rest/new_version.py b/monitorrent/rest/new_version.py
index d9caa712..70702593 100644
--- a/monitorrent/rest/new_version.py
+++ b/monitorrent/rest/new_version.py
@@ -1,5 +1,10 @@
+import falcon
+import structlog
+
from monitorrent.new_version_checker import NewVersionChecker
+log = structlog.get_logger()
+
# noinspection PyUnusedLocal
class NewVersion(object):
diff --git a/monitorrent/rest/notifiers.py b/monitorrent/rest/notifiers.py
index 59385c87..7dc6e3bd 100644
--- a/monitorrent/rest/notifiers.py
+++ b/monitorrent/rest/notifiers.py
@@ -5,6 +5,7 @@
from builtins import object
import structlog
+
from monitorrent.plugin_managers import NotifierManager
log = structlog.get_logger()
diff --git a/requirements.txt b/requirements.txt
index afd7b4e6..84de59ab 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,5 +18,6 @@ structlog
#python 2/3 compatibility and backports
six
-enum34>=1.0.4
+enum34
+aenum
future
diff --git a/server.py b/server.py
index 4b52df8a..4990d707 100644
--- a/server.py
+++ b/server.py
@@ -25,7 +25,8 @@
from monitorrent.rest.login import Login, Logout
from monitorrent.rest.topics import TopicCollection, TopicParse, Topic, TopicResetStatus, TopicPauseState
from monitorrent.rest.trackers import TrackerCollection, Tracker, TrackerCheck
-from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault
+from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, \
+ TorrentStatus, ClientStatus
from monitorrent.rest.settings_authentication import SettingsAuthentication
from monitorrent.rest.settings_password import SettingsPassword
from monitorrent.rest.settings_execute import SettingsExecute
@@ -91,6 +92,8 @@ def create_app(secret_key, token, tracker_manager, clients_manager, notifier_man
app.add_route('/api/clients/{client}', Client(clients_manager))
app.add_route('/api/clients/{client}/check', ClientCheck(clients_manager))
app.add_route('/api/clients/{client}/default', ClientDefault(clients_manager))
+ app.add_route('/api/clients/{client}/status', ClientStatus(clients_manager))
+ app.add_route('/api/clients/torrent/status/{torrent_hash}', TorrentStatus(clients_manager))
app.add_route('/api/notifiers', NotifierCollection(notifier_manager))
app.add_route('/api/notifiers/{notifier}', Notifier(notifier_manager))
app.add_route('/api/notifiers/{notifier}/check', NotifierCheck(notifier_manager))
diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found
new file mode 100644
index 00000000..2fc8e205
--- /dev/null
+++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found
@@ -0,0 +1,54 @@
+interactions:
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/token.html
+ response:
+ body: {string: '
iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
'}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['121']
+ Content-Type: [text/html]
+ Set-Cookie: [GUID=ueUrdYTfO6krxw1B8KYu; path=/]
+ status: {code: 200, message: OK}
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ Cookie: [GUID=ueUrdYTfO6krxw1B8KYu]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/?list=1&token=iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
+ response:
+ body: {string: '{"build":43388,"torrents": [
+
+
+ ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1250541568,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4369250876,"","","Downloading
+ 22.2 %","1",1489828545,0,"","C:\\Users\\DSilence\\Downloads",0,"75BD3F66"]],
+
+ "label": [],"torrentc": "1089974307"
+
+ ,"rssfeeds": []
+
+ ,"rssfilters": []
+
+ }
+
+ '}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['392']
+ Content-Type: [text/plain]
+ status: {code: 200, message: OK}
+version: 1
diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success
new file mode 100644
index 00000000..e1b046a7
--- /dev/null
+++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success
@@ -0,0 +1,54 @@
+interactions:
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/token.html
+ response:
+ body: {string: '3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
'}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['121']
+ Content-Type: [text/html]
+ Set-Cookie: [GUID=5NB1vxis1FNc70BFwRXi; path=/]
+ status: {code: 200, message: OK}
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ Cookie: [GUID=5NB1vxis1FNc70BFwRXi]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/?list=1&token=3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
+ response:
+ body: {string: '{"build":43388,"torrents": [
+
+
+ ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1251196928,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4368595516,"","","Downloading
+ 22.2 %","1",1489828545,0,"","C:\\Users\\Test\\Downloads",0,"75BD3F66"]],
+
+ "label": [],"torrentc": "675099747"
+
+ ,"rssfeeds": []
+
+ ,"rssfilters": []
+
+ }
+
+ '}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['391']
+ Content-Type: [text/plain]
+ status: {code: 200, message: OK}
+version: 1
diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found
new file mode 100644
index 00000000..dc8b787c
--- /dev/null
+++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found
@@ -0,0 +1,40 @@
+interactions:
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/token.html
+ response:
+ body: {string: 'iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
'}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['121']
+ Content-Type: [text/html]
+ Set-Cookie: [GUID=ueUrdYTfO6krxw1B8KYu; path=/]
+ status: {code: 200, message: OK}
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ Cookie: [GUID=ueUrdYTfO6krxw1B8KYu]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/?list=1&token=iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
+ response:
+ body: {string: ''}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['392']
+ Content-Type: [text/plain]
+ status: {code: 200, message: OK}
+version: 1
diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success
new file mode 100644
index 00000000..e1b046a7
--- /dev/null
+++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success
@@ -0,0 +1,54 @@
+interactions:
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/token.html
+ response:
+ body: {string: '3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
'}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['121']
+ Content-Type: [text/html]
+ Set-Cookie: [GUID=5NB1vxis1FNc70BFwRXi; path=/]
+ status: {code: 200, message: OK}
+- request:
+ body: null
+ headers:
+ Accept: ['*/*']
+ Accept-Encoding: ['gzip, deflate']
+ Authorization: [Basic YWRtaW46cGFzc3dvcmQ=]
+ Connection: [keep-alive]
+ Cookie: [GUID=5NB1vxis1FNc70BFwRXi]
+ User-Agent: [python-requests/2.13.0]
+ method: GET
+ uri: http://localhost:8080/gui/?list=1&token=3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
+ response:
+ body: {string: '{"build":43388,"torrents": [
+
+
+ ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1251196928,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4368595516,"","","Downloading
+ 22.2 %","1",1489828545,0,"","C:\\Users\\Test\\Downloads",0,"75BD3F66"]],
+
+ "label": [],"torrentc": "675099747"
+
+ ,"rssfeeds": []
+
+ ,"rssfilters": []
+
+ }
+
+ '}
+ headers:
+ Cache-Control: [no-cache]
+ Connection: [keep-alive]
+ Content-Length: ['391']
+ Content-Type: [text/plain]
+ status: {code: 200, message: OK}
+version: 1
diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py
index e154a7da..d3921e7c 100644
--- a/tests/plugins/clients/test_deluge_plugin.py
+++ b/tests/plugins/clients/test_deluge_plugin.py
@@ -277,3 +277,115 @@ def test_get_download_dir_exception(self, deluge_client):
plugin.get_download_dir()
rpc_client.call.assert_called_once_with('core.get_config_value', 'move_completed_path')
+
+ @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient')
+ def test_should_get_torrent_status_by_hash_successfully(self, deluge_client):
+ torrent_hash = 'SomeRandomHashMockString'
+ rpc_client = deluge_client.return_value
+ rpc_client.connected = True
+ rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30,
+ b'total_size': 100,
+ b'download_payload_rate': 50000.0,
+ b'upload_payload_rate': 0.0,
+ b'state': b'downloading',
+ b'progress': 23,
+ b'ratio': 1,
+ b'paused': True}}
+
+ plugin = DelugeClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ result = plugin.get_download_status_by_hash(torrent_hash)
+ assert result.download_speed == 50000.0
+ assert result.upload_speed == 0.0
+ assert result.total_bytes == 100
+ assert result.downloaded_bytes == 30
+
+ @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient')
+ def test_should_fail_when_no_settings_on_get_torrent_status_by_hash(self, deluge_client):
+ rpc_client = deluge_client.return_value
+ rpc_client.connected = True
+ rpc_client.call.side_effect = Exception
+
+ torrent_hash = 'SomeRandomHashMockString'
+
+ plugin = DelugeClientPlugin()
+
+ assert plugin.get_download_status_by_hash(torrent_hash) is False
+
+ rpc_client.call.assert_not_called()
+
+ @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient')
+ def test_should_fail_get_torrent_status_by_hash_on_error(self, deluge_client):
+ rpc_client = deluge_client.return_value
+ rpc_client.connected = True
+ rpc_client.call.side_effect = Exception
+
+ torrent_hash = 'SomeRandomHashMockString'
+
+ plugin = DelugeClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception) as e:
+ plugin.get_download_status_by_hash(torrent_hash)
+
+ rpc_client.call.assert_called_once_with("core.get_torrents_status",
+ {'hash': torrent_hash.lower()},
+ ['total_done', 'total_size', 'download_payload_rate',
+ 'upload_payload_rate', 'state', 'progress'])
+
+ @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient')
+ def test_should_get_torrent_statuses_successfully(self, deluge_client):
+ torrent_hash = 'SomeRandomHashMockString'
+ rpc_client = deluge_client.return_value
+ rpc_client.connected = True
+ rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30,
+ b'total_size': 100,
+ b'download_payload_rate': 50000.0,
+ b'upload_payload_rate': 0.0,
+ b'state': b'Downloading',
+ b'progress': 23,
+ b'ratio': 1,
+ b'paused': False
+ }}
+
+ plugin = DelugeClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ result = plugin.get_download_status().popitem()[1]
+ assert result.download_speed == 50000.0
+ assert result.upload_speed == 0.0
+ assert result.total_bytes == 100
+ assert result.downloaded_bytes == 30
+
+ @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient')
+ def test_should_fail_when_no_settings_on_get_torrent_statuses(self, deluge_client):
+ rpc_client = deluge_client.return_value
+ rpc_client.connected = True
+ rpc_client.call.side_effect = Exception
+
+ plugin = DelugeClientPlugin()
+
+ with pytest.raises(Exception):
+ plugin.get_download_status()
+
+ rpc_client.call.assert_not_called()
+
+ @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient')
+ def test_should_fail_get_torrent_status_on_error(self, deluge_client):
+ rpc_client = deluge_client.return_value
+ rpc_client.connected = True
+ rpc_client.call.side_effect = Exception
+
+ plugin = DelugeClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception) as e:
+ plugin.get_download_status()
+
+ rpc_client.call.assert_called_once_with("core.get_torrents_status",
+ {}, [])
diff --git a/tests/plugins/clients/test_downloader.py b/tests/plugins/clients/test_downloader.py
index 85624935..7005fce4 100644
--- a/tests/plugins/clients/test_downloader.py
+++ b/tests/plugins/clients/test_downloader.py
@@ -70,7 +70,7 @@ def test_find_torrent(self):
find_torrent = plugin.find_torrent(torrent.info_hash)
self.assertNotEqual(False, find_torrent)
- date_added = datetime.fromtimestamp(os.path.getctime(downloaded_filepath))\
+ date_added = datetime.fromtimestamp(os.path.getctime(downloaded_filepath)) \
.replace(tzinfo=reference.LocalTimezone()).astimezone(utc)
expected = {'name': torrent_filename, 'date_added': date_added}
self.assertEqual(expected, find_torrent)
@@ -197,3 +197,19 @@ def test_remove_torrent_failed(self):
remove.side_effect = OSError
self.assertFalse(plugin.remove_torrent(torrent.info_hash))
self.assertTrue(os.path.exists(downloaded_filepath))
+
+ def test_download_status_by_hash(self):
+ plugin = DownloaderPlugin()
+ status = plugin.get_download_status_by_hash("torrent")
+ assert status.upload_speed == 0
+ assert status.download_speed == 0
+ assert status.total_bytes == 0
+ assert status.downloaded_bytes == 0
+
+ def test_download_status(self):
+ plugin = DownloaderPlugin()
+ status = plugin.get_download_status().popitem()[1]
+ assert status.upload_speed == 0
+ assert status.download_speed == 0
+ assert status.total_bytes == 0
+ assert status.downloaded_bytes == 0
diff --git a/tests/plugins/clients/test_qbittorrent.py b/tests/plugins/clients/test_qbittorrent.py
index d2c64fcd..7e622328 100644
--- a/tests/plugins/clients/test_qbittorrent.py
+++ b/tests/plugins/clients/test_qbittorrent.py
@@ -247,3 +247,151 @@ def test_get_download_dir_error(self, mocker):
with pytest.raises(Exception) as e:
plugin.get_download_dir()
+
+ @Mocker()
+ def test_should_get_download_status_by_hash_successfully(self, mocker):
+ target = "{0}:{1}/".format(self.real_host, self.real_port)
+
+ mocker.post(target + "login", text="Ok.")
+
+ hash_string = 'hash'
+
+ mocker.get(target + "query/propertiesGeneral/" + hash_string,
+ status_code=200,
+ text="{\"addition_date\":1487792728,\"comment\":\"\",\"completion_date\":-1,\"created_by\":\"\","
+ "\"creation_date\":1487792646,\"dl_limit\":-1,\"dl_speed\":5990252,\"dl_speed_avg\":5851317,"
+ "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100,"
+ "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136,"
+ "\"pieces_num\":871,\"reannounce\":2849,"
+ "\"save_path\":\"C:\\\\Users\\\\DSilence\\\\Downloads\\\\test_dir\\\\\",\"seeding_time\":0,"
+ "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202,"
+ "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372,"
+ "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422,"
+ "\"total_wasted\":110,\"up_limit\":-1,\"up_speed\":129319,\"up_speed_avg\":90435}")
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin = QBittorrentClientPlugin()
+ plugin.set_settings(settings)
+
+ result = plugin.get_download_status_by_hash(hash_string)
+ assert result.upload_speed == 129319
+ assert result.download_speed == 5990252
+ assert result.downloaded_bytes == 1187817372
+ assert result.total_bytes == 7299496252
+
+ @Mocker()
+ def test_should_fail_download_status_by_hash_when_not_logged_in(self, mocker):
+ target = "{0}:{1}/".format(self.real_host, self.real_port)
+
+ mocker.post(target + "login", text="Fails.")
+
+ hash_string = 'hash'
+
+ mocker.get(target + "query/propertiesGeneral/" + hash_string,
+ status_code=200,
+ text="{\"addition_date\":1487792728,\"comment\":\"\",\"completion_date\":-1,\"created_by\":\"\","
+ "\"creation_date\":1487792646,\"dl_limit\":-1,\"dl_speed\":5990252,\"dl_speed_avg\":5851317,"
+ "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100,"
+ "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136,"
+ "\"pieces_num\":871,\"reannounce\":2849,"
+ "\"save_path\":\"C:\\\\Users\\\\sad\\\\tmp\\\\test_dir\\\\\",\"seeding_time\":0,"
+ "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202,"
+ "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372,"
+ "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422,"
+ "\"total_wasted\":110,\"up_limit\":-1,\"up_speed\":129319,\"up_speed_avg\":90435}")
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin = QBittorrentClientPlugin()
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception):
+ plugin.get_download_status_by_hash(hash_string)
+
+ @Mocker()
+ def test_should_fail_when_download_status_by_hash_wasnt_retrieved(self, mocker):
+ target = "{0}:{1}/".format(self.real_host, self.real_port)
+
+ mocker.post(target + "login", text="Ok.")
+
+ error = {'error': 500}
+ hash_string = 'hash'
+ mocker.get(target + "query/propertiesGeneral/" + hash_string, status_code=500, text=json.dumps(error))
+
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+
+ plugin = QBittorrentClientPlugin()
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception) as e:
+ plugin.get_download_status_by_hash(hash_string)
+
+ @Mocker()
+ def test_should_get_download_status_successfully(self, mocker):
+ target = "{0}:{1}/".format(self.real_host, self.real_port)
+
+ mocker.post(target + "login", text="Ok.")
+
+ mocker.get(target + "query/torrents/",
+ status_code = 200,
+ text='[{"added_on":1489778322,"category":"","completion_on":4294967295,"dlspeed":5242223,'
+ '"eta":8640000,"f_l_piece_prio":false,"force_start":false,'
+ '"hash":"1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7",'
+ '"name":"en_windows_10_pro_10240_x64_dvd","num_complete":28,"num_incomplete":2,'
+ '"num_leechs":0,"num_seeds":0,"priority":1,"progress":0.20,"ratio":0,'
+ '"save_path":"C:\\\\Users\\\\DSilence\\\\Downloads\\\\","seq_dl":false,"size":3992293376,'
+ '"state":"pausedDL","super_seeding":false,"upspeed":0}]')
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin = QBittorrentClientPlugin()
+ plugin.set_settings(settings)
+
+ expected_key = '1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7'
+ status = plugin.get_download_status()
+ assert expected_key in status
+ result = status[expected_key]
+ assert result.upload_speed == 0
+ assert result.download_speed == 5242223
+ assert result.downloaded_bytes == 798458675.2
+ assert result.total_bytes == 3992293376
+
+ @Mocker()
+ def test_should_fail_download_status_when_not_logged_in(self, mocker):
+ target = "{0}:{1}/".format(self.real_host, self.real_port)
+
+ mocker.post(target + "login", text="Fails.")
+
+ mocker.get(target + "query/torrents/",
+ status_code=200,
+ text='[{"added_on":1489778322,"category":"","completion_on":4294967295,"dlspeed":5242223,'
+ '"eta":8640000,"f_l_piece_prio":false,"force_start":false,'
+ '"hash":"1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7",'
+ '"name":"en_windows_10_pro_10240_x64_dvd","num_complete":28,"num_incomplete":2,'
+ '"num_leechs":0,"num_seeds":0,"priority":1,"progress":0.20,"ratio":0,'
+ '"save_path":"C:\\\\Users\\\\DSilence\\\\Downloads\\\\","seq_dl":false,"size":3992293376,'
+ '"state":"pausedDL","super_seeding":false,"upspeed":0}]')
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin = QBittorrentClientPlugin()
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception):
+ plugin.get_download_status()
+
+ @Mocker()
+ def test_should_fail_when_download_status_wasnt_retrieved(self, mocker):
+ target = "{0}:{1}/".format(self.real_host, self.real_port)
+
+ mocker.post(target + "login", text="Ok.")
+
+ error = {'error': 500}
+ mocker.get(target + "query/torrents/", status_code=500, text=json.dumps(error))
+
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+
+ plugin = QBittorrentClientPlugin()
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception) as e:
+ plugin.get_download_status()
diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py
index e78e490a..a35233ac 100644
--- a/tests/plugins/clients/test_transmission_plugin.py
+++ b/tests/plugins/clients/test_transmission_plugin.py
@@ -134,7 +134,8 @@ def test_add_torrent_with_settings(self, transmission_client):
torrent = b'!torrent.content'
self.assertTrue(plugin.add_torrent(torrent, TopicSettings('/path/to/download/dir')))
- rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8'), download_dir='/path/to/download/dir')
+ rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8'),
+ download_dir='/path/to/download/dir')
@patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
def test_add_torrent_without_credentials(self, transmission_client):
@@ -233,3 +234,90 @@ def test_get_download_dir_exception(self, transmission_client):
rpc_client.get_session.assert_called_once()
+ @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
+ def test_should_get_status_by_hash(self, transmission_client):
+ rpc_client = transmission_client.return_value
+ rpc_client.get_torrent.return_value = namedtuple('Torrent',
+ ('rateDownload', 'totalSize', 'downloadedEver',
+ 'rateUpload', 'status', 'progress', 'ratio')
+ )(5000, 10000, 6000, 20, 'downloading', 23, 1)
+
+ plugin = TransmissionClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ status = plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96')
+ assert status.downloaded_bytes == 6000
+ assert status.download_speed == 5000
+ assert status.total_bytes == 10000
+ assert status.upload_speed == 20
+
+ @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
+ def test_should_fail_when_no_settings_for_get_status_by_hash(self, transmission_client):
+ rpc_client = transmission_client.return_value
+ rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError
+
+ plugin = TransmissionClientPlugin()
+ with pytest.raises(Exception):
+ plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96')
+ rpc_client.get_torrent.assert_not_called()
+
+ @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
+ def test_should_fail_get_status_by_hash_when_get_torrent_fails(self, transmission_client):
+ rpc_client = transmission_client.return_value
+ rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError
+
+ plugin = TransmissionClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ with pytest.raises(transmissionrpc.TransmissionError) as e:
+ plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96')
+
+ rpc_client.get_torrent.assert_called_once()
+
+ @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
+ def test_should_get_status(self, transmission_client):
+ rpc_client = transmission_client.return_value
+ torrent_id = '4e2597302ad6b4d7a545c8ec02621ac232316b96'
+ rpc_client.get_torrents.return_value = [namedtuple('Torrent',
+ ('hashString', 'rateDownload', 'totalSize', 'downloadedEver',
+ 'rateUpload', 'status', 'progress', 'ratio')
+ )(torrent_id, 5000, 10000, 6000, 20, 'downloading', 23, 1)]
+
+ plugin = TransmissionClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ statuses = plugin.get_download_status()
+ assert torrent_id in statuses
+ status = statuses[torrent_id]
+ assert status.downloaded_bytes == 6000
+ assert status.download_speed == 5000
+ assert status.total_bytes == 10000
+ assert status.upload_speed == 20
+
+ @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
+ def test_should_fail_when_no_settings_for_get_status(self, transmission_client):
+ rpc_client = transmission_client.return_value
+ rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError
+
+ plugin = TransmissionClientPlugin()
+
+ with pytest.raises(Exception):
+ plugin.get_download_status()
+ rpc_client.get_torrent.assert_not_called()
+
+ @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client')
+ def test_should_fail_get_status_when_get_torrent_fails(self, transmission_client):
+ rpc_client = transmission_client.return_value
+ rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError
+
+ plugin = TransmissionClientPlugin()
+ settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'}
+ plugin.set_settings(settings)
+
+ with pytest.raises(transmissionrpc.TransmissionError) as e:
+ plugin.get_download_status()
+
+ rpc_client.get_torrents.assert_called_once()
diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py
index 588af011..f852ee18 100644
--- a/tests/plugins/clients/test_utorrent_plugin.py
+++ b/tests/plugins/clients/test_utorrent_plugin.py
@@ -1,9 +1,11 @@
+
import pytest
from ddt import ddt
from mock import patch, Mock, MagicMock
from requests import Response
-from monitorrent.plugins.clients.utorrent import UTorrentClientPlugin
+from monitorrent.plugins.clients import TorrentDownloadStatus
+from monitorrent.plugins.clients.utorrent import UTorrentClientPlugin, StatusFlags, get_status
from tests import DbTestCase, use_vcr
@@ -164,3 +166,81 @@ def test_remove_torrent_success(self, get_mock):
torrent = b'torrent'
self.assertTrue(plugin.remove_torrent(torrent))
+
+ @use_vcr
+ def test_get_download_status_by_hash_success(self):
+ plugin = UTorrentClientPlugin()
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin.set_settings(settings)
+
+ torrent = "3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE"
+ result = plugin.get_download_status_by_hash(torrent)
+
+ assert result.upload_speed == 6277
+ assert result.download_speed == 3106179
+ assert result.downloaded_bytes == 1251196928
+ assert result.total_bytes == 5619792444
+
+ def test_get_download_status_by_hash_bad_settings(self):
+ plugin = UTorrentClientPlugin()
+ torrent = 'torrent'
+ with pytest.raises(TypeError):
+ plugin.get_download_status_by_hash(torrent)
+
+ @use_vcr
+ def test_get_download_status_by_hash_not_found(self):
+ plugin = UTorrentClientPlugin()
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin.set_settings(settings)
+
+ torrent = "torrent"
+ with pytest.raises(Exception) as e:
+ plugin.get_download_status_by_hash(torrent)
+
+ @use_vcr
+ def test_get_download_status_success(self):
+ plugin = UTorrentClientPlugin()
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin.set_settings(settings)
+
+ torrent_hash = "3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE"
+ result = plugin.get_download_status()
+
+ assert torrent_hash in result
+ torrent = result[torrent_hash]
+ assert torrent.upload_speed == 6277
+ assert torrent.download_speed == 3106179
+ assert torrent.downloaded_bytes == 1251196928
+ assert torrent.total_bytes == 5619792444
+
+ def test_get_download_status_bad_settings(self):
+ plugin = UTorrentClientPlugin()
+ with pytest.raises(TypeError):
+ plugin.get_download_status()
+
+ @use_vcr
+ def test_get_download_status_not_found(self):
+ plugin = UTorrentClientPlugin()
+ settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login,
+ 'password': self.real_password}
+ plugin.set_settings(settings)
+
+ with pytest.raises(Exception) as e:
+ plugin.get_download_status()
+
+ def test_get_status(self):
+ status = StatusFlags.Error
+ assert get_status(status) == TorrentDownloadStatus.Error
+ status = StatusFlags.Paused
+ assert get_status(status) == TorrentDownloadStatus.Paused
+ status = StatusFlags.Checking
+ assert get_status(status) == TorrentDownloadStatus.Checking
+ status = StatusFlags.Started
+ assert get_status(status) == TorrentDownloadStatus.Downloading
+ status = StatusFlags.Queued
+ assert get_status(status) == TorrentDownloadStatus.Queued
+ status = StatusFlags.Checked
+ assert get_status(status) == TorrentDownloadStatus.Paused
diff --git a/tests/rest/test_api_clients.py b/tests/rest/test_api_clients.py
index eb82cdc7..d0ea15bc 100644
--- a/tests/rest/test_api_clients.py
+++ b/tests/rest/test_api_clients.py
@@ -3,8 +3,11 @@
import falcon
from mock import MagicMock
from ddt import ddt, data
+
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
from tests import RestTestBase
-from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault
+from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus, \
+ ClientStatus
from monitorrent.plugin_managers import ClientsManager
@@ -84,7 +87,7 @@ def test_not_found_settings(self):
self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND)
self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type'])
- def test_get_settings_error(self):
+ def test_error_get_settings(self):
clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()})
clients_manager.get_settings = MagicMock(side_effect=Exception)
@@ -118,7 +121,7 @@ def test_not_found_update_settings(self):
body=json.dumps({'login': 'login', 'password': 'password'}))
self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND)
- def test_eroor_update_settings(self):
+ def test_error_update_settings(self):
clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()})
clients_manager.set_settings = MagicMock(side_effect=Exception)
@@ -130,6 +133,74 @@ def test_eroor_update_settings(self):
self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR)
+@ddt
+class TorrentStatusTest(RestTestBase):
+ def test_get_download_status_by_hash(self):
+ clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()})
+ clients_manager.get_download_status_by_id = MagicMock(return_value=DownloadStatus(1, 2, 3, 4,
+ TorrentDownloadStatus.Unknown,
+ 5, 6))
+
+ client = TorrentStatus(clients_manager)
+ client.__no_auth__ = True
+ self.api.add_route('/api/clients/torrent/status/{torrent_hash}', client)
+
+ body = self.simulate_request('/api/clients/torrent/status/{0}'.format("hash"), decode="utf-8")
+ self.assertEqual(self.srmock.status, falcon.HTTP_OK)
+ self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type'])
+
+ result = json.loads(body)
+ assert result['downloaded_bytes'] == 1
+ assert result['total_bytes'] == 2
+ assert result['download_speed'] == 3
+ assert result['upload_speed'] == 4
+
+@ddt
+class ClientStatusTest(RestTestBase):
+ def test_get_download_status(self):
+ clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()})
+ clients_manager.get_download_status = MagicMock(return_value={"torrent": DownloadStatus(1, 2, 3, 4,
+ TorrentDownloadStatus
+ .Unknown, 5, 6)})
+
+ client = ClientStatus(clients_manager)
+ client.__no_auth__ = True
+ self.api.add_route('/api/clients/{client}/status', client)
+
+ body = self.simulate_request('/api/clients/{0}/status'.format("client"), decode="utf-8")
+ self.assertEqual(self.srmock.status, falcon.HTTP_OK)
+ self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type'])
+
+ result = json.loads(body)
+ torrent = result["torrent"]
+ assert torrent['downloaded_bytes'] == 1
+ assert torrent['total_bytes'] == 2
+ assert torrent['download_speed'] == 3
+ assert torrent['upload_speed'] == 4
+
+ def test_check_client_not_found(self):
+ clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()})
+ clients_manager.get_download_status = MagicMock(side_effect=KeyError)
+
+ client = ClientStatus(clients_manager)
+ client.__no_auth__ = True
+ self.api.add_route('/api/clients/{client}/status', client)
+
+ self.simulate_request('/api/clients/{0}/status'.format("client"))
+ self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND)
+
+ def test_check_client_error(self):
+ clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()})
+ clients_manager.get_download_status = MagicMock(side_effect=Exception)
+
+ client = ClientStatus(clients_manager)
+ client.__no_auth__ = True
+ self.api.add_route('/api/clients/{client}/status', client)
+
+ self.simulate_request('/api/clients/{0}/status'.format("client"))
+ self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR)
+
+
@ddt
class CheckClientTest(RestTestBase):
@data(True, False)
@@ -249,3 +320,14 @@ def test_set_default_not_found(self):
self.simulate_request('/api/clients/{0}/default'.format('random.org'), method='PUT')
self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND)
+
+ def test_set_default_on_error(self):
+ clients_manager = ClientsManager({'tracker.org': ClientCollectionTest.TestClient()})
+ clients_manager.set_default = MagicMock(side_effect=Exception)
+
+ client = ClientDefault(clients_manager)
+ client.__no_auth__ = True
+ self.api.add_route('/api/clients/{client}/default', client)
+
+ self.simulate_request('/api/clients/{0}/default'.format('tracker.org'), method='PUT')
+ self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR)
\ No newline at end of file
diff --git a/tests/rest/test_api_execute.py b/tests/rest/test_api_execute.py
index 950a5327..c56d1370 100644
--- a/tests/rest/test_api_execute.py
+++ b/tests/rest/test_api_execute.py
@@ -99,20 +99,23 @@ def set_result():
self.assertEqual(result, {'is_running': True, 'logs': [{}]})
- def test_execute_logs_failure(self):
+ def test_wrong_params(self):
log_manager = ExecuteLogManager()
log_manager.get_current_execute_log_details = Mock(side_effect=Exception)
- log_manager.is_running = Mock(return_value=True)
-
- execute_log_current = ExecuteLogCurrent(log_manager)
+ log_manager.is_running = Mock(return_value=False)
time = TimeMock()
with patch("monitorrent.rest.execute.time", time):
+
+ execute_log_current = ExecuteLogCurrent(log_manager)
+
self.api.add_route(self.test_route, execute_log_current)
self.simulate_request(self.test_route, decode='utf-8')
+
self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR)
+
@ddt
class ExecuteCallTest(RestTestBase):
def test_execute(self):
diff --git a/tests/rest/test_api_notifiers.py b/tests/rest/test_api_notifiers.py
index ccdb5009..54a65b13 100644
--- a/tests/rest/test_api_notifiers.py
+++ b/tests/rest/test_api_notifiers.py
@@ -218,8 +218,7 @@ def test_check_notifier_not_found(self):
self.simulate_request('/api/clients/{0}/check'.format('tracker.org'))
self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND)
- def test_check_notifier_failed(self):
- # noinspection PyTypeChecker
+ def test_check_notifier_exception(self):
notifiers_manager = NotifierManager(Mock(), {'test': NotifierCollectionTest.TestNotifier()})
notifiers_manager.send_test_message = Mock(side_effect=Exception)
diff --git a/tests/test_plugin_managers_clients_manager.py b/tests/test_plugin_managers_clients_manager.py
index b73e6160..0a9d57c5 100644
--- a/tests/test_plugin_managers_clients_manager.py
+++ b/tests/test_plugin_managers_clients_manager.py
@@ -1,5 +1,7 @@
from ddt import ddt, data
from mock import Mock, MagicMock, patch
+
+from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus
from tests import TestCase, DbTestCase
from monitorrent.plugin_managers import ClientsManager, DbClientsManager
from monitorrent.settings_manager import SettingsManager
@@ -173,6 +175,26 @@ def test_remove_torrent_false(self):
remove_torrent_mock1.assert_called_once_with(torrent_hash)
remove_torrent_mock2.assert_not_called()
+ def test_get_download_status_by_hash_success(self):
+ result = DownloadStatus(1, 2, 3, 4, TorrentDownloadStatus.Downloading, 5, 6)
+ get_download_status_by_hash_1 = MagicMock(return_value=result)
+ get_download_status_by_hash_2 = MagicMock(return_value=False)
+
+ torrent_hash = 'hash'
+ self.client1.get_download_status_by_hash = get_download_status_by_hash_1
+ self.client2.get_download_status_by_hash = get_download_status_by_hash_2
+ actual_result = self.clients_manager.get_download_status_by_id(torrent_hash)
+ assert actual_result == result
+
+ def test_get_download_status_success(self):
+ result = {"hash": DownloadStatus(1, 2, 3, 4, TorrentDownloadStatus.Downloading, 5, 6)}
+ get_download_status = MagicMock(return_value=result)
+
+ self.client1.get_download_status = get_download_status
+ actual_result = self.clients_manager.get_download_status(self.CLIENT1_NAME)
+ assert actual_result == result
+
+
def test_get_plugins(self):
clients = self.clients_manager.clients
get_plugins_mock = Mock(return_value=clients)
@@ -196,6 +218,7 @@ def test_empty_clients(self):
self.assertFalse(clients_amanger.add_torrent('!torrent', None))
self.assertFalse(clients_amanger.find_torrent('hash'))
self.assertFalse(clients_amanger.remove_torrent('hash'))
+ self.assertFalse(clients_amanger.get_download_status_by_id('hash'))
def test_unknow_client(self):
with self.assertRaises(KeyError):