Advertisement
Guest User

Untitled

a guest
Aug 7th, 2016
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.76 KB | None | 0 0
  1. from base64 import b16encode, b32decode
  2. from hashlib import sha1
  3. from datetime import timedelta
  4. import os
  5. import re
  6. from bencode import bencode, bdecode
  7. from couchpotato.core._base.downloader.main import DownloaderBase, ReleaseDownloadList
  8. from couchpotato.core.helpers.encoding import sp
  9. from couchpotato.core.helpers.variable import cleanHost
  10. from couchpotato.core.logger import CPLog
  11. from qbittorrent.client import QBittorrentClient
  12.  
  13.  
  14. log = CPLog(__name__)
  15.  
  16. autoload = 'qBittorrent'
  17.  
  18.  
  19. class qBittorrent(DownloaderBase):
  20.  
  21.     protocol = ['torrent', 'torrent_magnet']
  22.     qb = None
  23.  
  24.     def __init__(self):
  25.         super(qBittorrent, self).__init__()
  26.  
  27.     def connect(self, reconnect = False):
  28.         if not reconnect and self.qb is not None:
  29.             return self.qb
  30.  
  31.         url = cleanHost(self.conf('host'), protocol = True, ssl = False)
  32.        
  33.         if self.conf('username') and self.conf('password'):
  34.             self.qb = QBittorrentClient(url)
  35.             self.qb.login(username=self.conf('username'),password=self.conf('password'))
  36.         else:
  37.             self.qb = QBittorrentClient(url)
  38.  
  39.         return self.qb
  40.  
  41.     def test(self):
  42.         """ Check if connection works
  43.        :return: bool
  44.        """
  45.        
  46.         self.connect(True)
  47.        
  48.         return self.qb._is_authenticated
  49.  
  50.     def download(self, data = None, media = None, filedata = None):        
  51.         """ Send a torrent/nzb file to the downloader
  52.  
  53.        :param data: dict returned from provider
  54.            Contains the release information
  55.        :param media: media dict with information
  56.            Used for creating the filename when possible
  57.        :param filedata: downloaded torrent/nzb filedata
  58.            The file gets downloaded in the searcher and send to this function
  59.            This is done to have failed checking before using the downloader, so the downloader
  60.            doesn't need to worry about that
  61.        :return: boolean
  62.            One faile returns false, but the downloaded should log his own errors
  63.        """
  64.  
  65.         if not media: media = {}
  66.         if not data: data = {}
  67.  
  68.         log.debug('Sending "%s" to qBittorrent.', (data.get('name')))
  69.  
  70.         if not self.connect():
  71.             return False
  72.  
  73.         if not filedata and data.get('protocol') == 'torrent':
  74.             log.error('Failed sending torrent, no data')
  75.             return False
  76.  
  77.         if data.get('protocol') == 'torrent_magnet':       
  78.             try:
  79.                 #filedata is false, but we have a magnet link, just use it
  80.                 filedata=data['url']
  81.                 self.qb.download_from_link(filedata,label=self.conf('label'))
  82.                 torrent_hash=re.search("urn:btih:(.*?)&",filedata).group(1)
  83.                 return self.downloadReturnId(torrent_hash)
  84.             except Exception as e:
  85.                 log.error('Failed to send magnet to qBittorrent: %s', e)
  86.                 return False
  87.  
  88.         info = bdecode(filedata)["info"]
  89.         torrent_hash = sha1(bencode(info)).hexdigest()
  90.  
  91.         # Convert base 32 to hex
  92.         if len(torrent_hash) == 32:
  93.             torrent_hash = b16encode(b32decode(torrent_hash))
  94.  
  95.         # Send request to qBittorrent
  96.         try:
  97.             self.qb.download_from_file(filedata, label=self.conf('label'))
  98.  
  99.             return self.downloadReturnId(torrent_hash)
  100.         except Exception as e:
  101.             log.error('Failed to send torrent to qBittorrent: %s', e)
  102.             return False
  103.  
  104.     def getTorrentStatus(self, torrent):
  105.  
  106.         if torrent['state'] in ('uploading', 'queuedUP', 'stalledUP'):
  107.             return 'seeding'
  108.  
  109.         if torrent['progress'] == 1:
  110.             return 'completed'
  111.  
  112.         return 'busy'
  113.  
  114.     def getAllDownloadStatus(self, ids):
  115.         """ Get status of all active downloads
  116.  
  117.        :param ids: list of (mixed) downloader ids
  118.            Used to match the releases for this downloader as there could be
  119.            other downloaders active that it should ignore
  120.        :return: list of releases
  121.        """
  122.  
  123.         log.debug('Checking qBittorrent download status.')
  124.  
  125.         if not self.connect():
  126.             return []
  127.  
  128.         try:
  129.             torrents = self.qb.torrents(status='all', label=self.conf('label'))
  130.  
  131.             release_downloads = ReleaseDownloadList(self)
  132.  
  133.             for torrent in torrents:
  134.                 if torrent['hash'] in ids:
  135.                     torrent_filelist = self.qb.get_torrent_files(torrent['hash'])
  136.  
  137.                     torrent_files = []
  138.                     torrent_dir = os.path.join(torrent['save_path'], torrent['name'])
  139.  
  140.                     if os.path.isdir(torrent_dir):
  141.                         torrent['save_path'] = torrent_dir
  142.  
  143.                     if len(torrent_filelist) > 1 and os.path.isdir(torrent_dir): # multi file torrent, path.isdir check makes sure we're not in the root download folder
  144.                         for root, _, files in os.walk(torrent['save_path']):
  145.                             for f in files:
  146.                                 torrent_files.append(sp(os.path.join(root, f)))
  147.  
  148.                     else: # multi or single file placed directly in torrent.save_path
  149.                         for f in torrent_filelist:
  150.                             file_path = os.path.join(torrent['save_path'], f['name'])
  151.                             if os.path.isfile(file_path):
  152.                                 torrent_files.append(sp(file_path))
  153.  
  154.                     release_downloads.append({
  155.                         'id': torrent['hash'],
  156.                         'name': torrent['name'],
  157.                         'status': self.getTorrentStatus(torrent),
  158.                         'seed_ratio': torrent['ratio'],
  159.                         'original_status': torrent['state'],
  160.                         'timeleft': str(timedelta(seconds = torrent['eta'])),
  161.                         'folder': sp(torrent['save_path']),
  162.                         'files': torrent_files
  163.                     })
  164.  
  165.             return release_downloads
  166.  
  167.         except Exception as e:
  168.             log.error('Failed to get status from qBittorrent: %s', e)
  169.             return []
  170.  
  171.     def pause(self, release_download, pause = True):
  172.         if not self.connect():
  173.             return False
  174.  
  175.         torrent = self.qb.get_torrent(release_download['id'])
  176.         if torrent is None:
  177.             return False
  178.  
  179.         if pause:
  180.             return self.qb.pause(release_download['id'])
  181.         return self.qb.resume(release_download['id'])
  182.  
  183.     def removeFailed(self, release_download):
  184.         log.info('%s failed downloading, deleting...', release_download['name'])
  185.         return self.processComplete(release_download, delete_files = True)
  186.  
  187.     def processComplete(self, release_download, delete_files):
  188.         log.debug('Requesting qBittorrent to remove the torrent %s%s.',
  189.                   (release_download['name'], ' and cleanup the downloaded files' if delete_files else ''))
  190.  
  191.         if not self.connect():
  192.             return False
  193.  
  194.         torrent = self.qb.get_torrent(release_download['id'])
  195.  
  196.         if torrent is None:
  197.             return False
  198.  
  199.         if delete_files:
  200.             self.qb.delete_permanently(release_download['id']) # deletes torrent with data
  201.         else:
  202.             self.qb.delete(release_download['id']) # just removes the torrent, doesn't delete data
  203.  
  204.         return True
  205.  
  206.  
  207. config = [{
  208.     'name': 'qbittorrent',
  209.     'groups': [
  210.         {
  211.             'tab': 'downloaders',
  212.             'list': 'download_providers',
  213.             'name': 'qbittorrent',
  214.             'label': 'qBittorrent',
  215.             'description': 'Use <a href="http://www.qbittorrent.org/" target="_blank">qBittorrent</a> to download torrents.',
  216.             'wizard': True,
  217.             'options': [
  218.                 {
  219.                     'name': 'enabled',
  220.                     'default': 0,
  221.                     'type': 'enabler',
  222.                     'radio_group': 'torrent',
  223.                 },
  224.                 {
  225.                     'name': 'host',
  226.                     'default': 'http://localhost:8080/',
  227.                     'description': 'RPC Communication URI. Usually <strong>http://localhost:8080/</strong>'
  228.                 },
  229.                 {
  230.                     'name': 'username',
  231.                 },
  232.                 {
  233.                     'name': 'password',
  234.                     'type': 'password',
  235.                 },
  236.                 {
  237.                     'name': 'label',
  238.                     'label': 'Torrent Label',
  239.                     'default': 'couchpotato',
  240.                 },
  241.                 {
  242.                     'name': 'remove_complete',
  243.                     'label': 'Remove torrent',
  244.                     'default': False,
  245.                     'advanced': True,
  246.                     'type': 'bool',
  247.                     'description': 'Remove the torrent after it finishes seeding.',
  248.                 },
  249.                 {
  250.                     'name': 'delete_files',
  251.                     'label': 'Remove files',
  252.                     'default': True,
  253.                     'type': 'bool',
  254.                     'advanced': True,
  255.                     'description': 'Also remove the leftover files.',
  256.                 },
  257.                 {
  258.                     'name': 'paused',
  259.                     'type': 'bool',
  260.                     'advanced': True,
  261.                     'default': False,
  262.                     'description': 'Add the torrent paused.',
  263.                 },
  264.                 {
  265.                     'name': 'manual',
  266.                     'default': 0,
  267.                     'type': 'bool',
  268.                     'advanced': True,
  269.                     'description': 'Disable this downloader for automated searches, but use it when I manually send a release.',
  270.                 },
  271.             ],
  272.         }
  273.     ],
  274. }]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement