Advertisement
Guest User

Untitled

a guest
Dec 21st, 2018
165
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.88 KB | None | 0 0
  1. import requests
  2. import json
  3.  
  4.  
  5. class LoginRequired(Exception):
  6. def __str__(self):
  7. return 'Please login first.'
  8.  
  9.  
  10. class Client(object):
  11. """class to interact with qBittorrent WEB API"""
  12. def __init__(self, url):
  13. if not url.endswith('/'):
  14. url += '/'
  15. self.url = url
  16.  
  17. session = requests.Session()
  18. check_prefs = session.get(url+'query/preferences')
  19.  
  20. if check_prefs.status_code == 200:
  21. self._is_authenticated = True
  22. self.session = session
  23.  
  24. elif check_prefs.status_code == 404:
  25. self._is_authenticated = False
  26. raise RuntimeError("""
  27. This wrapper only supports qBittorrent applications
  28. with version higher than 3.1.x.
  29. Please use the latest qBittorrent release.
  30. """)
  31.  
  32. else:
  33. self._is_authenticated = False
  34.  
  35. def _get(self, endpoint, **kwargs):
  36. """
  37. Method to perform GET request on the API.
  38.  
  39. :param endpoint: Endpoint of the API.
  40. :param kwargs: Other keyword arguments for requests.
  41.  
  42. :return: Response of the GET request.
  43. """
  44. return self._request(endpoint, 'get', **kwargs)
  45.  
  46. def _post(self, endpoint, data, **kwargs):
  47. """
  48. Method to perform POST request on the API.
  49.  
  50. :param endpoint: Endpoint of the API.
  51. :param data: POST DATA for the request.
  52. :param kwargs: Other keyword arguments for requests.
  53.  
  54. :return: Response of the POST request.
  55. """
  56. return self._request(endpoint, 'post', data, **kwargs)
  57.  
  58. def _request(self, endpoint, method, data=None, **kwargs):
  59. """
  60. Method to hanle both GET and POST requests.
  61.  
  62. :param endpoint: Endpoint of the API.
  63. :param method: Method of HTTP request.
  64. :param data: POST DATA for the request.
  65. :param kwargs: Other keyword arguments.
  66.  
  67. :return: Response for the request.
  68. """
  69. final_url = self.url + endpoint
  70.  
  71. if not self._is_authenticated:
  72. raise LoginRequired
  73.  
  74. rq = self.session
  75. if method == 'get':
  76. request = rq.get(final_url, **kwargs)
  77. else:
  78. request = rq.post(final_url, data, **kwargs)
  79.  
  80. request.raise_for_status()
  81. request.encoding = 'utf_8'
  82.  
  83. if len(request.text) == 0:
  84. data = json.loads('{}')
  85. else:
  86. try:
  87. data = json.loads(request.text)
  88. except ValueError:
  89. data = request.text
  90.  
  91. return data
  92.  
  93. def login(self, username='admin', password='admin'):
  94. """
  95. Method to authenticate the qBittorrent Client.
  96.  
  97. Declares a class attribute named ``session`` which
  98. stores the authenticated session if the login is correct.
  99. Else, shows the login error.
  100.  
  101. :param username: Username.
  102. :param password: Password.
  103.  
  104. :return: Response to login request to the API.
  105. """
  106. self.session = requests.Session()
  107. login = self.session.post(self.url+'login',
  108. data={'username': username,
  109. 'password': password})
  110. if login.text == 'Ok.':
  111. self._is_authenticated = True
  112. else:
  113. return login.text
  114.  
  115. def logout(self):
  116. """
  117. Logout the current session.
  118. """
  119. response = self._get('logout')
  120. self._is_authenticated = False
  121. return response
  122.  
  123. @property
  124. def qbittorrent_version(self):
  125. """
  126. Get qBittorrent version.
  127. """
  128. return self._get('version/qbittorrent')
  129.  
  130. @property
  131. def api_version(self):
  132. """
  133. Get WEB API version.
  134. """
  135. return self._get('version/api')
  136.  
  137. @property
  138. def api_min_version(self):
  139. """
  140. Get minimum WEB API version.
  141. """
  142. return self._get('version/api_min')
  143.  
  144. def shutdown(self):
  145. """
  146. Shutdown qBittorrent.
  147. """
  148. return self._get('command/shutdown')
  149.  
  150. def torrents(self, **filters):
  151. """
  152. Returns a list of torrents matching the supplied filters.
  153.  
  154. :param filter: Current status of the torrents.
  155. :param category: Fetch all torrents with the supplied label.
  156. :param sort: Sort torrents by.
  157. :param reverse: Enable reverse sorting.
  158. :param limit: Limit the number of torrents returned.
  159. :param offset: Set offset (if less than 0, offset from end).
  160.  
  161. :return: list() of torrent with matching filter.
  162. """
  163. params = {}
  164. for name, value in filters.items():
  165. # make sure that old 'status' argument still works
  166. name = 'filter' if name == 'status' else name
  167. params[name] = value
  168.  
  169. return self._get('query/torrents', params=params)
  170.  
  171. def get_torrent(self, infohash):
  172. """
  173. Get details of the torrent.
  174.  
  175. :param infohash: INFO HASH of the torrent.
  176. """
  177. return self._get('query/propertiesGeneral/' + infohash.lower())
  178.  
  179. def get_torrent_trackers(self, infohash):
  180. """
  181. Get trackers for the torrent.
  182.  
  183. :param infohash: INFO HASH of the torrent.
  184. """
  185. return self._get('query/propertiesTrackers/' + infohash.lower())
  186.  
  187. def get_torrent_webseeds(self, infohash):
  188. """
  189. Get webseeds for the torrent.
  190.  
  191. :param infohash: INFO HASH of the torrent.
  192. """
  193. return self._get('query/propertiesWebSeeds/' + infohash.lower())
  194.  
  195. def get_torrent_files(self, infohash):
  196. """
  197. Get list of files for the torrent.
  198.  
  199. :param infohash: INFO HASH of the torrent.
  200. """
  201. return self._get('query/propertiesFiles/' + infohash.lower())
  202.  
  203. @property
  204. def global_transfer_info(self):
  205. """
  206. Get JSON data of the global transfer info of qBittorrent.
  207. """
  208. return self._get('query/transferInfo')
  209.  
  210. @property
  211. def preferences(self):
  212. """
  213. Get the current qBittorrent preferences.
  214. Can also be used to assign individual preferences.
  215. For setting multiple preferences at once,
  216. see ``set_preferences`` method.
  217.  
  218. Note: Even if this is a ``property``,
  219. to fetch the current preferences dict, you are required
  220. to call it like a bound method.
  221.  
  222. Wrong::
  223.  
  224. qb.preferences
  225.  
  226. Right::
  227.  
  228. qb.preferences()
  229.  
  230. """
  231. prefs = self._get('query/preferences')
  232.  
  233. class Proxy(Client):
  234. """
  235. Proxy class to to allow assignment of individual preferences.
  236. this class overrides some methods to ease things.
  237.  
  238. Because of this, settings can be assigned like::
  239.  
  240. In [5]: prefs = qb.preferences()
  241.  
  242. In [6]: prefs['autorun_enabled']
  243. Out[6]: True
  244.  
  245. In [7]: prefs['autorun_enabled'] = False
  246.  
  247. In [8]: prefs['autorun_enabled']
  248. Out[8]: False
  249.  
  250. """
  251.  
  252. def __init__(self, url, prefs, auth, session):
  253. super(Proxy, self).__init__(url)
  254. self.prefs = prefs
  255. self._is_authenticated = auth
  256. self.session = session
  257.  
  258. def __getitem__(self, key):
  259. return self.prefs[key]
  260.  
  261. def __setitem__(self, key, value):
  262. kwargs = {key: value}
  263. return self.set_preferences(**kwargs)
  264.  
  265. def __call__(self):
  266. return self.prefs
  267.  
  268. return Proxy(self.url, prefs, self._is_authenticated, self.session)
  269.  
  270. def sync(self, rid=0):
  271. """
  272. Sync the torrents by supplied LAST RESPONSE ID.
  273. Read more @ http://git.io/vEgXr
  274.  
  275. :param rid: Response ID of last request.
  276. """
  277. return self._get('sync/maindata', params={'rid': rid})
  278.  
  279. def download_from_link(self, link, **kwargs):
  280. """
  281. Download torrent using a link.
  282.  
  283. :param link: URL Link or list of.
  284. :param savepath: Path to download the torrent.
  285. :param category: Label or Category of the torrent(s).
  286.  
  287. :return: Empty JSON data.
  288. """
  289. # old:new format
  290. old_arg_map = {'save_path': 'savepath'} # , 'label': 'category'}
  291.  
  292. # convert old option names to new option names
  293. options = kwargs.copy()
  294. for old_arg, new_arg in old_arg_map.items():
  295. if options.get(old_arg) and not options.get(new_arg):
  296. options[new_arg] = options[old_arg]
  297.  
  298. options['urls'] = link
  299.  
  300. # workaround to send multipart/formdata request
  301. # http://stackoverflow.com/a/23131823/4726598
  302. dummy_file = {'_dummy': (None, '_dummy')}
  303.  
  304. return self._post('command/download', data=options, files=dummy_file)
  305.  
  306. def download_from_file(self, file_buffer, **kwargs):
  307. """
  308. Download torrent using a file.
  309.  
  310. :param file_buffer: Single file() buffer or list of.
  311. :param save_path: Path to download the torrent.
  312. :param label: Label of the torrent(s).
  313.  
  314. :return: Empty JSON data.
  315. """
  316. if isinstance(file_buffer, list):
  317. torrent_files = {}
  318. for i, f in enumerate(file_buffer):
  319. torrent_files.update({'torrents%s' % i: f})
  320. else:
  321. torrent_files = {'torrents': file_buffer}
  322.  
  323. data = kwargs.copy()
  324.  
  325. if data.get('save_path'):
  326. data.update({'savepath': data['save_path']})
  327. return self._post('command/upload', data=data, files=torrent_files)
  328.  
  329. def add_trackers(self, infohash, trackers):
  330. """
  331. Add trackers to a torrent.
  332.  
  333. :param infohash: INFO HASH of torrent.
  334. :param trackers: Trackers.
  335. """
  336. data = {'hash': infohash.lower(),
  337. 'urls': trackers}
  338. return self._post('command/addTrackers', data=data)
  339.  
  340. @staticmethod
  341. def _process_infohash_list(infohash_list):
  342. """
  343. Method to convert the infohash_list to qBittorrent API friendly values.
  344.  
  345. :param infohash_list: List of infohash.
  346. """
  347. if isinstance(infohash_list, list):
  348. data = {'hashes': '|'.join([h.lower() for h in infohash_list])}
  349. else:
  350. data = {'hashes': infohash_list.lower()}
  351. return data
  352.  
  353. def pause(self, infohash):
  354. """
  355. Pause a torrent.
  356.  
  357. :param infohash: INFO HASH of torrent.
  358. """
  359. return self._post('command/pause', data={'hash': infohash.lower()})
  360.  
  361. def pause_all(self):
  362. """
  363. Pause all torrents.
  364. """
  365. return self._get('command/pauseAll')
  366.  
  367. def pause_multiple(self, infohash_list):
  368. """
  369. Pause multiple torrents.
  370.  
  371. :param infohash_list: Single or list() of infohashes.
  372. """
  373. data = self._process_infohash_list(infohash_list)
  374. return self._post('command/pauseAll', data=data)
  375.  
  376. def set_label(self, infohash_list, label):
  377. """
  378. Set the label on multiple torrents.
  379. IMPORTANT: OLD API method, kept as it is to avoid breaking stuffs.
  380.  
  381. :param infohash_list: Single or list() of infohashes.
  382. """
  383. data = self._process_infohash_list(infohash_list)
  384. data['label'] = label
  385. return self._post('command/setLabel', data=data)
  386.  
  387. def set_category(self, infohash_list, category):
  388. """
  389. Set the category on multiple torrents.
  390.  
  391. :param infohash_list: Single or list() of infohashes.
  392. """
  393. data = self._process_infohash_list(infohash_list)
  394. data['category'] = category
  395. return self._post('command/setCategory', data=data)
  396.  
  397. def resume(self, infohash):
  398. """
  399. Resume a paused torrent.
  400.  
  401. :param infohash: INFO HASH of torrent.
  402. """
  403. return self._post('command/resume', data={'hash': infohash.lower()})
  404.  
  405. def resume_all(self):
  406. """
  407. Resume all torrents.
  408. """
  409. return self._get('command/resumeAll')
  410.  
  411. def resume_multiple(self, infohash_list):
  412. """
  413. Resume multiple paused torrents.
  414.  
  415. :param infohash_list: Single or list() of infohashes.
  416. """
  417. data = self._process_infohash_list(infohash_list)
  418. return self._post('command/resumeAll', data=data)
  419.  
  420. def delete(self, infohash_list):
  421. """
  422. Delete torrents.
  423.  
  424. :param infohash_list: Single or list() of infohashes.
  425. """
  426. data = self._process_infohash_list(infohash_list)
  427. return self._post('command/delete', data=data)
  428.  
  429. def delete_permanently(self, infohash_list):
  430. """
  431. Permanently delete torrents.
  432.  
  433. :param infohash_list: Single or list() of infohashes.
  434. """
  435. data = self._process_infohash_list(infohash_list)
  436. return self._post('command/deletePerm', data=data)
  437.  
  438. def recheck(self, infohash_list):
  439. """
  440. Recheck torrents.
  441.  
  442. :param infohash_list: Single or list() of infohashes.
  443. """
  444. data = self._process_infohash_list(infohash_list)
  445. return self._post('command/recheck', data=data)
  446.  
  447. def increase_priority(self, infohash_list):
  448. """
  449. Increase priority of torrents.
  450.  
  451. :param infohash_list: Single or list() of infohashes.
  452. """
  453. data = self._process_infohash_list(infohash_list)
  454. return self._post('command/increasePrio', data=data)
  455.  
  456. def decrease_priority(self, infohash_list):
  457. """
  458. Decrease priority of torrents.
  459.  
  460. :param infohash_list: Single or list() of infohashes.
  461. """
  462. data = self._process_infohash_list(infohash_list)
  463. return self._post('command/decreasePrio', data=data)
  464.  
  465. def set_max_priority(self, infohash_list):
  466. """
  467. Set torrents to maximum priority level.
  468.  
  469. :param infohash_list: Single or list() of infohashes.
  470. """
  471. data = self._process_infohash_list(infohash_list)
  472. return self._post('command/topPrio', data=data)
  473.  
  474. def set_min_priority(self, infohash_list):
  475. """
  476. Set torrents to minimum priority level.
  477.  
  478. :param infohash_list: Single or list() of infohashes.
  479. """
  480. data = self._process_infohash_list(infohash_list)
  481. return self._post('command/bottomPrio', data=data)
  482.  
  483. def set_file_priority(self, infohash, file_id, priority):
  484. """
  485. Set file of a torrent to a supplied priority level.
  486.  
  487. :param infohash: INFO HASH of torrent.
  488. :param file_id: ID of the file to set priority.
  489. :param priority: Priority level of the file.
  490. """
  491. if priority not in [0, 1, 2, 7]:
  492. raise ValueError("Invalid priority, refer WEB-UI docs for info.")
  493. elif not isinstance(file_id, int):
  494. raise TypeError("File ID must be an int")
  495.  
  496. data = {'hash': infohash.lower(),
  497. 'id': file_id,
  498. 'priority': priority}
  499.  
  500. return self._post('command/setFilePrio', data=data)
  501.  
  502. # Get-set global download and upload speed limits.
  503.  
  504. def get_global_download_limit(self):
  505. """
  506. Get global download speed limit.
  507. """
  508. return self._get('command/getGlobalDlLimit')
  509.  
  510. def set_global_download_limit(self, limit):
  511. """
  512. Set global download speed limit.
  513.  
  514. :param limit: Speed limit in bytes.
  515. """
  516. return self._post('command/setGlobalDlLimit', data={'limit': limit})
  517.  
  518. global_download_limit = property(get_global_download_limit,
  519. set_global_download_limit)
  520.  
  521. def get_global_upload_limit(self):
  522. """
  523. Get global upload speed limit.
  524. """
  525. return self._get('command/getGlobalUpLimit')
  526.  
  527. def set_global_upload_limit(self, limit):
  528. """
  529. Set global upload speed limit.
  530.  
  531. :param limit: Speed limit in bytes.
  532. """
  533. return self._post('command/setGlobalUpLimit', data={'limit': limit})
  534.  
  535. global_upload_limit = property(get_global_upload_limit,
  536. set_global_upload_limit)
  537.  
  538. # Get-set download and upload speed limits of the torrents.
  539. def get_torrent_download_limit(self, infohash_list):
  540. """
  541. Get download speed limit of the supplied torrents.
  542.  
  543. :param infohash_list: Single or list() of infohashes.
  544. """
  545. data = self._process_infohash_list(infohash_list)
  546. return self._post('command/getTorrentsDlLimit', data=data)
  547.  
  548. def set_torrent_download_limit(self, infohash_list, limit):
  549. """
  550. Set download speed limit of the supplied torrents.
  551.  
  552. :param infohash_list: Single or list() of infohashes.
  553. :param limit: Speed limit in bytes.
  554. """
  555. data = self._process_infohash_list(infohash_list)
  556. data.update({'limit': limit})
  557. return self._post('command/setTorrentsDlLimit', data=data)
  558.  
  559. def get_torrent_upload_limit(self, infohash_list):
  560. """
  561. Get upoload speed limit of the supplied torrents.
  562.  
  563. :param infohash_list: Single or list() of infohashes.
  564. """
  565. data = self._process_infohash_list(infohash_list)
  566. return self._post('command/getTorrentsUpLimit', data=data)
  567.  
  568. def set_torrent_upload_limit(self, infohash_list, limit):
  569. """
  570. Set upload speed limit of the supplied torrents.
  571.  
  572. :param infohash_list: Single or list() of infohashes.
  573. :param limit: Speed limit in bytes.
  574. """
  575. data = self._process_infohash_list(infohash_list)
  576. data.update({'limit': limit})
  577. return self._post('command/setTorrentsUpLimit', data=data)
  578.  
  579. # setting preferences
  580. def set_preferences(self, **kwargs):
  581. """
  582. Set preferences of qBittorrent.
  583. Read all possible preferences @ http://git.io/vEgDQ
  584.  
  585. :param kwargs: set preferences in kwargs form.
  586. """
  587. json_data = "json={}".format(json.dumps(kwargs))
  588. headers = {'content-type': 'application/x-www-form-urlencoded'}
  589. return self._post('command/setPreferences', data=json_data,
  590. headers=headers)
  591.  
  592. def get_alternative_speed_status(self):
  593. """
  594. Get Alternative speed limits. (1/0)
  595. """
  596. return self._get('command/alternativeSpeedLimitsEnabled')
  597.  
  598. alternative_speed_status = property(get_alternative_speed_status)
  599.  
  600. def toggle_alternative_speed(self):
  601. """
  602. Toggle alternative speed limits.
  603. """
  604. return self._get('command/toggleAlternativeSpeedLimits')
  605.  
  606. def toggle_sequential_download(self, infohash_list):
  607. """
  608. Toggle sequential download in supplied torrents.
  609.  
  610. :param infohash_list: Single or list() of infohashes.
  611. """
  612. data = self._process_infohash_list(infohash_list)
  613. return self._post('command/toggleSequentialDownload', data=data)
  614.  
  615. def toggle_first_last_piece_priority(self, infohash_list):
  616. """
  617. Toggle first/last piece priority of supplied torrents.
  618.  
  619. :param infohash_list: Single or list() of infohashes.
  620. """
  621. data = self._process_infohash_list(infohash_list)
  622. return self._post('command/toggleFirstLastPiecePrio', data=data)
  623.  
  624. def force_start(self, infohash_list, value=True):
  625. """
  626. Force start selected torrents.
  627.  
  628. :param infohash_list: Single or list() of infohashes.
  629. :param value: Force start value (bool)
  630. """
  631. data = self._process_infohash_list(infohash_list)
  632. data.update({'value': json.dumps(value)})
  633. return self._post('command/setForceStart', data=data)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement