def __init__(self, config, persistence):
     self.conn = Client(config.address,
                        port=config.port,
                        user=config.user,
                        password=config.password)
     self.secret = config.secret
     self.persistence = persistence
Example #2
0
	def __init__(self, session):
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)

		try:
			self.transmission = Client(
				address = config.plugins.emission.hostname.value,
				port = config.plugins.emission.port.value,
				user = config.plugins.emission.username.value,
				password = config.plugins.emission.password.value
			)
		except TransmissionError as te:
			self.transmission = None

		self["SetupActions"] = HelpableActionMap(self, "SetupActions",
		{
			"ok": (self.ok, _("show details")),
			"cancel": (self.close, _("close")),
		})

		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"green": (self.bandwidth, _("open bandwidth settings")),
			"yellow": (self.prevlist, _("show previous list")),
			"blue": (self.nextlist, _("show next list")),
		})

		self["MenuActions"] = HelpableActionMap(self, "MenuActions",
		{
			"menu": (self.menu, _("open context menu")),
		})

		self["key_red"] = StaticText(_("Close"))
		self["key_green"] = StaticText(_("Bandwidth"))
		self["key_yellow"] = StaticText("")
		self["key_blue"] = StaticText("")

		self["all_text"] = StaticText(_("All"))
		self["downloading_text"] = StaticText(_("DL"))
		self["seeding_text"] = StaticText(_("UL"))
		self["upspeed"] = StaticText("")
		self["downspeed"] = StaticText("")
		self["torrents"] = StaticText("")

		self["all_sel"] = Pixmap()
		self["downloading_sel"] = Pixmap()
		self["seeding_sel"] = Pixmap()

		self['list'] = List([])

		self.list_type = config.plugins.emission.last_tab.value
		self.sort_type = config.plugins.emission.last_sort.value
		self.showHideSetTextMagic()

		self.timer = eTimer()
		self.timer.callback.append(self.updateList)
		self.timer.start(0, 1)
Example #3
0
def reader():
    """Read data and dispatch"""
    client = Client(user=USERNAME, password=PASSWORD)
    stats = client.session_stats()

    dispatch_value('transmission_speed', [
        stats.cumulative_stats['downloadedBytes'],
        stats.cumulative_stats['uploadedBytes']
    ])

    dispatch_value('transmission_count',
                   [stats.activeTorrentCount, stats.pausedTorrentCount])
def reader():
    """Read data and dispatch"""
    client = Client(user=USERNAME, password=PASSWORD)
    stats = client.session_stats()

    dispatch_value('transmission_speed', [
        stats.cumulative_stats['downloadedBytes'],
        stats.cumulative_stats['uploadedBytes']
    ])

    dispatch_value('transmission_count', [
        stats.activeTorrentCount,
        stats.pausedTorrentCount
    ])
Example #5
0
 def connect(self):
     try:
         self.client = Client(address=self.host,
                              port=self.port,
                              user=self.user,
                              password=self.password)
     except TransmissionError as err:
         if err.original.code == 111:
             self.log.error(
                 "Failed to connect to transmission-daemon, is it running?")
         elif err.original.code == 113:
             self.log.error("No route to host")
         else:
             self.log.exception("Error connecting to transmission server")
         raise
Example #6
0
 def configureCallback(self):
     try:
         self.transmission = Client(
             address=config.plugins.emission.hostname.value,
             port=config.plugins.emission.port.value,
             user=config.plugins.emission.username.value,
             password=config.plugins.emission.password.value)
     except TransmissionError as te:
         self.transmission = None
         self.session.open(
             MessageBox,
             _("Error communicating with transmission-daemon: %s.") % (te),
             type=MessageBox.TYPE_ERROR,
             timeout=5)
     else:
         self.updateList()
Example #7
0
    def connect(self, host, username, password):
        if self.conn is not None:
            return self.conn

        if not host:
            return False
        try:
            if username and password:
                self.conn = Client(host, user=username, password=password)
            else:
                self.conn = Client(host)
        except:
            logger.error('Could not connect to %h' % host)
            return False

        return self.conn
Example #8
0
def createClient(*args, **kwargs):
    test_name = None
    if 'test_name' in kwargs:
        test_name = kwargs['test_name']
        del kwargs['test_name']
    kwargs['http_handler'] = TestHTTPHandler(test_name)
    return Client(*args, **kwargs)
Example #9
0
class transmission_client:
    def __init__(self, host, port, username, password):
        self.client = Client(host, port=port, user=username, password=password)
        self.torrent = os.path.join(
            os.path.expanduser('~'), 'Downloads',
            'E5340FB5C061E4E53618F41B48D7E1CEA445BB02.torrent')
        print(self.torrent)

    def download(self):
        try:
            self.client.add_torrent(self.torrent,
                                    download_dir='/home/goku/Documents')
            print(
                'Add torrent into the download queue, the file will be saved at '
            )
        except ImportError:
            pass
Example #10
0
 def connect(self):
     try:
         self.client = Client(address=self.host, port=self.port, user=self.user, password=self.password)
     except TransmissionError as err:
         if err.original.code == 111:
             self.log.error("Failed to connect to transmission-daemon, is it running?")
         else:
             self.log.exception("Error connecting to transmission server")
 def connect(self, address, port, user, password):
     try:
         self.client = TClient(address, port, user, password)
     except TransmissionError as err:
         raise BTCexception(" {} TransmissionError {}".format(address, err))
     except socket.timeout as err:
         raise BTCexception(" {} Socket error {}".format(address, err))
     else:
         self.dict_client['name'] = address
	def __init__(self, session):
		Screen.__init__(self, session)
		HelpableScreen.__init__(self)

		try:
			self.transmission = Client(
				address = config.plugins.emission.hostname.value,
				port = config.plugins.emission.port.value,
				user = config.plugins.emission.username.value,
				password = config.plugins.emission.password.value
			)
		except TransmissionError as te:
			self.transmission = None

		self["SetupActions"] = HelpableActionMap(self, "SetupActions",
		{
			"ok": (self.ok, _("show details")),
			"cancel": (self.close, _("close")),
		})

		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
		{
			"green": (self.bandwidth, _("open bandwidth settings")),
			"yellow": (self.prevlist, _("show previous list")),
			"blue": (self.nextlist, _("show next list")),
		})

		self["MenuActions"] = HelpableActionMap(self, "MenuActions",
		{
			"menu": (self.menu, _("open context menu")),
		})

		self["key_red"] = StaticText(_("Close"))
		self["key_green"] = StaticText(_("Bandwidth"))
		self["key_yellow"] = StaticText("")
		self["key_blue"] = StaticText("")

		self["all_text"] = StaticText(_("All"))
		self["downloading_text"] = StaticText(_("DL"))
		self["seeding_text"] = StaticText(_("UL"))
		self["upspeed"] = StaticText("")
		self["downspeed"] = StaticText("")
		self["torrents"] = StaticText("")

		self["all_sel"] = Pixmap()
		self["downloading_sel"] = Pixmap()
		self["seeding_sel"] = Pixmap()

		self['list'] = List([])

		self.list_type = config.plugins.emission.last_tab.value
		self.sort_type = config.plugins.emission.last_sort.value
		self.showHideSetTextMagic()

		self.timer = eTimer()
		self.timer_conn = self.timer.timeout.connect(self.updateList)
		self.timer.start(0, 1)
Example #13
0
    def connect(self, host, username, password):
        if self.conn is not None:
            return self.conn

        if not host:
            return False
        try:
            if username and password:
                self.conn = Client(
                    host,
                    user=username,
                    password=password
                )
            else:
                self.conn = Client(host)
        except:
            logger.error('Could not connect to %h' % host)
            return False

        return self.conn
Example #14
0
def run(config, logger):
    logger.debug('Daemonized successfully')
    c = Client(address=config['address'],user=config['username'],password=config['password'])
    logger.debug('Connected to transmission successfully')
    while True:
        logger.debug('Starting run')
        torrents=c.info()
        torrentnames = map(lambda x: x.fields['name'], torrents.values())

        todelete = find_torrents_to_delete(torrents, logger, config['max_days'], config['series_max_days'])
        toadd = find_torrents_to_add(config['feeds'], torrentnames, logger)
        if config['dry_run'] == False:
            # Let's delete all old torrents
            if len(todelete) >0:
                c.remove(todelete, delete_data=True, timeout=config['timeout'])
            if len(toadd) > 0:
                for a in toadd:
                    try:
                        c.add_uri(a)
                    except (TransmissionError, URLError) as e:
                        if isinstance(e, TransmissionError):
                            e.message = e._message
                        if isinstance(e, URLError):
                            e.message = e.reason
                        if e.message == 'Query failed with result "duplicate torrent".':
                            logger.info('Torrent: %s already downloading' % (a))
                        else:
                            logger.warning('Failed to add: %s. Reason: %s' % (a, e.message))

        logger.debug('Ending run')
        time.sleep(config['interval'])
Example #15
0
def simplerss_update_callback(id=None):
    try:
        from Plugins.Extensions.SimpleRSS.plugin import rssPoller
    except ImportError as e:
        pass  # should not happen since the poller has to be active for us to be called :-)
    else:
        errors = 0
        # we only check the "new items" feed currently since we do not keep track of the files we downloaded
        if id is None:
            client = None
            for item in rssPoller.newItemFeed.history:
                for file in item[3]:
                    if file.mimetype == "application/x-bittorrent":
                        if client is None:
                            client = Client(
                                address=config.plugins.emission.hostname.value,
                                port=config.plugins.emission.port.value,
                                user=config.plugins.emission.username.value,
                                password=config.plugins.emission.password.value
                            )
                        try:
                            # XXX: we might want to run this in the background cause this might block...
                            client.add_url(file.path)
                        except TransmissionError:
                            errors += 1

        # Inform the user if an error occured
        # XXX: we could also either open a notification per torrent (success/failure)
        #      or create a custom screen which gives this information in a
        #      more understandable and less annyoing way (e.g. a list of torrent
        #      names with an X or hook symbol next to it...)
        if errors > 0:
            from Tools.Notifications import AddPopup
            from Screens.MessageBox import MessageBox

            AddPopup(
                _("Failed to add %d torrents.") % (errors),
                MessageBox.TYPE_WARNING, 5, NOTIFICATIONID)
Example #16
0
def filescan_open(item, session, **kwargs):
    client = Client(address=config.plugins.emission.hostname.value,
                    port=config.plugins.emission.port.value,
                    user=config.plugins.emission.username.value,
                    password=config.plugins.emission.password.value)

    added = 0
    errors = 0

    # XXX: a list of failed/added torrents would be nice, see comment in simplerss_update_callback
    for each in item:
        try:
            if client.add_url(each):
                added += 1
        except TransmissionError:
            errors += 1

    from Screens.MessageBox import MessageBox

    session.open(MessageBox,
                 _("%d Torrents(s) were scheduled for download, %d failed.") %
                 (added, errors),
                 type=MessageBox.TYPE_INFO,
                 timeout=5)
Example #17
0
class TransmissionBroker:
    def __init__(self, config, persistence):
        self.conn = Client(config.address,
                           port=config.port,
                           user=config.user,
                           password=config.password)
        self.secret = config.secret
        self.persistence = persistence

    @staticmethod
    def pretty_torrents_list(torrents):
        info_list = list()
        for torrent in torrents:
            info_list.append('%s: %s, %s : %d%%' %
                             (torrent.id, torrent.name, torrent.status,
                              torrent.percentDone * 100))
        return '\n'.join(info_list)

    def retrieve_list(self, chat_id):
        torrents = self.conn.get_torrents()

        return TransmissionBroker.pretty_torrents_list(torrents)

    def add_torrent(self, chat_id, url):
        self.conn.add_torrent(url)

    def remove_torrent(self, chat_id, torrent_ids):
        # Check is not embedded to transmissionrpc module, so we have to do it ourselves
        missing_torrents = list()
        torrents = self.conn.get_torrents()
        for tid in torrent_ids:
            id_found = False
            for torrent in torrents:
                if tid == torrent.id:
                    id_found = True
                    break
            if not id_found:
                missing_torrents.append(tid)

        if len(missing_torrents) > 0:
            raise TransmissionError('Torrents %s not found' % missing_torrents)

        self.conn.remove_torrent(torrent_ids)

    def check_chat_authorization(self, chat_id):
        if not self.persistence.check_chat_id(chat_id):
            raise NotAuthorizedChatException()

    def authorize_chat(self, chat_id, secret):
        if self.secret == secret:
            self.persistence.add_chat_id(chat_id)
            return True
        else:
            return False
Example #18
0
class TransmissionClient(client.TorrentClient):
    _config_key = "transmission"

    def __init__(self, host=None, port=None, user=None, password=None):
        if not host:
            host = config.get_default(self._config_key, "host", "localhost")
        self.host = host
        if not port:
            port = config.get_default(self._config_key, "port", DEFAULT_PORT, int)
        self.port = port
        if not user:
            user = config.get_default(self._config_key, "user", None)
        self.user = user
        if not password:
            password = config.get_default(self._config_key, "password", None)
        self.password = password
        self.connect()

    def client_version(self):
        return "Transmission XX"

    def connect(self):
        try:
            self.client = Client(self.host, port=self.port, user=self.user, password=self.password)
        except TransmissionError as err:
            if err.original.code == 111:
                logger.error("Failed to connect to transmission-daemon, is it running?")
            else:
                logger.exception("Error connecting to transmission server")

    def add(self, data, download_dir=None):
        try:
            encoded_data = b64encode(data)
            res = self.client.add(encoded_data, download_dir=download_dir)
        except TransmissionError, err:
            try:
                msg = err._message
            except AttributeError:
                msg = err.message
            if "duplicate torrent" in msg:
                logger.warning("Tried to add duplicate torrent file")
                return True
            logger.exception(err)
            return False

        return res
 def configureCallback(self):
     try:
         self.transmission = Client(
             address=config.plugins.emission.hostname.value,
             port=config.plugins.emission.port.value,
             user=config.plugins.emission.username.value,
             password=config.plugins.emission.password.value,
         )
     except TransmissionError as te:
         self.transmission = None
         self.session.open(
             MessageBox,
             _("Error communicating with transmission-daemon: %s.") % (te),
             type=MessageBox.TYPE_ERROR,
             timeout=5,
         )
     else:
         self.updateList()
Example #20
0
class TransmissionBroker:
    def __init__(self):
        print(f"user={TS_USER}, password={PASSWORD}")
        self.conn = Client(ADDRESS, PORT, user=TS_USER, password=PASSWORD)
        self.persistence = PERSISTENCE_FILE

    @staticmethod
    def pretty_torrents_list(torrents):
        info_list = []
        for torrent in torrents:
            percent = torrent.percentDone * 100
            info_list.append(
                f'{torrent.id}:{torrent.name}, {torrent.status}:{percent}'
            )
        return '\n'.join(info_list)

    def retrieve_list(self, chat_id):
        torrents = self.conn.get_torrents()
        return TransmissionBroker.pretty_torrents_list(torrents)

    def add_torrent(self, chat_id, url):
        self.conn.add_torrent(url)

    def remove_torrent(self, chat_id, torrent_ids):
        # Check is not embedded to transmissionrpc module, so we have to do it ourselves
        missing_torrents = list()
        torrents = self.conn.get_torrents()
        for tid in torrent_ids:
            id_found = False
            for torrent in torrents:
                if tid == torrent.id:
                    id_found = True
                    break
            if not id_found:
                missing_torrents.append(tid)

        if len(missing_torrents) > 0:
            raise TransmissionError(f'Torrents {missing_torrents} not found')

        self.conn.remove_torrent(torrent_ids)
class TorC:
    MANAGE_ACTION_INIT = 1
    MANAGE_ACTION_CLEAN = 2

    TOR_TYPE_SINGLE = 1
    TOR_TYPE_SERIES = 2
 
    Videos = ["mp4", "mkv", "avi", "mpeg", "mpg", "mov", "mov", "wmv"]

    def __init__(self, user, password):
        #TODO: host/port/url
        self.client = Client(user=user, password=password)
        self.torrent_props = {}
        for t in self.client.get_torrents():
            self.sort_by_episodes(t)

    def get_files(self, torrent = None):
        if not torrent:
            ret = []
            for t, tinfo in self.client.get_files().items():
                for fid, finfo in tinfo.items():
                    if finfo["name"].split(".")[-1] in TorC.Videos:
                        ret.append(finfo)
            return ret
        return [x for x in list(torrent.files().values()) if x["name"].split(".")[-1] in TorC.Videos ]

    def download_dir(self):
        return self.client.session.download_dir

    def add_magnet(self, magnet):
        torrent = self.client.add_torrent(magnet)
        while not torrent.files():
            time.sleep(.1)
            torrent.update()
        torrent.uploadLimit = 20
        torrent.stop()
        torrent.update()
        self.manage_files(torrent, TorC.MANAGE_ACTION_INIT)

        return torrent

    def status(self):
        ret = []
        for t in self.client.get_torrents():
            t.update()
            tor_data = [t.name, float(t.rateDownload), []]
            if t.hashString not in self.torrent_props:
                tor_data[2].append({
                            "name": t.name,
                            "completed": float(t.percentDone)*100})
            else:
                files_list = t.files()
                for f in self.torrent_props[t.hashString]["episodes"]:
                    fdict = files_list[f[0]]
                    if fdict["size"] == fdict["completed"] or not fdict["selected"]: continue
                    tor_data[2].append({"name": fdict["name"],
                        "completed": float(100*fdict["completed"])/fdict["size"]}),

            ret.append(tor_data)
        return ret

    def clean(self):
        toremove = []
        for t in self.client.get_torrents():
            t.update()
            if t.hashString not in self.torrent_props:
                if t.status == "seeding": toremove.append(t.id)
            else:
                if not self.start_using_priority(t): toremove.append(t.id)
        if toremove:
            self.client.remove_torrent(toremove)

    def init_torrent_download(self, torrent):
        files_list = torrent.files()
        self.torrent_props[torrent.hashString]  = {}
        #Skip all files:
        skip_files = {torrent.id : {}}
        for idx, f in enumerate(files_list):
            skip_files[torrent.id][idx] = {"selected": False}
        self.client.set_files(skip_files)

        video_files = { k:v for k,v in files_list.items() if v["name"].split(".")[-1].lower() in TorC.Videos }
        if len(video_files) > 1:
            sizesorted_files_list = sorted(video_files.values(), key=lambda x: x["size"], reverse=True)
            if sizesorted_files_list[0]["size"] > 5* sizesorted_files_list[1]["size"]:
                video_files = [sizesorted_files_list[0]]

        self.torrent_props[torrent.hashString]["episodes"] = self.sort_by_episodes(torrent)
        if len(video_files) > 2: #Type is series
            self.sort_by_episodes(torrent)
            self.start_using_priority(torrent)
        else:
            start_files = {}
            for idx,f in video_files.items():
                start_files[torrent.id] = {idx:{"selected": True}}
            self.client.set_files(start_files)




    def start_using_priority(self, torrent, startat=0):
        #TODO startat function
        files_list = torrent.files()
        pos = startat
        f = files_list[self.torrent_props[torrent.hashString]["episodes"][pos][0]]
        while  pos < len(self.torrent_props[torrent.hashString]["episodes"]) and f["size"] == f["completed"]:
            pos += 1
            try:
                f = files_list[self.torrent_props[torrent.hashString]["episodes"][pos][0]]
            except IndexError: pass
        if pos == len(self.torrent_props[torrent.hashString]["episodes"]):
            return False
        mod_files = {torrent.id:{self.torrent_props[torrent.hashString]["episodes"][pos][0]:{"selected": True, "priority": "high"}}}

        if pos < len(self.torrent_props[torrent.hashString]["episodes"]) - 1:
            mod_files[torrent.id][self.torrent_props[torrent.hashString]["episodes"][pos+1][0]] = {"priority":"normal", "selected": True}
        self.client.set_files(mod_files)
        return True

    def manage_files(self, torrent, action):
        files_list = torrent.files()
        if action == TorC.MANAGE_ACTION_INIT:
            self.init_torrent_download(torrent)

    def sort_by_episodes(self, torrent):
        files_list = torrent.files()
        video_files = { k:v for k,v in files_list.items() if v["name"].split(".")[-1].lower() in TorC.Videos }
        p = re.compile("S\d+E\d+", re.IGNORECASE)
        episodes = { re.search(p, v["name"]).group().upper(): (k, v["name"]) for k,v in video_files.items()}
        self.torrent_props[torrent.hashString]= {"episodes": [y[1] for y in sorted(episodes.items(), key = lambda x: x[0])]}

    def start_all(self):
        for t in self.client.get_torrents(): t.start()

    def stop_all(self):
        for t in self.client.get_torrents(): t.stop()
 def __init__(self, user, password):
     #TODO: host/port/url
     self.client = Client(user=user, password=password)
     self.torrent_props = {}
     for t in self.client.get_torrents():
         self.sort_by_episodes(t)
Example #23
0
class TransmissionClient(client.TorrentClient):
    """ Backend implementation for transmission """

    config_key = "client_transmission"

    _torrent_list_args = None

    def __init__(self, host=None, port=None, user=None, password=None):
        super(TransmissionClient, self).__init__()
        if not host:
            host = config.get_default(self.config_key, "host", "localhost")
        self.host = host
        if not port:
            port = config.get_default(self.config_key, "port", DEFAULT_PORT, int)
        self.port = port
        if not user:
            user = config.get_default(self.config_key, "user", None)
        self.user = user
        if not password:
            password = config.get_default(self.config_key, "password", None)
        self.password = password
        self.client = None
        self.connect()

    def client_version(self):
        version = "{}.{} ({})".format(*self.client.server_version)
        return version

    def connect(self):
        try:
            self.client = Client(address=self.host, port=self.port, user=self.user, password=self.password)
        except TransmissionError as err:
            if err.original.code == 111:
                self.log.error("Failed to connect to transmission-daemon, is it running?")
            elif err.original.code == 113:
                self.log.error("No route to host")
            else:
                self.log.exception("Error connecting to transmission server")
            raise

    def add(self, data, download_dir=None):
        """ Add a torrent to the client

        :param data: Torrent data to load in
        :type data: TorrentData
        :param download_dir: Path on deluge server to store download
        :type download_dir: basestring
        :return: Status of successful load (according to deluge)
        :rtype: bool
        """
        try:
            torrent = Torrent.from_str(data.torrent_data)
            try:
                self.torrent_status(torrent.info_hash)
            except KeyError:
                pass
            else:
                self.log.warn("Tried to load duplicate info hash: {}".format(torrent.info_hash))
                return True
            torrent_data = b64encode(data.torrent_data)
            res = self.client.add_torrent(torrent_data, download_dir=download_dir)
        except TransmissionError as err:
            try:
                msg = err._message
            except AttributeError:
                msg = err.message
            if "duplicate torrent" in msg:
                self.log.warning("Tried to add duplicate torrent file")
                return True
            self.log.exception(err)
            return False

        return res

    torrent_add = add

    def current_speeds(self):
        """ Fetch the speeds from the session instance
        :return: Upload, Download speeds in bytes/s
        :rtype: tuple
        """
        ses = self.client.session_stats()
        return ses.uploadSpeed, ses.downloadSpeed

    def torrent_list(self):
        """ Get a list of currently loaded torrents from the client

        :return:
        :rtype:
        """
        if not self._torrent_list_args:
            self._torrent_list_args = get_arguments('torrent-get', self.client.rpc_version)
            self._torrent_list_args.extend(['seeders', 'peersKnown',
                                            'peersGettingFromUs', 'peersSendingToUs',
                                            'isPrivate'])
        torrents = self.client.get_torrents(arguments=self._torrent_list_args)
        torrent_data = list()
        for torrent in torrents:
            data = client.ClientTorrentData(
                info_hash=torrent.hashString,
                name=torrent.name,
                ratio=torrent.ratio,
                up_rate=torrent.rateUpload,
                dn_rate=torrent.rateDownload,
                up_total=torrent.uploadedEver,
                dn_total=torrent.downloadedEver,
                size=torrent.sizeWhenDone,
                size_completed=torrent.sizeWhenDone-torrent.leftUntilDone,
                # TODO peer values are wrong
                peers=torrent.peersGettingFromUs,
                total_peers=0,
                seeders=torrent.peersSendingToUs,
                total_seeders=0,
                priority=torrent.priority,
                private=torrent.isPrivate,
                state=torrent.status,
                progress=torrent.progress
            )
            torrent_data.append(data)
        return torrent_data

    def torrent_remove(self, torrent_id, remove_data=False):
        """ Remove a torrent from the backend client via its torrentID supplied by the
        torrent daemon

        :param remove_data: Remove the torrent data file as well as .torrent
        :type remove_data: bool
        :param torrent_id: TorrentID provided by transmission
        :type torrent_id: int
        """
        self.client.remove_torrent(torrent_id, delete_data=remove_data)

    def torrent_peers(self, info_hash):
        torrent = self.client.get_torrent(info_hash, arguments=['id', 'hashString', 'peers'])
        peers = []
        session = Session()
        # TODO country code lookup
        for peer in torrent.peers:
            peers.append({
                'client': peer['clientName'],
                'down_speed': peer['rateToClient'],
                'up_speed': peer['rateToPeer'],
                'progress': peer['progress'],
                'ip': peer['address'],
                'country': geoip.find_country_code(session, peer['address'])
            })
        return peers

    def torrent_start(self, info_hash):
        self.client.start_torrent(info_hash)
        return True

    def torrent_pause(self, info_hash):
        self.client.stop(info_hash)
        return True

    def torrent_files(self, info_hash):
        files = []
        file_set = self.client.get_files(info_hash)
        for v in file_set.values():
            for file_info in [f for f in v.values()]:
                files.append(client.ClientFileData(
                    path=file_info['name'],
                    progress=file_info['size'] - file_info['completed'],
                    size=file_info['size'],
                    priority=file_info['priority']
                ))
                break
        return files

    def torrent_speed(self, info_hash):
        speed = self.client.get_torrent(info_hash, arguments=['id', 'hashString', 'rateDownload', 'rateUpload'])
        return speed.rateDownload, speed.rateUpload

    def disconnect(self):
        return True

    def torrent_status(self, info_hash):
        key_map = {
            'info_hash': 'hashString',
            'up_rate': 'rateUpload',
            'dn_rate': 'rateDownload',
            'up_total': 'uploadedEver',
            'dn_total': 'downloadedEver',
            'size': 'totalSize',
            'size_completed': 'percentDone',  # wrong
            'seeders': lambda t: len(t.peers),
            'total_seeders': lambda t: len(t.peers),
            'peers': 'peersConnected',
            'total_peers': lambda t: len(t.peers),
            'priority': 'queue_position',
            'private': 'isPrivate',
            'state': 'status',
            'progress': '',
            'tracker_status': '',
            'next_announce': lambda t: t.trackerStats[0]['nextAnnounceTime'],
            'save_path': 'downloadDir',
            'piece_length': 'pieceSize',
            'num_pieces': 'pieceCount',
            'time_added': 'addedDate',
            'distributed_copies': lambda t: functools.reduce(lambda a,b: a+b, [p['progress'] for p in t.peers], 0),
            'active_time': '',
            #'seeding_time': 'secondsSeeding', Not found in my version? trans 2.8.4
            'num_files': lambda t: len(torrent.files()),
            'queue_position': 'queue_position'
        }
        torrent = self.client.get_torrent(info_hash)
        detail = client.ClientTorrentDataDetail(info_hash=info_hash)
        for key in detail.key_list:
            val = key_map.get(key, None)
            if val:
                if callable(val):
                    detail[key] = val(torrent)
                else:
                    detail[key] = getattr(torrent, val)
        return detail

    def torrent_recheck(self, info_hash):
        self.client.verify_torrent(info_hash)
        return True

    def torrent_reannounce(self, info_hash):
        self.client.reannounce_torrent(info_hash)
        return True

    def torrent_queue_up(self, info_hash):
        self.client.queue_up(info_hash)
        return True

    def torrent_queue_down(self, info_hash):
        self.client.queue_down(info_hash)
        return True

    def torrent_queue_top(self, info_hash):
        self.client.queue_top(info_hash)
        return True

    def torrent_queue_bottom(self, info_hash):
        self.client.queue_bottom(info_hash)
        return True

    def torrent_move_data(self, info_hash, dest):
        self.client.move_torrent_data(info_hash, dest)
        return True
Example #24
0
 def __init__(self, host, port, username, password):
     self.client = Client(host, port=port, user=username, password=password)
     self.torrent = os.path.join(
         os.path.expanduser('~'), 'Downloads',
         'E5340FB5C061E4E53618F41B48D7E1CEA445BB02.torrent')
     print(self.torrent)
def upload_torrents():
  parser = ArgumentParser(description='Upload .torrent files to a Transmission server.')

  parser.add_argument('host', type=str, help='Transmission host[:port] (port defaults to 9091)')

  parser.add_argument('-u', '--user', type=str, default=getuser(), help='Transmission username (defaults to current user)')
  parser.add_argument('-d', '--directory', type=str, default='~/Downloads', help='directory to search for .torrent files (defaults to ~/Downloads)')
  parser.add_argument('-k', '--keep', action='store_true', help='do not trash .torrent files after uploading')

  args = parser.parse_args()
  t = Terminal()

  directory = Path(normpath(expanduser(expandvars(args.directory)))).resolve()
  print('\nScanning {} for {} files...'.format(t.bold(str(directory)), t.bold('.torrent')), end='')
  torrent_files = sorted(directory.glob('*.torrent'))
  if torrent_files:
    print(t.bold_bright_green(' Found {}'.format(len(torrent_files))))
  else:
    print(t.bold_bright_red(' None found'))
    return

  password = getpass('\n{}\'s password: '******':' in args.host:
      hostname, port = args.host.split(':')
      client = Client(address=hostname, port=port, user=args.user, password=password)
    else:
      client = Client(address=args.host, user=args.user, password=password)
    print(t.bold_bright_green('Connected'))
  except TransmissionError as e:
    print(t.bold_bright_red('Connection failed') + ' to Transmission at ' + t.bold(args.host))
    return

  uploaded, failed = [], []

  # pad the index so that the brackets are all vertically aligned
  width = len(str(len(torrent_files)))

  for i, f in enumerate(torrent_files):
    prefix = ('[{:>{width}}]').format(i + 1, width=width)
    print('\n' + t.bold(prefix + ' Uploading') + '\t' + f.name)
    try:
      torrent = client.add_torrent('file://' + str(f))
      uploaded.append(f)
      print(t.bold_bright_green(prefix + ' Started') + '\t' + t.bright_cyan(torrent.name))
    except TransmissionError as e:
      failed.append(f)
      print(t.bold_bright_red(prefix + ' Error') + '\t' + t.bright_black(str(e)))

  if not args.keep:
    # convert to list to force iteration
    trash = lambda f: send2trash(str(f))
    list(map(trash, uploaded))

  print('')

  if uploaded:
    print('{} file{} uploaded successfully{}'.format(
      t.bold_bright_green(str(len(uploaded))),
      's' if len(uploaded) != 1 else '',
      ' and moved to trash' if not args.keep else ''
    ))

  if failed:
    print('{} file{} failed to upload'.format(
      t.bold_bright_red(str(len(failed))),
      's' if len(failed) != 1 else ''
    ))
Example #26
0
class TorrentClient(object):
    def __init__(self):
        self.conn = None

    def connect(self, host, username, password):
        if self.conn is not None:
            return self.conn

        if not host:
            return False
        try:
            if username and password:
                self.conn = Client(
                    host,
                    user=username,
                    password=password
                )
            else:
                self.conn = Client(host)
        except:
            logger.error('Could not connect to %h' % host)
            return False

        return self.conn

    def find_torrent(self, hash):
        try:
            return self.conn.get_torrent(hash)
        except KeyError:
            logger.error('torrent %s does not exist')
            return False

    def get_torrent(self, torrent):
        torrent = self.conn.get_torrent(torrent.hashString)
        torrent_files = []
        torrent_directory = os.path.normpath(torrent.downloadDir)

        for f in torrent.files().itervalues():
            if not os.path.normpath(f['name']).startswith(torrent_directory):
                file_path = os.path.join(torrent_directory,
                                         f['name'].lstrip('/'))
            else:
                file_path = f['name']

            torrent_files.append(file_path)

        torrent_info = {
            'hash': torrent.hashString,
            'name': torrent.name,
            'folder': torrent.downloadDir,
            'completed': torrent.progress == 100,
            'label': 'None', ## labels not supported in transmission - for when it's in transmission
            'files': torrent_files,
            'upload_total': torrent.uploadedEver,
            'download_total': torrent.downloadedEver,
            'ratio': torrent.ratio,
            'total_filesize': torrent.sizeWhenDone,
            'time_started': torrent.date_started
        }
        logger.debug(torrent_info)
        return torrent_info if torrent_info else False

    def start_torrent(self, torrent):
        return torrent.start()

    def stop_torrent(self, torrent):
        return torrent.stop()

    def load_torrent(self, filepath):
        if any([mylar.TRANSMISSION_DIRECTORY is None, mylar.TRANSMISSION_DIRECTORY == '', mylar.TRANSMISSION_DIRECTORY == 'None']):
            down_dir = mylar.CHECK_FOLDER
        else:
            down_dir = mylar.TRANSMISSION_DIRECTORY
        if filepath.startswith('magnet'):
            torrent = self.conn.add_torrent('%s' % filepath,
                                            download_dir=down_dir)
        else:
            torrent = self.conn.add_torrent('file://%s' % filepath,
                                            download_dir=down_dir)

        torrent.start()
        return self.get_torrent(torrent)

    def delete_torrent(self, torrent):
        deleted = []
        files = torrent.files()
        for file_item in files.itervalues():
            file_path = os.path.join(torrent.downloadDir,
                                     file_item['name'])
            deleted.append(file_path)

        if len(files) > 1:
            torrent_path = os.path.join(torrent.downloadDir, torrent.name)
            for path, _, _ in os.walk(torrent_path, topdown=False):
                deleted.append(path)

        if self.conn.remove_torrent(torrent.hashString, delete_data=True):
            return deleted
        else:
            logger.error('Unable to delete %s' % torrent.name)
            return []
Example #27
0
 def __init__(self):
     print(f"user={TS_USER}, password={PASSWORD}")
     self.conn = Client(ADDRESS, PORT, user=TS_USER, password=PASSWORD)
     self.persistence = PERSISTENCE_FILE
class EmissionOverview(Screen, HelpableScreen):
    skin = """<screen name="EmissionOverview" title="Torrent Overview" position="75,135" size="565,330">
		<ePixmap position="0,0" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
		<ePixmap position="140,0" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
		<ePixmap position="280,0" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
		<ePixmap position="420,0" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
		<widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget size="320,25" alphatest="on" position="5,45" zPosition="1" name="all_sel" pixmap="skin_default/epg_now.png" />
		<widget valign="center" transparent="1" size="108,22" backgroundColor="#25062748" position="5,47" zPosition="2" source="all_text" render="Label" halign="center" font="Regular;18" />
		<widget size="320,25" alphatest="on" position="5,45" zPosition="1" name="downloading_sel" pixmap="skin_default/epg_next.png" />
		<widget valign="center" transparent="1" size="108,22" backgroundColor="#25062748" position="111,47" zPosition="2" source="downloading_text" render="Label" halign="center" font="Regular;18" />
		<widget size="320,25" alphatest="on" position="5,45" zPosition="1" name="seeding_sel" pixmap="skin_default/epg_more.png" />
		<widget valign="center" transparent="1" size="108,22" backgroundColor="#25062748" position="212,47" zPosition="2" source="seeding_text" render="Label" halign="center" font="Regular;18" />
		<widget source="torrents" render="Label" size="240,22" position="320,47" halign="right" font="Regular;18" />
		<!--ePixmap size="550,230" alphatest="on" position="5,65" pixmap="skin_default/border_epg.png" /-->
		<widget source="list" render="Listbox" position="5,70" size="550,225" scrollbarMode="showAlways">
			<convert type="TemplatedMultiContent">
				{"template": [
						MultiContentEntryText(pos=(2,2), size=(555,22), text = 1, font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
						MultiContentEntryText(pos=(2,26), size=(555,18), text = 2, font = 1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
						(eListboxPythonMultiContent.TYPE_PROGRESS, 0, 44, 537, 6, -3),
					],
				  "fonts": [gFont("Regular", 20),gFont("Regular", 16)],
				  "itemHeight": 51
				 }
			</convert>
		</widget>
		<widget source="upspeed" render="Label" size="150,20" position="5,300" halign="left" font="Regular;18" />
		<widget source="downspeed" render="Label" size="150,20" position="410,300" halign="right" font="Regular;18" />
	</screen>"""

    def __init__(self, session):
        Screen.__init__(self, session)
        HelpableScreen.__init__(self)

        try:
            self.transmission = Client(
                address=config.plugins.emission.hostname.value,
                port=config.plugins.emission.port.value,
                user=config.plugins.emission.username.value,
                password=config.plugins.emission.password.value,
            )
        except TransmissionError as te:
            self.transmission = None

        self["SetupActions"] = HelpableActionMap(
            self, "SetupActions", {"ok": (self.ok, _("show details")), "cancel": (self.close, _("close"))}
        )

        self["ColorActions"] = HelpableActionMap(
            self,
            "ColorActions",
            {
                "green": (self.bandwidth, _("open bandwidth settings")),
                "yellow": (self.prevlist, _("show previous list")),
                "blue": (self.nextlist, _("show next list")),
            },
        )

        self["MenuActions"] = HelpableActionMap(self, "MenuActions", {"menu": (self.menu, _("open context menu"))})

        self["key_red"] = StaticText(_("Close"))
        self["key_green"] = StaticText(_("Bandwidth"))
        self["key_yellow"] = StaticText("")
        self["key_blue"] = StaticText("")

        self["all_text"] = StaticText(_("All"))
        self["downloading_text"] = StaticText(_("DL"))
        self["seeding_text"] = StaticText(_("UL"))
        self["upspeed"] = StaticText("")
        self["downspeed"] = StaticText("")
        self["torrents"] = StaticText("")

        self["all_sel"] = Pixmap()
        self["downloading_sel"] = Pixmap()
        self["seeding_sel"] = Pixmap()

        self["list"] = List([])

        self.list_type = config.plugins.emission.last_tab.value
        self.sort_type = config.plugins.emission.last_sort.value
        self.showHideSetTextMagic()

        self.timer = eTimer()
        self.timer.callback.append(self.updateList)
        self.timer.start(0, 1)

    def bandwidthCallback(self, ret=None):
        if self.transmission is not None and ret:
            try:
                self.transmission.set_session(**ret)
            except TransmissionError as te:
                self.session.open(
                    MessageBox,
                    _("Error communicating with transmission-daemon: %s.") % (te),
                    type=MessageBox.TYPE_ERROR,
                    timeout=5,
                )
        self.updateList()

    def menuCallback(self, ret=None):
        ret and ret[1]()

    def newDlCallback(self, ret=None):
        if self.transmission is not None and ret:
            try:
                res = self.transmission.add_url(ret)
            except TransmissionError as te:
                self.session.open(
                    MessageBox,
                    _("Error communicating with transmission-daemon: %s.") % (te),
                    type=MessageBox.TYPE_ERROR,
                    timeout=5,
                )
            else:
                if not res:
                    self.session.open(
                        MessageBox,
                        _("Torrent could not be scheduled not download!"),
                        type=MessageBox.TYPE_ERROR,
                        timeout=5,
                    )
        self.updateList()

    def newDl(self):
        self.timer.stop()
        self.session.openWithCallback(self.newDlCallback, TorrentLocationBox)

    def sortCallback(self, ret=None):
        if ret is not None:
            self.sort_type = config.plugins.emission.last_sort.value = ret[1]
            config.plugins.emission.last_sort.save()
        self.updateList()

    def sort(self):
        self.timer.stop()
        self.session.openWithCallback(
            self.sortCallback,
            ChoiceBox,
            _("Which sorting method do you prefer?"),
            [
                (_("by eta"), SORT_TYPE_TIME),
                (_("by progress"), SORT_TYPE_PROGRESS),
                (_("by age"), SORT_TYPE_ADDED),
                (_("by speed"), SORT_TYPE_SPEED),
            ],
        )

    def pauseShown(self):
        if self.transmission is not None:
            self.transmission.stop([x[0].id for x in self.list])

    def unpauseShown(self):
        if self.transmission is not None:
            self.transmission.start([x[0].id for x in self.list])

    def pauseAll(self):
        if self.transmission is None:
            return

        try:
            self.transmission.stop([x.id for x in self.transmission.list().values()])
        except TransmissionError as te:
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5,
            )

    def unpauseAll(self):
        if self.transmission is None:
            return

        try:
            self.transmission.start([x.id for x in self.transmission.list().values()])
        except TransmissionError as te:
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5,
            )

    def configure(self):
        # reload(EmissionSetup)
        self.timer.stop()
        self.session.openWithCallback(self.configureCallback, EmissionSetup.EmissionSetup)

    def menu(self):
        self.session.openWithCallback(
            self.menuCallback,
            ChoiceBox,
            _("What do you want to do?"),
            [
                (_("Configure connection"), self.configure),
                (_("Change sorting"), self.sort),
                (_("Add new download"), self.newDl),
                (_("Pause shown"), self.pauseShown),
                (_("Unpause shown"), self.unpauseShown),
                (_("Pause all"), self.pauseAll),
                (_("Unpause all"), self.unpauseAll),
            ],
        )

    def showHideSetTextMagic(self):
        list_type = self.list_type
        if list_type == LIST_TYPE_ALL:
            self["all_sel"].show()
            self["downloading_sel"].hide()
            self["seeding_sel"].hide()
            self["key_yellow"].setText(_("Seeding"))
            self["key_blue"].setText(_("Download"))
        elif list_type == LIST_TYPE_DOWNLOADING:
            self["all_sel"].hide()
            self["downloading_sel"].show()
            self["seeding_sel"].hide()
            self["key_yellow"].setText(_("All"))
            self["key_blue"].setText(_("Seeding"))
        else:  # if list_type == LIST_TYPE_SEEDING:
            self["all_sel"].hide()
            self["downloading_sel"].hide()
            self["seeding_sel"].show()
            self["key_yellow"].setText(_("Download"))
            self["key_blue"].setText(_("All"))

    def prevlist(self):
        self.timer.stop()
        list_type = self.list_type
        if list_type == LIST_TYPE_ALL:
            self.list_type = LIST_TYPE_SEEDING
        elif list_type == LIST_TYPE_DOWNLOADING:
            self.list_type = LIST_TYPE_ALL
        else:  # if list_type == LIST_TYPE_SEEDING:
            self.list_type = LIST_TYPE_DOWNLOADING
        self.showHideSetTextMagic()
        self.updateList()

    def nextlist(self):
        self.timer.stop()
        list_type = self.list_type
        if list_type == LIST_TYPE_ALL:
            self.list_type = LIST_TYPE_DOWNLOADING
        elif list_type == LIST_TYPE_DOWNLOADING:
            self.list_type = LIST_TYPE_SEEDING
        else:  # if list_type == LIST_TYPE_SEEDING:
            self.list_type = LIST_TYPE_ALL
        self.showHideSetTextMagic()
        self.updateList()

    def prevItem(self):
        self["list"].selectPrevious()
        cur = self["list"].getCurrent()
        return cur and cur[0]

    def nextItem(self):
        self["list"].selectNext()
        cur = self["list"].getCurrent()
        return cur and cur[0]

    def bandwidth(self):
        if self.transmission is None:
            return

            # reload(EmissionBandwidth)
        self.timer.stop()
        try:
            sess = self.transmission.get_session()
            rpc_version = self.transmission.rpc_version
        except TransmissionError as te:
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5,
            )
            # XXX: this seems silly but cleans the gui and restarts the timer :-)
            self.updateList()
        else:
            self.session.openWithCallback(
                self.bandwidthCallback, EmissionBandwidth.EmissionBandwidth, sess, False, rpc_version
            )

    def configureCallback(self):
        try:
            self.transmission = Client(
                address=config.plugins.emission.hostname.value,
                port=config.plugins.emission.port.value,
                user=config.plugins.emission.username.value,
                password=config.plugins.emission.password.value,
            )
        except TransmissionError as te:
            self.transmission = None
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5,
            )
        else:
            self.updateList()

    def updateList(self, *args, **kwargs):
        # XXX: if we are not connected do NOT restart timer, it's useless anyway
        if self.transmission is None:
            return

        try:
            lst = list(self.transmission.list().values())
            session = self.transmission.session_stats()
        except TransmissionError:
            # XXX: some hint in gui would be nice
            self["list"].setList([])
            self["torrents"].setText("")
            self["upspeed"].setText("")
            self["downspeed"].setText("")
        else:
            sort_type = self.sort_type
            if sort_type == SORT_TYPE_TIME:

                def cmp_func(x, y):
                    x_eta = x.fields["eta"]
                    y_eta = y.fields["eta"]
                    if x_eta > -1 and y_eta < 0:
                        return 1
                    if x_eta < 0 and y_eta > -1:
                        return -1
                        # note: cmp call inversed because lower eta is "better"
                    return cmp(y_eta, x_eta) or cmp(x.progress, y.progress)

                lst.sort(cmp=cmp_func, reverse=True)
            elif sort_type == SORT_TYPE_PROGRESS:
                lst.sort(key=lambda x: x.progress, reverse=True)
            elif sort_type == SORT_TYPE_SPEED:
                lst.sort(key=lambda x: (x.rateDownload, x.rateUpload), reverse=True)
                # SORT_TYPE_ADDED is what we already have

            list_type = self.list_type
            if list_type == LIST_TYPE_ALL:
                lst = [
                    (x, x.name.encode("utf-8", "ignore"), str(x.eta or "?:??:??").encode("utf-8"), int(x.progress))
                    for x in lst
                ]
            elif list_type == LIST_TYPE_DOWNLOADING:
                lst = [
                    (x, x.name.encode("utf-8", "ignore"), str(x.eta or "?:??:??").encode("utf-8"), int(x.progress))
                    for x in lst
                    if x.status == "downloading"
                ]
            else:  # if list_type == LIST_TYPE_SEEDING:
                lst = [
                    (x, x.name.encode("utf-8", "ignore"), str(x.eta or "?:??:??").encode("utf-8"), int(x.progress))
                    for x in lst
                    if x.status == "seeding"
                ]

            self["torrents"].setText(_("Active Torrents: %d/%d") % (session.activeTorrentCount, session.torrentCount))
            self["upspeed"].setText(_("UL: %d kb/s") % (session.uploadSpeed / 1024))
            self["downspeed"].setText(_("DL: %d kb/s") % (session.downloadSpeed / 1024))

            # XXX: this is a little ugly but this way we have the least
            # visible distortion :-)
            index = min(self["list"].index, len(lst) - 1)
            self["list"].setList(lst)
            self["list"].index = index

            self.list = lst
        self.timer.startLongTimer(10)

    def ok(self):
        cur = self["list"].getCurrent()
        if self.transmission is not None and cur:
            # reload(EmissionDetailview)
            self.timer.stop()
            self.session.openWithCallback(
                self.updateList,
                EmissionDetailview.EmissionDetailview,
                self.transmission,
                cur[0],
                self.prevItem,
                self.nextItem,
            )

    def close(self):
        self.timer.stop()
        config.plugins.emission.last_tab.value = self.list_type
        config.plugins.emission.last_tab.save()
        Screen.close(self)
Example #29
0
class TransmissionClient(client.TorrentClient):
    """ Backend implementation for transmission """

    config_key = "client_transmission"

    _torrent_list_args = None

    def __init__(self, host=None, port=None, user=None, password=None):
        super(TransmissionClient, self).__init__()
        if not host:
            host = config.get_default(self.config_key, "host", "localhost")
        self.host = host
        if not port:
            port = config.get_default(self.config_key, "port", DEFAULT_PORT,
                                      int)
        self.port = port
        if not user:
            user = config.get_default(self.config_key, "user", None)
        self.user = user
        if not password:
            password = config.get_default(self.config_key, "password", None)
        self.password = password
        self.client = None
        self.connect()

    def client_version(self):
        version = "{}.{} ({})".format(*self.client.server_version)
        return version

    def connect(self):
        try:
            self.client = Client(address=self.host,
                                 port=self.port,
                                 user=self.user,
                                 password=self.password)
        except TransmissionError as err:
            if err.original.code == 111:
                self.log.error(
                    "Failed to connect to transmission-daemon, is it running?")
            elif err.original.code == 113:
                self.log.error("No route to host")
            else:
                self.log.exception("Error connecting to transmission server")
            raise

    def add(self, data, download_dir=None):
        """ Add a torrent to the client

        :param data: Torrent data to load in
        :type data: TorrentData
        :param download_dir: Path on deluge server to store download
        :type download_dir: basestring
        :return: Status of successful load (according to deluge)
        :rtype: bool
        """
        try:
            torrent = Torrent.from_str(data.torrent_data)
            try:
                self.torrent_status(torrent.info_hash)
            except KeyError:
                pass
            else:
                self.log.warn("Tried to load duplicate info hash: {}".format(
                    torrent.info_hash))
                return True
            torrent_data = b64encode(data.torrent_data)
            res = self.client.add_torrent(torrent_data,
                                          download_dir=download_dir)
        except TransmissionError as err:
            try:
                msg = err._message
            except AttributeError:
                msg = err.message
            if "duplicate torrent" in msg:
                self.log.warning("Tried to add duplicate torrent file")
                return True
            self.log.exception(err)
            return False

        return res

    torrent_add = add

    def current_speeds(self):
        """ Fetch the speeds from the session instance
        :return: Upload, Download speeds in bytes/s
        :rtype: tuple
        """
        ses = self.client.session_stats()
        return ses.uploadSpeed, ses.downloadSpeed

    def torrent_list(self):
        """ Get a list of currently loaded torrents from the client

        :return:
        :rtype:
        """
        if not self._torrent_list_args:
            self._torrent_list_args = get_arguments('torrent-get',
                                                    self.client.rpc_version)
            self._torrent_list_args.extend([
                'seeders', 'peersKnown', 'peersGettingFromUs',
                'peersSendingToUs', 'isPrivate'
            ])
        torrents = self.client.get_torrents(arguments=self._torrent_list_args)
        torrent_data = list()
        for torrent in torrents:
            data = client.ClientTorrentData(
                info_hash=torrent.hashString,
                name=torrent.name,
                ratio=torrent.ratio,
                up_rate=torrent.rateUpload,
                dn_rate=torrent.rateDownload,
                up_total=torrent.uploadedEver,
                dn_total=torrent.downloadedEver,
                size=torrent.sizeWhenDone,
                size_completed=torrent.sizeWhenDone - torrent.leftUntilDone,
                # TODO peer values are wrong
                peers=torrent.peersGettingFromUs,
                total_peers=0,
                seeders=torrent.peersSendingToUs,
                total_seeders=0,
                priority=torrent.priority,
                private=torrent.isPrivate,
                state=torrent.status,
                progress=torrent.progress)
            torrent_data.append(data)
        return torrent_data

    def torrent_remove(self, torrent_id, remove_data=False):
        """ Remove a torrent from the backend client via its torrentID supplied by the
        torrent daemon

        :param remove_data: Remove the torrent data file as well as .torrent
        :type remove_data: bool
        :param torrent_id: TorrentID provided by transmission
        :type torrent_id: int
        """
        self.client.remove_torrent(torrent_id, delete_data=remove_data)

    def torrent_peers(self, info_hash):
        torrent = self.client.get_torrent(
            info_hash, arguments=['id', 'hashString', 'peers'])
        peers = []
        session = Session()
        # TODO country code lookup
        for peer in torrent.peers:
            peers.append({
                'client':
                peer['clientName'],
                'down_speed':
                peer['rateToClient'],
                'up_speed':
                peer['rateToPeer'],
                'progress':
                peer['progress'],
                'ip':
                peer['address'],
                'country':
                geoip.find_country_code(session, peer['address'])
            })
        return peers

    def torrent_start(self, info_hash):
        self.client.start_torrent(info_hash)
        return True

    def torrent_pause(self, info_hash):
        self.client.stop(info_hash)
        return True

    def torrent_files(self, info_hash):
        files = []
        file_set = self.client.get_files(info_hash)
        for v in list(file_set.values()):
            for file_info in [f for f in list(v.values())]:
                files.append(
                    client.ClientFileData(path=file_info['name'],
                                          progress=file_info['size'] -
                                          file_info['completed'],
                                          size=file_info['size'],
                                          priority=file_info['priority']))
                break
        return files

    def torrent_speed(self, info_hash):
        speed = self.client.get_torrent(
            info_hash,
            arguments=['id', 'hashString', 'rateDownload', 'rateUpload'])
        return speed.rateDownload, speed.rateUpload

    def disconnect(self):
        return True

    def torrent_status(self, info_hash):
        key_map = {
            'info_hash':
            'hashString',
            'up_rate':
            'rateUpload',
            'dn_rate':
            'rateDownload',
            'up_total':
            'uploadedEver',
            'dn_total':
            'downloadedEver',
            'size':
            'totalSize',
            'size_completed':
            'percentDone',  # wrong
            'seeders':
            lambda t: len(t.peers),
            'total_seeders':
            lambda t: len(t.peers),
            'peers':
            'peersConnected',
            'total_peers':
            lambda t: len(t.peers),
            'priority':
            'queue_position',
            'private':
            'isPrivate',
            'state':
            'status',
            'progress':
            '',
            'tracker_status':
            '',
            'next_announce':
            lambda t: t.trackerStats[0]['nextAnnounceTime'],
            'save_path':
            'downloadDir',
            'piece_length':
            'pieceSize',
            'num_pieces':
            'pieceCount',
            'time_added':
            'addedDate',
            'distributed_copies':
            lambda t: functools.reduce(lambda a, b: a + b,
                                       [p['progress'] for p in t.peers], 0),
            'active_time':
            '',
            #'seeding_time': 'secondsSeeding', Not found in my version? trans 2.8.4
            'num_files':
            lambda t: len(torrent.files()),
            'queue_position':
            'queue_position'
        }
        torrent = self.client.get_torrent(info_hash)
        detail = client.ClientTorrentDataDetail(info_hash=info_hash)
        for key in detail.key_list:
            val = key_map.get(key, None)
            if val:
                if callable(val):
                    detail[key] = val(torrent)
                else:
                    detail[key] = getattr(torrent, val)
        return detail

    def torrent_recheck(self, info_hash):
        self.client.verify_torrent(info_hash)
        return True

    def torrent_reannounce(self, info_hash):
        self.client.reannounce_torrent(info_hash)
        return True

    def torrent_queue_up(self, info_hash):
        self.client.queue_up(info_hash)
        return True

    def torrent_queue_down(self, info_hash):
        self.client.queue_down(info_hash)
        return True

    def torrent_queue_top(self, info_hash):
        self.client.queue_top(info_hash)
        return True

    def torrent_queue_bottom(self, info_hash):
        self.client.queue_bottom(info_hash)
        return True

    def torrent_move_data(self, info_hash, dest):
        self.client.move_torrent_data(info_hash, dest)
        return True
Example #30
0
#!/usr/bin/env python3

from transmissionrpc import Client

if __name__ == "__main__":
    client = Client('localhost', port=9091)
    torrents = client.get_torrents()
    for torrent in torrents:
        if "S02E05" in torrent.name:
            client.remove_torrent(torrent.id)
class TransmissionClient(BitTorrentClient):
    def __init__(self):
        BitTorrentClient.__init__(self, TRANSMISSION_CLIENT)
        self.dict_client = {}

    def connect(self, address, port, user, password):
        try:
            self.client = TClient(address, port, user, password)
        except TransmissionError as err:
            raise BTCexception(" {} TransmissionError {}".format(address, err))
        except socket.timeout as err:
            raise BTCexception(" {} Socket error {}".format(address, err))
        else:
            self.dict_client['name'] = address

    def get_torrents(self):
        tr_torrents = self.client.get_torrents()
        self.dict_client['torrents'] = []
        for _torrent in tr_torrents:
            _torrent_dict = {
                u'name': _torrent.name,
                u'hash': _torrent.hashString,
                u'progress': _torrent.progress,
                u'status': _torrent.status,
                u'date_active': _torrent.date_active,
                u'date_added': _torrent.date_added,
                u'date_started': _torrent.date_started,
                u'date_done': _torrent.date_done,
                u'eta': -1,
                u'error': _torrent.error,
                u'errorString': _torrent.errorString[:350],
            }
            #TODO: Warning eta can return exception ValuerError

            try:
                _torrent_dict[u'eta'] = timedelta.total_seconds(_torrent.eta)
            except ValueError:
                pass
            self.dict_client['torrents'].append(_torrent_dict)
            if _torrent_dict[u'error'] == 3:
                _torrent_dict[u'status'] = u'error_torrentclient'
            if _torrent_dict[u'error'] in [1, 2]:
                _torrent_dict[u'status'] = u'error_torrenttracker'

        return self.dict_client

    def add_torrent(self, torrent_path):
        pass

    def remove(self, torrent_hash):
        self.client.remove_torrent(torrent_hash)

    def verify(self, torrent_hash):
        self.client.verify_torrent(torrent_hash)

    def free_space_bytes(self):
        free_space = None
        try:
            session = self.client.get_session()
        except TransmissionError as err:
            raise BTCexception(" {} TransmissionError {}".format(
                self.dict_client['name'], err))
        except socket.timeout as err:
            raise BTCexception(" {} Socket error {}".format(
                self.dict_client['name'], err))
        except Exception as err:
            raise BTCexception("{}: {}".format(self.dict_client['name'],
                                               err.message))
        else:
            free_space = session.download_dir_free_space
        return free_space
Example #32
0
class TorrentClient(object):
    def __init__(self):
        self.conn = None

    def connect(self, host, username, password):
        if self.conn is not None:
            return self.conn

        if not host:
            return False
        try:
            if username and password:
                self.conn = Client(host, user=username, password=password)
            else:
                self.conn = Client(host)
        except:
            logger.error('Could not connect to %h' % host)
            return False

        return self.conn

    def find_torrent(self, hash):
        try:
            return self.conn.get_torrent(hash)
        except KeyError:
            logger.error('torrent %s does not exist')
            return False

    def get_torrent(self, torrent):
        torrent = self.conn.get_torrent(torrent.hashString)
        torrent_files = []
        torrent_directory = os.path.normpath(torrent.downloadDir)

        for f in torrent.files().itervalues():
            if not os.path.normpath(f['name']).startswith(torrent_directory):
                file_path = os.path.join(torrent_directory,
                                         f['name'].lstrip('/'))
            else:
                file_path = f['name']

            torrent_files.append(file_path)

        torrent_info = {
            'hash': torrent.hashString,
            'name': torrent.name,
            'folder': torrent.downloadDir,
            'completed': torrent.progress == 100,
            'label':
            'None',  ## labels not supported in transmission - for when it's in transmission
            'files': torrent_files,
            'upload_total': torrent.uploadedEver,
            'download_total': torrent.downloadedEver,
            'ratio': torrent.ratio,
            'total_filesize': torrent.sizeWhenDone,
            'time_started': torrent.date_started
        }
        logger.debug(torrent_info)
        return torrent_info if torrent_info else False

    def start_torrent(self, torrent):
        return torrent.start()

    def stop_torrent(self, torrent):
        return torrent.stop()

    def load_torrent(self, filepath):
        if any([
                mylar.TRANSMISSION_DIRECTORY is None,
                mylar.TRANSMISSION_DIRECTORY == '',
                mylar.TRANSMISSION_DIRECTORY == 'None'
        ]):
            down_dir = mylar.CHECK_FOLDER
        else:
            down_dir = mylar.TRANSMISSION_DIRECTORY
        torrent = self.conn.add_torrent(filepath, download_dir=down_dir)

        torrent.start()
        return self.get_torrent(torrent)

    def delete_torrent(self, torrent):
        deleted = []
        files = torrent.files()
        for file_item in files.itervalues():
            file_path = os.path.join(torrent.downloadDir, file_item['name'])
            deleted.append(file_path)

        if len(files) > 1:
            torrent_path = os.path.join(torrent.downloadDir, torrent.name)
            for path, _, _ in os.walk(torrent_path, topdown=False):
                deleted.append(path)

        if self.conn.remove_torrent(torrent.hashString, delete_data=True):
            return deleted
        else:
            logger.error('Unable to delete %s' % torrent.name)
            return []
Example #33
0
def connect(settings):
    return Client(**to_rpcclient_settings(settings))
