def start_url(self, uri):
        if self._th:
            raise Exception('Torrent is already started')

        if uri.startswith('http://') or uri.startswith('https://'):
            self._url = uri
            stored = self._cache.get_torrent(url=uri)
            if stored:
                tp = self.info_from_file(stored)
            else:
                tp = {'url': uri}
                resume_data = self._cache.get_resume(url=uri)
                if resume_data:
                    tp['resume_data'] = resume_data
        elif uri.startswith('magnet:'):
            self._url = uri
            stored = self._cache.get_torrent(info_hash=CacheBT.hash_from_magnet(uri))
            if stored:
                tp = self.info_from_file(stored)
            else:
                tp = {'url': uri}
                resume_data = self._cache.get_resume(info_hash=CacheBT.hash_from_magnet(uri))
                if resume_data:
                    tp['resume_data'] = resume_data
        elif os.path.isfile(uri):
            tp = self.info_from_file(uri)
        else:
            raise ValueError("Invalid torrent %s" % uri)

        tp.update(self._torrent_params)
        self._th = self._ses.add_torrent(tp)
        for tr in INITIAL_TRACKERS:
            self._th.add_tracker({'url': tr})
        self._th.set_sequential_download(True)
        time.sleep(1)
        self._th.force_dht_announce()

        self._monitor.start()
        self._dispatcher.do_start(self._th, self._ses)
    def __init__(self, path_to_store,
                 args=None,
                 state_file="",
                 lt=None,
                 **kwargs):
        super(BTClient, self).__init__(path_to_store, args=args)
        self.lt=lt
        self._cache = CacheBT(path_to_store, self.lt)
        self._torrent_params = {'save_path': path_to_store,
                                'storage_mode': self.lt.storage_mode_t.storage_mode_sparse
                                }
        if not state_file:
            state_file=os.path.join(path_to_store,'.btclient_state')
        self._state_file = os.path.expanduser(state_file)
        self._ses = self.lt.session()
        if os.path.exists(self._state_file):
            with open(self._state_file) as f:
                state = pickle.load(f)
                self._ses.load_state(state)
        # self._ses.set_alert_mask(self.lt.alert.category_t.progress_notification)
        if args:
            s = self._ses.get_settings()
            s['download_rate_limit'] = int(round(args.bt_download_limit * 1024))
            s['upload_rate_limit'] = int(round(args.bt_upload_limit * 1024))
            self._ses.set_settings(s)
            self._ses.listen_on(args.listen_port_min, args.listen_port_max)
            self.content_id=args.content_id
        else:
            self._ses.listen_on(6881, 6891)
        self._start_services()
        self._th = None

        self._monitor.add_listener(self._check_ready)
        self._dispatcher = BTClient.Dispatcher(self, lt=self.lt)
        self._dispatcher.add_listener(self._update_ready_pieces)
        self._hash = None
        self._url = None

        if args and args.debug_log and args.trace:
            self.add_monitor_listener(self.debug_download_queue)
            self.add_dispatcher_listener(self.debug_alerts)
class BTClient(BaseClient):
    def __init__(self, path_to_store,
                 args=None,
                 state_file="",
                 lt=None,
                 **kwargs):
        super(BTClient, self).__init__(path_to_store, args=args)
        self.lt=lt
        self._cache = CacheBT(path_to_store, self.lt)
        self._torrent_params = {'save_path': path_to_store,
                                'storage_mode': self.lt.storage_mode_t.storage_mode_sparse
                                }
        if not state_file:
            state_file=os.path.join(path_to_store,'.btclient_state')
        self._state_file = os.path.expanduser(state_file)
        self._ses = self.lt.session()
        if os.path.exists(self._state_file):
            with open(self._state_file) as f:
                state = pickle.load(f)
                self._ses.load_state(state)
        # self._ses.set_alert_mask(self.lt.alert.category_t.progress_notification)
        if args:
            s = self._ses.get_settings()
            s['download_rate_limit'] = int(round(args.bt_download_limit * 1024))
            s['upload_rate_limit'] = int(round(args.bt_upload_limit * 1024))
            self._ses.set_settings(s)
            self._ses.listen_on(args.listen_port_min, args.listen_port_max)
            self.content_id=args.content_id
        else:
            self._ses.listen_on(6881, 6891)
        self._start_services()
        self._th = None

        self._monitor.add_listener(self._check_ready)
        self._dispatcher = BTClient.Dispatcher(self, lt=self.lt)
        self._dispatcher.add_listener(self._update_ready_pieces)
        self._hash = None
        self._url = None

        if args and args.debug_log and args.trace:
            self.add_monitor_listener(self.debug_download_queue)
            self.add_dispatcher_listener(self.debug_alerts)

    @property
    def is_file_complete(self):
        pcs = self._th.status().pieces[self._file.first_piece:self._file.last_piece + 1]
        return all(pcs)

    def _update_ready_pieces(self, alert_type, alert):
        if alert_type == 'read_piece_alert' and self._file:
            self._file.update_piece(alert.piece, alert.buffer)

    def _check_ready(self, s, **kwargs):
        if s.state in [3, 4, 5] and not self._file and s.progress > 0:
            try:
                self._meta_ready(self._th.torrent_file())
            except:
                self._meta_ready(self._th.get_torrent_info())
            logger.debug('Got torrent metadata and start download')
            self.hash = True
            self.hash = Hasher(self._file, self._on_file_ready)

    def _choose_file(self, files, i):
        if not i and i!=0:
            videos = filter(lambda f: VIDEO_EXTS.has_key(os.path.splitext(f.path)[1]), files)
            if not videos:
                raise Exception('No video files in torrent')
            f = sorted(videos, key=lambda f: f.size)[-1]
            i = files.index(f)
            f.index = i
        f=files[i]
        f.index = i
        return f

    def _meta_ready(self, meta):
        fs = meta.files()
        files = fs if isinstance(fs, list) else [fs.at(i) for i in xrange(fs.num_files())]
        f = self._choose_file(files, self.content_id)
        fmap = meta.map_file(f.index, 0, 1)
        self._file = BTFile(f.path, self._base_path, f.index, f.size, fmap, meta.piece_length(),
                            self.prioritize_piece)

        self.prioritize_file()
        print ('File %s pieces (pc=%d, ofs=%d, sz=%d), total_pieces=%d, pc_length=%d' %
               (f.path, fmap.piece, fmap.start, fmap.length,
                     meta.num_pieces(), meta.piece_length()))

        try:
            meta = self._th.torrent_file()
        except:
            meta=self._th.get_torrent_info()
        self._cache.file_complete(meta,
                                  self._url if self._url and self._url.startswith('http') else None)

    def prioritize_piece(self, pc, idx):
        piece_duration = 1000
        min_deadline = 2000
        dl = idx * piece_duration + min_deadline
        self._th.set_piece_deadline(pc, dl, self.lt.deadline_flags.alert_when_available)
        logger.debug("Set deadline %d for piece %d", dl, pc)

        # we do not need to download pieces that are lower then current index, but last two pieces are special because players sometime look at end of file
        if idx == 0 and (self._file.last_piece - pc) > 2:
            for i in xrange(pc - 1):
                self._th.piece_priority(i, 0)
                self._th.reset_piece_deadline(i)

    def prioritize_file(self):
        try:
            meta = self._th.torrent_file()
        except:
            meta=self._th.get_torrent_info()
        priorities = [1 if i >= self._file.first_piece and i <= self.file.last_piece else 0 \
                      for i in xrange(meta.num_pieces())]
        self._th.prioritize_pieces(priorities)

    def encrypt(self):
        # Encryption settings
        print 'Encryption enabling...'
        try:
            encryption_settings = self.lt.pe_settings()
            encryption_settings.out_enc_policy = self.lt.enc_policy(self.lt.enc_policy.forced)
            encryption_settings.in_enc_policy = self.lt.enc_policy(self.lt.enc_policy.forced)
            encryption_settings.allowed_enc_level = self.lt.enc_level.both
            encryption_settings.prefer_rc4 = True
            self._ses.set_pe_settings(encryption_settings)
            print 'Encryption on!'
        except Exception, e:
            print 'Encryption failed! Exception: ' + str(e)
            pass