Example #34
0
class EmissionOverview(Screen, HelpableScreen):
    skin = """<screen name="EmissionOverview" title="Torrent Overview" position="75,135" size="565,330">
		<ePixmap position="0,0" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
		<ePixmap position="140,0" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
		<ePixmap position="280,0" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
		<ePixmap position="420,0" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
		<widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
		<widget size="320,25" alphatest="on" position="5,45" zPosition="1" name="all_sel" pixmap="skin_default/epg_now.png" />
		<widget valign="center" transparent="1" size="108,22" backgroundColor="#25062748" position="5,47" zPosition="2" source="all_text" render="Label" halign="center" font="Regular;18" />
		<widget size="320,25" alphatest="on" position="5,45" zPosition="1" name="downloading_sel" pixmap="skin_default/epg_next.png" />
		<widget valign="center" transparent="1" size="108,22" backgroundColor="#25062748" position="111,47" zPosition="2" source="downloading_text" render="Label" halign="center" font="Regular;18" />
		<widget size="320,25" alphatest="on" position="5,45" zPosition="1" name="seeding_sel" pixmap="skin_default/epg_more.png" />
		<widget valign="center" transparent="1" size="108,22" backgroundColor="#25062748" position="212,47" zPosition="2" source="seeding_text" render="Label" halign="center" font="Regular;18" />
		<widget source="torrents" render="Label" size="240,22" position="320,47" halign="right" font="Regular;18" />
		<!--ePixmap size="550,230" alphatest="on" position="5,65" pixmap="skin_default/border_epg.png" /-->
		<widget source="list" render="Listbox" position="5,70" size="550,225" scrollbarMode="showAlways">
			<convert type="TemplatedMultiContent">
				{"template": [
						MultiContentEntryText(pos=(2,2), size=(555,22), text = 1, font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
						MultiContentEntryText(pos=(2,26), size=(555,18), text = 2, font = 1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
						(eListboxPythonMultiContent.TYPE_PROGRESS, 0, 44, 537, 6, -3),
					],
				  "fonts": [gFont("Regular", 20),gFont("Regular", 16)],
				  "itemHeight": 51
				 }
			</convert>
		</widget>
		<widget source="upspeed" render="Label" size="150,20" position="5,300" halign="left" font="Regular;18" />
		<widget source="downspeed" render="Label" size="150,20" position="410,300" halign="right" font="Regular;18" />
	</screen>"""

    def __init__(self, session):
        Screen.__init__(self, session)
        HelpableScreen.__init__(self)

        try:
            self.transmission = Client(
                address=config.plugins.emission.hostname.value,
                port=config.plugins.emission.port.value,
                user=config.plugins.emission.username.value,
                password=config.plugins.emission.password.value)
        except TransmissionError as te:
            self.transmission = None

        self["SetupActions"] = HelpableActionMap(
            self, "SetupActions", {
                "ok": (self.ok, _("show details")),
                "cancel": (self.close, _("close")),
            })

        self["ColorActions"] = HelpableActionMap(
            self, "ColorActions", {
                "green": (self.bandwidth, _("open bandwidth settings")),
                "yellow": (self.prevlist, _("show previous list")),
                "blue": (self.nextlist, _("show next list")),
            })

        self["MenuActions"] = HelpableActionMap(self, "MenuActions", {
            "menu": (self.menu, _("open context menu")),
        })

        self["key_red"] = StaticText(_("Close"))
        self["key_green"] = StaticText(_("Bandwidth"))
        self["key_yellow"] = StaticText("")
        self["key_blue"] = StaticText("")

        self["all_text"] = StaticText(_("All"))
        self["downloading_text"] = StaticText(_("DL"))
        self["seeding_text"] = StaticText(_("UL"))
        self["upspeed"] = StaticText("")
        self["downspeed"] = StaticText("")
        self["torrents"] = StaticText("")

        self["all_sel"] = Pixmap()
        self["downloading_sel"] = Pixmap()
        self["seeding_sel"] = Pixmap()

        self['list'] = List([])

        self.list_type = config.plugins.emission.last_tab.value
        self.sort_type = config.plugins.emission.last_sort.value
        self.showHideSetTextMagic()

        self.timer = eTimer()
        self.timer_conn = self.timer.timeout.connect(self.updateList)
        self.timer.start(0, 1)

    def bandwidthCallback(self, ret=None):
        if self.transmission is not None and ret:
            try:
                self.transmission.set_session(**ret)
            except TransmissionError as te:
                self.session.open(
                    MessageBox,
                    _("Error communicating with transmission-daemon: %s.") %
                    (te),
                    type=MessageBox.TYPE_ERROR,
                    timeout=5)
        self.updateList()

    def menuCallback(self, ret=None):
        ret and ret[1]()

    def newDlCallback(self, ret=None):
        if self.transmission is not None and ret:
            try:
                res = self.transmission.add_url(ret)
            except TransmissionError as te:
                self.session.open(
                    MessageBox,
                    _("Error communicating with transmission-daemon: %s.") %
                    (te),
                    type=MessageBox.TYPE_ERROR,
                    timeout=5)
            else:
                if not res:
                    self.session.open(
                        MessageBox,
                        _("Torrent could not be scheduled not download!"),
                        type=MessageBox.TYPE_ERROR,
                        timeout=5)
        self.updateList()

    def newDl(self):
        self.timer.stop()
        self.session.openWithCallback(self.newDlCallback, TorrentLocationBox)

    def sortCallback(self, ret=None):
        if ret is not None:
            self.sort_type = config.plugins.emission.last_sort.value = ret[1]
            config.plugins.emission.last_sort.save()
        self.updateList()

    def sort(self):
        self.timer.stop()
        self.session.openWithCallback(self.sortCallback, ChoiceBox,
                                      _("Which sorting method do you prefer?"),
                                      [(_("by eta"), SORT_TYPE_TIME),
                                       (_("by progress"), SORT_TYPE_PROGRESS),
                                       (_("by age"), SORT_TYPE_ADDED),
                                       (_("by speed"), SORT_TYPE_SPEED)])

    def pauseShown(self):
        if self.transmission is not None:
            self.transmission.stop([x[0].id for x in self.list])

    def unpauseShown(self):
        if self.transmission is not None:
            self.transmission.start([x[0].id for x in self.list])

    def pauseAll(self):
        if self.transmission is None:
            return

        try:
            self.transmission.stop(
                [x.id for x in self.transmission.list().values()])
        except TransmissionError as te:
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5)

    def unpauseAll(self):
        if self.transmission is None:
            return

        try:
            self.transmission.start(
                [x.id for x in self.transmission.list().values()])
        except TransmissionError as te:
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5)

    def configure(self):
        #reload(EmissionSetup)
        self.timer.stop()
        self.session.openWithCallback(self.configureCallback,
                                      EmissionSetup.EmissionSetup)

    def menu(self):
        self.session.openWithCallback(
            self.menuCallback,
            ChoiceBox,
            _("What do you want to do?"),
            [(_("Configure connection"), self.configure),
             (_("Change sorting"), self.sort),
             (_("Add new download"), self.newDl),
             (_("Pause shown"), self.pauseShown),
             (_("Unpause shown"), self.unpauseShown),
             (_("Pause all"), self.pauseAll),
             (_("Unpause all"), self.unpauseAll)],
        )

    def showHideSetTextMagic(self):
        list_type = self.list_type
        if list_type == LIST_TYPE_ALL:
            self["all_sel"].show()
            self["downloading_sel"].hide()
            self["seeding_sel"].hide()
            self["key_yellow"].setText(_("Seeding"))
            self["key_blue"].setText(_("Download"))
        elif list_type == LIST_TYPE_DOWNLOADING:
            self["all_sel"].hide()
            self["downloading_sel"].show()
            self["seeding_sel"].hide()
            self["key_yellow"].setText(_("All"))
            self["key_blue"].setText(_("Seeding"))
        else:  #if list_type == LIST_TYPE_SEEDING:
            self["all_sel"].hide()
            self["downloading_sel"].hide()
            self["seeding_sel"].show()
            self["key_yellow"].setText(_("Download"))
            self["key_blue"].setText(_("All"))

    def prevlist(self):
        self.timer.stop()
        list_type = self.list_type
        if list_type == LIST_TYPE_ALL:
            self.list_type = LIST_TYPE_SEEDING
        elif list_type == LIST_TYPE_DOWNLOADING:
            self.list_type = LIST_TYPE_ALL
        else:  #if list_type == LIST_TYPE_SEEDING:
            self.list_type = LIST_TYPE_DOWNLOADING
        self.showHideSetTextMagic()
        self.updateList()

    def nextlist(self):
        self.timer.stop()
        list_type = self.list_type
        if list_type == LIST_TYPE_ALL:
            self.list_type = LIST_TYPE_DOWNLOADING
        elif list_type == LIST_TYPE_DOWNLOADING:
            self.list_type = LIST_TYPE_SEEDING
        else:  #if list_type == LIST_TYPE_SEEDING:
            self.list_type = LIST_TYPE_ALL
        self.showHideSetTextMagic()
        self.updateList()

    def prevItem(self):
        self['list'].selectPrevious()
        cur = self['list'].getCurrent()
        return cur and cur[0]

    def nextItem(self):
        self['list'].selectNext()
        cur = self['list'].getCurrent()
        return cur and cur[0]

    def bandwidth(self):
        if self.transmission is None:
            return

        #reload(EmissionBandwidth)
        self.timer.stop()
        try:
            sess = self.transmission.get_session()
            rpc_version = self.transmission.rpc_version
        except TransmissionError as te:
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5)
            # XXX: this seems silly but cleans the gui and restarts the timer :-)
            self.updateList()
        else:
            self.session.openWithCallback(self.bandwidthCallback,
                                          EmissionBandwidth.EmissionBandwidth,
                                          sess, False, rpc_version)

    def configureCallback(self):
        try:
            self.transmission = Client(
                address=config.plugins.emission.hostname.value,
                port=config.plugins.emission.port.value,
                user=config.plugins.emission.username.value,
                password=config.plugins.emission.password.value)
        except TransmissionError as te:
            self.transmission = None
            self.session.open(
                MessageBox,
                _("Error communicating with transmission-daemon: %s.") % (te),
                type=MessageBox.TYPE_ERROR,
                timeout=5)
        else:
            self.updateList()

    def updateList(self, *args, **kwargs):
        # XXX: if we are not connected do NOT restart timer, it's useless anyway
        if self.transmission is None:
            return

        try:
            lst = list(self.transmission.list().values())
            session = self.transmission.session_stats()
        except TransmissionError:
            # XXX: some hint in gui would be nice
            self['list'].setList([])
            self["torrents"].setText("")
            self["upspeed"].setText("")
            self["downspeed"].setText("")
        else:
            sort_type = self.sort_type
            if sort_type == SORT_TYPE_TIME:

                def cmp_func(x, y):
                    x_eta = x.fields['eta']
                    y_eta = y.fields['eta']
                    if x_eta > -1 and y_eta < 0:
                        return 1
                    if x_eta < 0 and y_eta > -1:
                        return -1
                    # note: cmp call inversed because lower eta is "better"
                    return cmp(y_eta, x_eta) or cmp(x.progress, y.progress)

                lst.sort(cmp=cmp_func, reverse=True)
            elif sort_type == SORT_TYPE_PROGRESS:
                lst.sort(key=lambda x: x.progress, reverse=True)
            elif sort_type == SORT_TYPE_SPEED:
                lst.sort(key=lambda x: (x.rateDownload, x.rateUpload),
                         reverse=True)
            # SORT_TYPE_ADDED is what we already have

            list_type = self.list_type
            if list_type == LIST_TYPE_ALL:
                lst = [(x, x.name.encode('utf-8', 'ignore'),
                        str(x.eta
                            or '?:??:??').encode('utf-8'), int(x.progress))
                       for x in lst]
            elif list_type == LIST_TYPE_DOWNLOADING:
                lst = [(x, x.name.encode('utf-8', 'ignore'),
                        str(x.eta
                            or '?:??:??').encode('utf-8'), int(x.progress))
                       for x in lst if x.status == "downloading"]
            else:  #if list_type == LIST_TYPE_SEEDING:
                lst = [(x, x.name.encode('utf-8', 'ignore'),
                        str(x.eta
                            or '?:??:??').encode('utf-8'), int(x.progress))
                       for x in lst if x.status == "seeding"]

            self["torrents"].setText(
                _("Active Torrents: %d/%d") %
                (session.activeTorrentCount, session.torrentCount))
            self["upspeed"].setText(
                _("UL: %d kb/s") % (session.uploadSpeed / 1024))
            self["downspeed"].setText(
                _("DL: %d kb/s") % (session.downloadSpeed / 1024))

            # XXX: this is a little ugly but this way we have the least
            # visible distortion :-)
            index = min(self['list'].index, len(lst) - 1)
            self['list'].setList(lst)
            self['list'].index = index

            self.list = lst
        self.timer.startLongTimer(10)

    def ok(self):
        cur = self['list'].getCurrent()
        if self.transmission is not None and cur:
            #reload(EmissionDetailview)
            self.timer.stop()
            self.session.openWithCallback(
                self.updateList,
                EmissionDetailview.EmissionDetailview,
                self.transmission,
                cur[0],
                self.prevItem,
                self.nextItem,
            )

    def close(self):
        self.timer.stop()
        config.plugins.emission.last_tab.value = self.list_type
        config.plugins.emission.last_tab.save()
        Screen.close(self)
    trk_remote = []
    config['local_lists'].append(cache_file)

trk_local = readLocalLists()
if trk_local:
    dbg('Local lists loaded: {} trackers'.format(len(trk_local)))

trackers = set(trk_remote + trk_local)
dbg('Total trackers: {}'.format(len(trackers)))

if not trackers:
    lg("No trackers loaded, nothing to do")
    exit(1)

try:
    tc = Client(**client)
except:
    if not config['err_on_connect']:
        exit()

    print("Unable to connect to Transmission: ", sys.exc_info()[0])
    raise

torrents = tc.get_torrents()

dbg('{} torrents total'.format(len(torrents)))

for t in torrents:
    if config['status_filter'] and not t.status in config['status_filter']:
        dbg('{}: skipping due to status filter'.format(t.name))
        continue