Пример #1
0
    def test(self):
        c = Context('Root')
        c.start()

        o0 = self.SampleObj()
        Dispatcher.add(obj=o0, parent_obj=None, context=c)

        o1 = self.SampleObj()
        Dispatcher.add(obj=o1, parent_obj=None, context=c)
        Dispatcher.add_listener(src_obj=o1, dst_obj=o0)

        Dispatcher.send(event=Event('1'), src_obj=o0, dst_obj=o1)
        assert str(o1.lastHandler) == 'on1'

        Dispatcher.queue(event=Event('2'), src_obj=o0, dst_obj=o1)
        #assert str(o1.lastHandler) == 'on2'

        Dispatcher.send(event=Event('3'), src_obj=o0, dst_obj=o1)
        #assert str(o1.lastHandler) == 'on3'

        Dispatcher.notify(event=Event('4'), src_obj=o1)
        #assert str(o1.lastHandler) == 'on4'
        Dispatcher.notify(event=Event('4'), src_obj=o1)
        #assert str(o1.lastHandler) == 'on4'

        c.stop()
Пример #2
0
class Client(object):
    INITIAL_TRACKERS=['udp://tracker.openbittorrent.com:80',
                  'udp://tracker.istole.it:80',
                  'udp://open.demonii.com:80',
                  'udp://tracker.coppersurfer.tk:80',
                  'udp://tracker.leechers-paradise.org:6969',
                  'udp://exodus.desync.com:6969',
                  'udp://tracker.publicbt.com:80'
                  "http://exodus.desync.com:6969/announce",
                  "udp://tracker.publicbt.com:80/announce",
                  "udp://tracker.openbittorrent.com:80/announce",
                  "http://tracker.torrentbay.to:6969/announce",
                  "http://fr33dom.h33t.com:3310/announce",
                  "http://tracker.pow7.com/announce",
                  "udp://tracker.ccc.de:80/announce",
                  "http://tracker.bittorrent.am:80/announce",
                  "http://denis.stalker.h3q.com:6969/announce",
                  "udp://tracker.prq.to:80/announce",
                  "udp://tracker.istole.it:80/announce",
                  "udp://open.demonii.com:1337",                  
                  "http://9.rarbg.com:2710/announce",
                  "http://announce.torrentsmd.com:6969/announce",
                  "http://bt.careland.com.cn:6969/announce",
                  "http://explodie.org:6969/announce",
                  "http://mgtracker.org:2710/announce",
                  "http://tracker.best-torrents.net:6969/announce",
                  "http://tracker.tfile.me/announce",
                  "http://tracker.torrenty.org:6969/announce",
                  "http://tracker1.wasabii.com.tw:6969/announce",
                  "udp://9.rarbg.com:2710/announce",
                  "udp://9.rarbg.me:2710/announce",
                  "udp://coppersurfer.tk:6969/announce",
                  "udp://tracker.btzoo.eu:80/announce",                  
                  "http://www.spanishtracker.com:2710/announce",
                  "http://www.todotorrents.com:2710/announce",
                  ]                  

    VIDEO_EXTS={'.avi':'video/x-msvideo','.mp4':'video/mp4','.mkv':'video/x-matroska',
            '.m4v':'video/mp4','.mov':'video/quicktime', '.mpg':'video/mpeg','.ogv':'video/ogg',
            '.ogg':'video/ogg', '.webm':'video/webm', '.ts': 'video/mp2t', '.3gp':'video/3gpp'}

    def __init__(self, url = None, port=None, ip=None, auto_shutdown=True, wait_time=35, timeout=15, auto_delete=False, temp_path=None, is_playing_fnc=None, print_status = False):

        #server
        self.port = port if port else random.randint(8000,8099)
        self.ip = ip if ip else "127.0.0.1"
        self.server = Server((self.ip,self.port), Handler, client=self)


        #Options
        self.temp_path = temp_path if temp_path else os.path.join(os.path.dirname(__file__),"tmp")
        self.is_playing_fnc = is_playing_fnc
        self.timeout = timeout
        self.auto_delete = auto_delete
        self.wait_time= wait_time
        self.auto_shutdown = auto_shutdown
        self.buffer_size = 10
        self.state_file="state"
        self.torrent_paramss={'save_path':self.temp_path,'storage_mode':lt.storage_mode_t.storage_mode_sparse}



        #State
        self.has_meta = False
        self.meta = None
        self.start_time = None
        self.last_connect = 0
        self.connected = False
        self.closed = False
        self.file = None
        self.files = None
        self._th=None


        #Sesion
        self._cache=Cache(self.temp_path)
        self._ses=lt.session()
        self._ses.listen_on(0,0)
        #Cargamos el archivo de estado (si existe)
        if os.path.exists(os.path.join(self.temp_path,self.state_file)):
            try:
                with open(os.path.join(self.temp_path,self.state_file), "rb") as f:
                    state=pickle.load(f)
                    self._ses.load_state(state)
            except:
                pass
                
        
        self._start_services()
        
        #Monitor & Dispatcher
        self._monitor= Monitor(self)
        if print_status:
            self._monitor.add_listener(self.print_status)
        self._monitor.add_listener(self._check_meta)
        self._monitor.add_listener(self.save_state)
        self._monitor.add_listener(self.priorize_start_file)
        self._monitor.add_listener(self.announce_torrent)
        
        if self.auto_shutdown:
            self._monitor.add_listener(self._auto_shutdown)
        self._dispatcher=Dispatcher(self)
        self._dispatcher.add_listener(self._update_ready_pieces)

        #Iniciamos la URL
        if url:
            self.start_url(url)

    def get_play_list(self):
        #Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)

        #Comprobamos que haya archivos de video
        if self.files:
            if len(self.files) > 1:
                return "http://" + self.ip + ":" + str(self.port) + "/playlist.pls"
            else:
                return "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(self.files[0].path)

    def get_files(self):
        #Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)
        files = []

        #Comprobamos que haya archivos de video
        if self.files:
            #Creamos el dict con los archivos
            for file in self.files:
                n = file.path
                u = "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(n)
                s = file.size
                files.append({"name":n,"url":u,"size":s})

        return files

    def announce_torrent(self):
        self._th.force_reannounce()
        self._th.force_dht_announce()

    def _auto_shutdown(self, *args, **kwargs):
        if self.file and self.file.cursor:
            self.last_connect = time.time()
            self.connected = True

        if self.is_playing_fnc and self.is_playing_fnc():
            self.last_connect = time.time()
            self.connected = True

        if self.auto_shutdown:
            #shutdown por haber cerrado el reproductor
            if self.connected and self.is_playing_fnc and not self.is_playing_fnc():
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

            #shutdown por no realizar ninguna conexion
            if (not self.file or not self.file.cursor) and self.start_time and self.wait_time and not self.connected:
                if time.time() - self.start_time - 1 > self.wait_time:
                    self.stop()

            #shutdown tras la ultima conexion
            if (not self.file or not self.file.cursor) and self.timeout and self.connected and not self.is_playing_fnc:
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

    def save_state(self):
        state=self._ses.save_state()
        with open(os.path.join(self.temp_path,self.state_file), 'wb') as f:
            pickle.dump(state,f)

     
    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_meta(self):
        if self.status.state>=3 and  self.status.state <= 5 and not self.has_meta:

            #Guardamos los metadatos
            self.meta = self._th.get_torrent_info()

            #Obtenemos la lista de archivos del meta
            fs=self.meta.files()
            files=fs if isinstance(fs, list) else [fs.at(i) for i in xrange(fs.num_files())]

            #Guardamos la lista de archivos
            self.files=self._choose_files(files)

            #Marcamos el primer archivo como activo
            self.set_file(self.files[0])

            #Damos por iniciada la descarga
            self.start_time = time.time()

            #Guardamos el .torrent en el cahce
            self._cache.file_complete(self._th.get_torrent_info())

            self.has_meta = True



    def _choose_files(self, files, search=None):
        #Obtenemos los archivos que la extension este en la lista
        videos=filter(lambda f: self.VIDEO_EXTS.has_key(os.path.splitext(f.path)[1]), files)

        if not videos:
            raise Exception('No video files in torrent')
        for v in videos:
            videos[videos.index(v)].index = files.index(v)
        return videos


    def set_file(self, f):
        #Seleccionamos el archivo que vamos a servir
        fmap=self.meta.map_file(f.index, 0,1)
        self.file=File(f.path, self.temp_path, f.index, f.size, fmap, self.meta.piece_length(), self)
        self.prioritize_file()
        
    def priorize_start_file(self):
        #Si tenemos un archivo seleccionado, pero no hay conexion, priorizamos el inicio del archivo
        if self.file and not self.file.cursor:
          for x in range(self.file.first_piece,self.file.last_piece):

            if not self._th.have_piece(x):
                for y in range(x,x+self.buffer_size):
                  if y == x+self.buffer_size-1 and not self._th.have_piece(self.file.last_piece):
                    self.prioritize_piece(self.file.last_piece,y-x)
                  else:
                    self.prioritize_piece(y,y-x)
                break
            
        



    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,lt.deadline_flags.alert_when_available)
        
        if idx==0:  # it enough when first piece if prioritize, no need to repeat it for following pieces
            # we do not need to download pieces that are lower then current index, 
            # but last x pieces are special because players often  look at end of file
            # for mp4 metadata - so in this case,  we do not want to stop download of previous pieces
            tail_pieces=9
            if (self.file.last_piece - pc) > tail_pieces:
                for i in xrange(self.file.first_piece,pc):
                    self._th.piece_priority(i,0)
                    self._th.reset_piece_deadline(i)
                
            # If we are skipping back we'd like to re-enable all pieces after this one
            # e.g. give them at least priority 1
            for i in xrange(pc+1, self.file.last_piece+1):
                self._th.piece_priority(i,1)

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


    def download_torrent(self,url):
        from core import scrapertools
        
        data = scrapertools.downloadpage(url)
        return data


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

        if uri.startswith('http://') or uri.startswith('https://'):
            torrent_data = self.download_torrent(uri)
            info = lt.torrent_info(lt.bdecode(torrent_data))
            tp = {'ti':info}
            resume_data= self._cache.get_resume(info_hash=str(info.info_hash()))
            if resume_data:
                tp['resume_data']=resume_data

        elif uri.startswith('magnet:'):
            tp={'url':uri}
            resume_data=self._cache.get_resume(info_hash=Cache.hash_from_magnet(uri))
            if resume_data:
                tp['resume_data']=resume_data

        elif os.path.isfile(uri):
            if os.access(uri,os.R_OK):
                info = lt.torrent_info(uri)
                tp= {'ti':info}
                resume_data= self._cache.get_resume(info_hash=str(info.info_hash()))
                if resume_data:
                    tp['resume_data']=resume_data
            else:
                raise ValueError('Invalid torrent path %s' % uri)
        else:
            raise ValueError("Invalid torrent %s" %uri)
        
        tp.update(self.torrent_paramss)
        self._th = self._ses.add_torrent(tp)
        

        for tr in self.INITIAL_TRACKERS:
            self._th.add_tracker({'url':tr})

        self._th.set_sequential_download(True)
        self._th.force_reannounce()
        self._th.force_dht_announce()

        self._monitor.start()
        self._dispatcher.do_start(self._th, self._ses)
        self.server.run()

        
    def stop(self):
        self._dispatcher.stop()
        self._dispatcher.join()
        self._monitor.stop()
        self.server.stop()
        self._dispatcher.stop()
        if self._ses:
            self._ses.pause()
            if self._th:
                self.save_resume()
            self.save_state()
        self._stop_services()
        self._ses.remove_torrent(self._th, self.auto_delete)
        del self._ses
        self.closed = True

    def _start_services(self):
        self._ses.add_dht_router("router.bittorrent.com",6881)
        self._ses.add_dht_router("router.utorrent.com",6881)
        self._ses.add_dht_router("router.bitcomet.com",554)
        self._ses.add_dht_router("router.utorrent.com",6881)
        self._ses.add_dht_router("dht.transmissionbt.com",6881)            
        self._ses.start_dht()
        self._ses.start_lsd()
        self._ses.start_upnp()
        self._ses.start_natpmp()
        
        
    def _stop_services(self):
        self._ses.stop_natpmp()
        self._ses.stop_upnp()
        self._ses.stop_lsd()
        self._ses.stop_dht()


    def save_resume(self):
        if self._th.need_save_resume_data() and self._th.is_valid() and self.meta:
            r = ResumeData(self)
            start=time.time()
            while (time.time() - start) <= 5 :
                if r.data or r.failed:
                    break
                time.sleep(0.1)
            if r.data:
                self._cache.save_resume(self.unique_file_id,lt.bencode(r.data))

    @property
    def status(self):
        if self._th:
            s = self._th.status()
            s._download_rate = s.download_rate / 1000

            if self.file:
                pieces=s.pieces[self.file.first_piece:self.file.last_piece]
                progress= float(sum(pieces))/len(pieces)
            else:
                progress=0

            s.progress_file=progress * 100
            s.file_size=self.file.size / 1048576.0 if self.file else 0

            if self.file and self.file.cursor:
                percent = len(self.file.cursor.cache)
                percent = percent * 100 / self.buffer_size
                s.buffer = int(percent)

            elif self.file:
                bp = [True if (x == self.buffer_size -1 and self._th.have_piece(self.file.last_piece)) or (x < self.buffer_size -1 and self._th.have_piece(x)) else False for x in range(self.buffer_size ) ]
                percent = len([a for a in bp if a == True])
                percent = percent * 100 / self.buffer_size
                s.buffer = int(percent)

            else:
                s.buffer = 0

            if self.auto_shutdown:
                if self.connected:
                    if self.timeout:
                        s.timeout = int(self.timeout - (time.time() - self.last_connect -1))
                        if self.file and self.file.cursor:
                             s.timeout = self.timeout
                        if s.timeout < 0: s.timeout = "Cerrando"
                    else:
                        s.timeout = "---"
                else:
                    if self.start_time and self.wait_time:
                        s.timeout = int(self.wait_time - (time.time() - self.start_time -1))
                        if s.timeout < 0: s.timeout = "Cerrando"
                    else:
                        s.timeout = "---"

            else:
                s.timeout = "Off"

            STATE_STR = ['En cola', 'Comprobando', 'Descargando metadata', \
                    'Descargando', 'Finalizado', 'Seeding', 'Allocating', 'Comprobando fastresume']

            s.str_state = STATE_STR[s.state]

            if self._ses.dht_state() is not None:
                s.dht_state = "On"
                s.dht_nodes=self._ses.status().dht_nodes
            else:
                s.dht_state = "Off"
                s.dht_nodes = 0

            s.trackers= len(self._th.trackers())

            s.dht_peers = 0
            s.trk_peers = 0
            s.pex_peers = 0
            s.lsd_peers = 0

            for peer in self._th.get_peer_info():
                if peer.source & 1:
                    s.trk_peers +=1
                if peer.source & 2:
                    s.dht_peers +=1
                if peer.source & 4:
                    s.pex_peers +=1
                if peer.source & 8:
                    s.lsd_peers +=1

            return s

    def print_status(self):
        s = self.status

        archivo = "N/D"
        percent = s.buffer

        if percent < 75: color= "33"
        if percent < 40: color= "31"
        if percent > 75: color= "32"

        buffer = "\033[%sm%s%s\033[39m" % ( color ,u"\u25A0" * (percent/10), u"\u25A1" *(10-percent/10))

        print '\r\033[39;m%.2f%% de %.1fMB %s | %.1f kB/s | #%s %s | AutoClose: %s | S: %d(%d) P: %d(%d)) | TRK: %d DHT: %d PEX: %d LSD %d | DHT:%s (%d) | Trackers: %d' % \
            (s.progress_file , s.file_size, s.str_state, s._download_rate ,archivo, buffer, s.timeout , s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.trk_peers,s.dht_peers, s.pex_peers, s.lsd_peers, s.dht_state, s.dht_nodes, s.trackers),
Пример #3
0
class Client(object):
    INITIAL_TRACKERS = [
        'udp://tracker.openbittorrent.com:80', 'udp://tracker.istole.it:80',
        'udp://open.demonii.com:80', 'udp://tracker.coppersurfer.tk:80',
        'udp://tracker.leechers-paradise.org:6969',
        'udp://exodus.desync.com:6969', 'udp://tracker.publicbt.com:80',
        'http://tracker.torrentbay.to:6969/announce',
        'http://tracker.pow7.com/announce', 'udp://tracker.ccc.de:80/announce',
        'udp://open.demonii.com:1337', 'http://9.rarbg.com:2710/announce',
        'http://bt.careland.com.cn:6969/announce',
        'http://explodie.org:6969/announce',
        'http://mgtracker.org:2710/announce',
        'http://tracker.best-torrents.net:6969/announce',
        'http://tracker.tfile.me/announce',
        'http://tracker1.wasabii.com.tw:6969/announce',
        'udp://9.rarbg.com:2710/announce', 'udp://9.rarbg.me:2710/announce',
        'udp://coppersurfer.tk:6969/announce',
        'http://www.spanishtracker.com:2710/announce',
        'http://www.todotorrents.com:2710/announce'
    ]  ### Added some trackers from MCT

    VIDEO_EXTS = {
        '.avi': 'video/x-msvideo',
        '.mp4': 'video/mp4',
        '.mkv': 'video/x-matroska',
        '.m4v': 'video/mp4',
        '.mov': 'video/quicktime',
        '.mpg': 'video/mpeg',
        '.ogv': 'video/ogg',
        '.ogg': 'video/ogg',
        '.webm': 'video/webm',
        '.ts': 'video/mp2t',
        '.3gp': 'video/3gpp',
        '.rar': 'video/unrar'
    }

    def __init__(self,
                 url=None,
                 port=None,
                 ip=None,
                 auto_shutdown=True,
                 wait_time=20,
                 timeout=5,
                 auto_delete=True,
                 temp_path=None,
                 is_playing_fnc=None,
                 print_status=False):

        # server
        if port:
            self.port = port
        else:
            self.port = random.randint(8000, 8099)
        if ip:
            self.ip = ip
        else:
            self.ip = "127.0.0.1"
        self.server = Server((self.ip, self.port), Handler, client=self)

        # Options
        if temp_path:
            self.temp_path = temp_path
        else:
            self.temp_path = DOWNLOAD_PATH
        self.is_playing_fnc = is_playing_fnc
        self.timeout = timeout
        self.auto_delete = auto_delete
        self.wait_time = wait_time
        self.auto_shutdown = auto_shutdown
        self.buffer_size = BUFFER
        self.first_pieces_priorize = BUFFER
        self.last_pieces_priorize = 5
        self.state_file = "state"
        try:
            self.torrent_paramss = {
                'save_path': self.temp_path,
                'storage_mode': lt.storage_mode_t.storage_mode_allocate
            }
        except Exception as e:
            try:
                do = xbmcgui.Dialog()
                e = e1 or e2
                do.ok(
                    config.get_localized_string(30035) + 'BT Libtorrent',
                    config.get_localized_string(30036),
                    config.get_localized_string(60015), str(e))
            except:
                pass
            return

        # State
        self.has_meta = False
        self.meta = None
        self.start_time = None
        self.last_connect = 0
        self.connected = False
        self.closed = False
        self.file = None
        self.files = None
        self._th = None
        self.seleccion = 0
        self.index = 0

        # Sesion
        self._cache = Cache(self.temp_path)
        self._ses = lt.session()
        #self._ses.listen_on(0, 0)                                              ### ALFA: it blocks repro of some .torrents
        # Cargamos el archivo de estado (si existe)
        """                                                                     ### ALFA: it blocks repro of some .torrents
        if os.path.exists(os.path.join(self.temp_path, self.state_file)):
            try:
                f = open(os.path.join(self.temp_path, self.state_file), "rb")
                state = pickle.load(f)
                self._ses.load_state(state)
                f.close()
            except:
                pass
        """

        self._start_services()

        # Monitor & Dispatcher
        self._monitor = Monitor(self)
        if print_status:
            self._monitor.add_listener(self.print_status)
        self._monitor.add_listener(self._check_meta)
        self._monitor.add_listener(self.save_state)
        self._monitor.add_listener(self.priorize_start_file)
        self._monitor.add_listener(self.announce_torrent)

        if self.auto_shutdown:
            self._monitor.add_listener(self._auto_shutdown)

        self._dispatcher = Dispatcher(self)
        self._dispatcher.add_listener(self._update_ready_pieces)

        # Iniciamos la URL
        if url:
            self.start_url(url)

    def set_speed_limits(self, download=0, upload=0):
        """
        Función encargada de poner límites a la velocidad de descarga o subida
        """
        if isinstance(download, int) and download > 0:
            self._th.set_download_limit(download * 1024)
        if isinstance(upload, int) and download > 0:
            self._th.set_upload_limit(upload * 1024)

    def get_play_list(self):
        """
        Función encargada de generar el playlist
        """
        # Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)

        # Comprobamos que haya archivos de video
        if self.files:
            if len(self.files) > 1:
                return "http://" + self.ip + ":" + str(
                    self.port) + "/playlist.pls"
            else:
                return "http://" + self.ip + ":" + str(
                    self.port) + "/" + urllib.quote(self.files[0].path)

    def get_files(self):
        """
        Función encargada de genera el listado de archivos
        """
        # Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)
        files = []

        # Comprobamos que haya archivos de video
        if self.files:
            # Creamos el dict con los archivos
            for file in self.files:
                n = file.path
                u = "http://" + self.ip + ":" + str(
                    self.port) + "/" + urllib.quote(n)
                s = file.size
                files.append({"name": n, "url": u, "size": s})

        return files

    def _find_files(self, files, search=None):
        """
        Función encargada de buscar los archivos reproducibles del torrent
        """
        self.total_size = 0
        # Obtenemos los archivos que la extension este en la lista
        videos = filter(
            lambda f: self.VIDEO_EXTS.has_key(os.path.splitext(f.path)[1]),
            files)

        if not videos:
            raise Exception('No video files in torrent')
        for v in videos:
            self.total_size += v.size  ### ALFA
            videos[videos.index(v)].index = files.index(v)
        return videos

    def set_file(self, f):
        """
        Función encargada de seleccionar el archivo que vamos a servir y por tanto, priorizar su descarga
        """
        # Seleccionamos el archivo que vamos a servir
        fmap = self.meta.map_file(f.index, 0, 1)
        self.file = File(f.path, self.temp_path, f.index, f.size, fmap,
                         self.meta.piece_length(), self)
        if self.seleccion < 0:  ### ALFA
            self.file.first_piece = 0  ### ALFA
            self.file.last_piece = self.meta.num_pieces()  ### ALFA
            self.file.size = self.total_size  ### ALFA
        self.prioritize_file()

    def prioritize_piece(self, pc, idx):
        """
        Función encargada de priorizar una determinada pieza
        """
        piece_duration = 1000
        min_deadline = 2000
        dl = idx * piece_duration + min_deadline
        """                                                                     ### ALFA
        try:
            self._th.set_piece_deadline(pc, dl, lt.deadline_flags.alert_when_available)
        except:
            pass
        """

        if idx == 0:
            tail_pieces = 9
            # Piezas anteriores a la primera se desactivan
            if (self.file.last_piece - pc) > tail_pieces:
                for i in xrange(self.file.first_piece, pc):
                    self._th.piece_priority(i, 0)
                    self._th.reset_piece_deadline(i)

            # Piezas siguientes a la primera se activan
            for i in xrange(pc + 1, self.file.last_piece + 1):
                #self._th.piece_priority(i, 0)
                self._th.piece_priority(i, 1)

    def prioritize_file(self):
        """
        Función encargada de priorizar las piezas correspondientes al archivo seleccionado en la funcion set_file()
        """
        priorities = []
        for i in xrange(self.meta.num_pieces()):
            if i >= self.file.first_piece and i <= self.file.last_piece:
                priorities.append(1)
            else:
                if self.index < 0:
                    priorities.append(1)  ### ALFA
                else:
                    priorities.append(0)  ### ALFA

        self._th.prioritize_pieces(priorities)

        x = 0
        for i, _set in enumerate(self._th.piece_priorities()):
            if _set > 0: x += 1
            #logger.info("***** Nº Pieza: %s: %s" % (i, str(_set)))
        logger.info("***** Piezas %s : Activas: %s" % (str(i + 1), str(x)))
        logger.info("***** first_piece %s : last_piece: %s" %
                    (str(self.file.first_piece), str(self.file.last_piece)))

    def download_torrent(self, url):
        """
        Función encargada de descargar un archivo .torrent
        """
        from core import httptools

        data = httptools.downloadpage(url).data
        return data

    def start_url(self, uri):
        """
        Función encargada iniciar la descarga del torrent desde la url, permite:
          - Url apuntando a un .torrent
          - Url magnet
          - Archivo .torrent local
        """

        if self._th:
            raise Exception('Torrent is already started')

        if uri.startswith('http://') or uri.startswith('https://'):
            torrent_data = self.download_torrent(uri)
            info = lt.torrent_info(lt.bdecode(torrent_data))
            tp = {'ti': info}
            resume_data = self._cache.get_resume(
                info_hash=str(info.info_hash()))
            if resume_data:
                tp['resume_data'] = resume_data

        elif uri.startswith('magnet:'):
            tp = {'url': uri}
            resume_data = self._cache.get_resume(
                info_hash=Cache.hash_from_magnet(uri))
            if resume_data:
                tp['resume_data'] = resume_data

        elif os.path.isfile(uri):
            if os.access(uri, os.R_OK):
                info = lt.torrent_info(uri)
                tp = {'ti': info}
                resume_data = self._cache.get_resume(
                    info_hash=str(info.info_hash()))
                if resume_data:
                    tp['resume_data'] = resume_data
            else:
                raise ValueError('Invalid torrent path %s' % uri)
        else:
            raise ValueError("Invalid torrent %s" % uri)

        tp.update(self.torrent_paramss)
        self._th = self._ses.add_torrent(tp)

        for tr in self.INITIAL_TRACKERS:
            self._th.add_tracker({'url': tr})

        self._th.set_sequential_download(True)
        self._th.force_reannounce()
        self._th.force_dht_announce()

        self._monitor.start()
        self._dispatcher.do_start(self._th, self._ses)
        self.server.run()

    def stop(self):
        """
        Función encargada de de detener el torrent y salir
        """
        self._dispatcher.stop()
        self._dispatcher.join()
        self._monitor.stop()
        self.server.stop()
        self._dispatcher.stop()
        if self._ses:
            self._ses.pause()
            if self._th:
                self.save_resume()
            self.save_state()
        self._stop_services()
        self._ses.remove_torrent(self._th, self.auto_delete)
        del self._ses
        self.closed = True

    def pause(self):
        """
        Función encargada de de pausar el torrent
        """
        self._ses.pause()

    def _start_services(self):
        """
        Función encargada de iniciar los servicios de libtorrent: dht, lsd, upnp, natpnp
        """
        self._ses.add_dht_router("router.bittorrent.com", 6881)
        self._ses.add_dht_router("router.bitcomet.com", 554)
        self._ses.add_dht_router("router.utorrent.com", 6881)
        self._ses.add_dht_router("dht.transmissionbt.com", 6881)  ### from MCT
        self._ses.start_dht()
        self._ses.start_lsd()
        self._ses.start_upnp()
        self._ses.start_natpmp()

    def _stop_services(self):
        """
        Función encargada de detener los servicios de libtorrent: dht, lsd, upnp, natpnp
        """
        self._ses.stop_natpmp()
        self._ses.stop_upnp()
        self._ses.stop_lsd()
        self._ses.stop_dht()

    def save_resume(self):
        """
        Función encargada guardar los metadatos para continuar una descarga mas rapidamente
        """
        if self._th.need_save_resume_data() and self._th.is_valid(
        ) and self.meta:
            r = ResumeData(self)
            start = time.time()
            while (time.time() - start) <= 5:
                if r.data or r.failed:
                    break
                time.sleep(0.1)
            if r.data:
                self._cache.save_resume(self.unique_file_id,
                                        lt.bencode(r.data))

    @property
    def status(self):
        """
        Función encargada de devolver el estado del torrent
        """
        if self._th:
            s = self._th.status()
            # Download Rate
            s._download_rate = s.download_rate / 1024

            # Progreso del archivo
            if self.file:
                pieces = s.pieces[self.file.first_piece:
                                  self.file.last_piece]  ### ALFA
                progress = float(sum(pieces)) / len(pieces)
                s.pieces_len = len(pieces)  ### ALFA
                s.pieces_sum = sum(pieces)  ### ALFA
                #logger.info('***** Estado piezas: %s' % pieces)
            else:
                progress = 0
                s.pieces_len = 0  ### ALFA
                s.pieces_sum = 0  ### ALFA

            s.progress_file = progress * 100

            # Tamaño del archivo
            s.file_name = ''  ### ALFA
            s.seleccion = ''  ### ALFA

            if self.file:
                s.seleccion = self.seleccion  ### ALFA
                s.file_name = self.file.path  ### ALFA
                s.file_size = self.file.size / 1048576.0
            else:
                s.file_size = 0

            # Estado del buffer
            if self.file and self.file.cursor:  # Con una conexion activa: Disponible vs Posicion del reproductor
                percent = len(self.file.cursor.cache)
                percent = percent * 100 / self.buffer_size
                s.buffer = int(percent)

            elif self.file:  # Sin una conexion activa: Pre-buffer antes de iniciar
                # El Pre-buffer consta de dos partes_
                # 1. Buffer al inicio del archivo para que el reproductor empieze sin cortes
                # 2. Buffer al final del archivo (en algunos archivos el reproductor mira el final del archivo antes de comenzar)
                bp = []

                # El tamaño del buffer de inicio es el tamaño del buffer menos el tamaño del buffer del final
                first_pieces_priorize = self.buffer_size - self.last_pieces_priorize

                # Comprobamos qué partes del buffer del inicio estan disponibles
                for x in range(first_pieces_priorize):
                    if self._th.have_piece(self.file.first_piece + x):
                        bp.append(True)
                    else:
                        bp.append(False)

                # Comprobamos qué partes del buffer del final estan disponibles
                for x in range(self.last_pieces_priorize):
                    if self._th.have_piece(self.file.last_piece - x):
                        bp.append(True)
                    else:
                        bp.append(False)

                s.buffer = int(sum(bp) * 100 / self.buffer_size)

            else:  # Si no hay ningun archivo seleccionado: No hay buffer
                s.buffer = 0

            # Tiempo restante para cerrar en caso de tener el timeout activo
            if self.auto_shutdown:
                if self.connected:
                    if self.timeout:
                        s.timeout = int(self.timeout -
                                        (time.time() - self.last_connect - 1))
                        if self.file and self.file.cursor:
                            s.timeout = self.timeout
                        if s.timeout < 0: s.timeout = "Cerrando"
                    else:
                        s.timeout = "---"
                else:
                    if self.start_time and self.wait_time:
                        s.timeout = int(self.wait_time -
                                        (time.time() - self.start_time - 1))
                        if s.timeout < 0: s.timeout = "Cerrando"
                    else:
                        s.timeout = "---"

            else:
                s.timeout = "Off"

            # Estado de la descarga
            STATE_STR = ['Queued', 'Checking', 'Downloading Metadata', \
                         'Downloading', 'Finalized', 'Seeding', 'Allocating', 'Checking Fastresume']
            s.str_state = STATE_STR[s.state]

            # Estado DHT
            if self._ses.dht_state() is not None:
                s.dht_state = "On"
                s.dht_nodes = self._ses.status().dht_nodes
            else:
                s.dht_state = "Off"
                s.dht_nodes = 0

            # Cantidad de Trackers
            s.trackers = len(self._th.trackers())

            # Origen de los peers
            s.dht_peers = 0
            s.trk_peers = 0
            s.pex_peers = 0
            s.lsd_peers = 0

            for peer in self._th.get_peer_info():
                if peer.source & 1:
                    s.trk_peers += 1
                if peer.source & 2:
                    s.dht_peers += 1
                if peer.source & 4:
                    s.pex_peers += 1
                if peer.source & 8:
                    s.lsd_peers += 1

            return s

    """
    Servicios:
      - Estas funciones se ejecutan de forma automatica cada x tiempo en otro Thread.
      - Estas funciones son ejecutadas mientras el torrent esta activo algunas pueden desactivarse 
        segun la configuracion como por ejemplo la escritura en el log
    """

    def _auto_shutdown(self, *args, **kwargs):
        """
        Servicio encargado de autoapagar el servidor
        """
        if self.file and self.file.cursor:
            self.last_connect = time.time()
            self.connected = True

        if self.is_playing_fnc and self.is_playing_fnc():
            self.last_connect = time.time()
            self.connected = True

        if self.auto_shutdown:
            # shudown por haber cerrado el reproductor
            if self.connected and self.is_playing_fnc and not self.is_playing_fnc(
            ):
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

            # shutdown por no realizar ninguna conexion
            if (
                    not self.file or not self.file.cursor
            ) and self.start_time and self.wait_time and not self.connected:
                if time.time() - self.start_time - 1 > self.wait_time:
                    self.stop()

            # shutdown tras la ultima conexion
            if (
                    not self.file or not self.file.cursor
            ) and self.timeout and self.connected and not self.is_playing_fnc:
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

    def announce_torrent(self):
        """
        Servicio encargado de anunciar el torrent
        """
        self._th.force_reannounce()
        self._th.force_dht_announce()

    def save_state(self):
        """
        Servicio encargado de guardar el estado
        """
        state = self._ses.save_state()
        f = open(os.path.join(self.temp_path, self.state_file), 'wb')
        pickle.dump(state, f)
        f.close()

    def _update_ready_pieces(self, alert_type, alert):
        """
        Servicio encargado de informar que hay una pieza disponible
        """
        if alert_type == 'read_piece_alert' and self.file:
            self.file.update_piece(alert.piece, alert.buffer)

    def _check_meta(self):
        """
        Servicio encargado de comprobar si los metadatos se han descargado
        """
        if self.status.state >= 3 and self.status.state <= 5 and not self.has_meta:

            # Guardamos los metadatos
            self.meta = self._th.get_torrent_info()

            # Obtenemos la lista de archivos del meta
            fs = self.meta.files()
            if isinstance(fs, list):
                files = fs
            else:
                files = [fs.at(i) for i in xrange(fs.num_files())]

            # Guardamos la lista de archivos
            self.files = self._find_files(files)

            # Si hay varios vídeos (no RAR), se selecciona el vídeo o "todos"
            lista = []
            seleccion = 0
            for file in self.files:
                if '.rar' in str(file.path):
                    seleccion = -9
                lista += [os.path.split(str(file.path))[1]]
            if len(lista) > 1 and seleccion >= 0:
                d = xbmcgui.Dialog()
                seleccion = d.select(
                    msg_header + config.get_localized_string(30034), lista)

            if seleccion < 0:
                index = 0
                self.index = seleccion
            else:
                index = seleccion
                self.index = self.files[index].index
            self.seleccion = seleccion

            # Marcamos el primer archivo como activo
            self.set_file(self.files[index])

            # Damos por iniciada la descarga
            self.start_time = time.time()

            # Guardamos el .torrent en el cache
            self._cache.file_complete(self._th.get_torrent_info())

            self.has_meta = True

    def priorize_start_file(self):
        '''
        Servicio encargado de priorizar el principio y final de archivo cuando no hay conexion
        '''
        if self.file and not self.file.cursor:
            num_start_pieces = self.buffer_size - self.last_pieces_priorize  # Cantidad de piezas a priorizar al inicio
            num_end_pieces = self.last_pieces_priorize  # Cantidad de piezas a priorizar al final

            pieces_count = 0
            # Priorizamos las ultimas piezas
            for y in range(self.file.last_piece - num_end_pieces,
                           self.file.last_piece + 1):
                if not self._th.have_piece(y):
                    self.prioritize_piece(y, pieces_count)
                    pieces_count += 1

            # Priorizamos las primeras piezas
            for y in range(self.file.first_piece, self.file.last_piece + 1):
                if not self._th.have_piece(y):
                    if pieces_count == self.buffer_size:
                        break
                    self.prioritize_piece(y, pieces_count)
                    pieces_count += 1

    def print_status(self):
        '''
        Servicio encargado de mostrar en el log el estado de la descarga
        '''
        s = self.status  ### ALFA
        if self.seleccion >= 0:
            archivo = self.seleccion + 1
        else:
            archivo = self.seleccion

        logger.info(
            '%.2f%% de %.1fMB %s | %.1f kB/s | #%s %d%% | AutoClose: %s | S: %d(%d) P: %d(%d)) | TRK: %d DHT: %d PEX: %d LSD %d | DHT:%s (%d) | Trakers: %d | Pieces: %d (%d)' % \
            (s.progress_file, s.file_size, s.str_state, s._download_rate, archivo, s.buffer, s.timeout, s.num_seeds, \
             s.num_complete, s.num_peers, s.num_incomplete, s.trk_peers, s.dht_peers, s.pex_peers, s.lsd_peers,
             s.dht_state, s.dht_nodes, s.trackers, s.pieces_sum, s.pieces_len)) ### ALFA
class Client(object):
    INITIAL_TRACKERS=['udp://tracker.openbittorrent.com:80',
                  'udp://tracker.istole.it:80',
                  'udp://open.demonii.com:80',
                  'udp://tracker.coppersurfer.tk:80',
                  'udp://tracker.leechers-paradise.org:6969',
                  'udp://exodus.desync.com:6969',
                  'udp://tracker.publicbt.com:80']

    VIDEO_EXTS={'.avi':'video/x-msvideo','.mp4':'video/mp4','.mkv':'video/x-matroska',
            '.m4v':'video/mp4','.mov':'video/quicktime', '.mpg':'video/mpeg','.ogv':'video/ogg',
            '.ogg':'video/ogg', '.webm':'video/webm', '.ts': 'video/mp2t', '.3gp':'video/3gpp'}

    def __init__(self, url = None, port=None, ip=None, auto_shutdown=True, wait_time=20, timeout=5, auto_delete=True, temp_path=None, is_playing_fnc=None, print_status = False):

        #server
        if port:
          self.port = port
        else:
          self.port = random.randint(8000,8099)
        if ip:
          self.ip = ip
        else:
          self.ip = "127.0.0.1"
        self.server = Server((self.ip,self.port), Handler, client=self)


        #Options
        if temp_path:
          self.temp_path = temp_path  
        else:
          self.temp_path = os.path.join(os.path.dirname(__file__),"tmp")
        self.is_playing_fnc = is_playing_fnc
        self.timeout = timeout
        self.auto_delete = auto_delete
        self.wait_time= wait_time
        self.auto_shutdown = auto_shutdown
        self.buffer_size = 15 
        self.last_pieces_priorize = 5
        self.state_file="state"
        self.torrent_paramss={'save_path':self.temp_path,'storage_mode':lt.storage_mode_t.storage_mode_sparse}


        #State
        self.has_meta = False
        self.meta = None
        self.start_time = None
        self.last_connect = 0
        self.connected = False
        self.closed = False
        self.file = None
        self.files = None
        self._th=None


        #Sesion
        self._cache=Cache(self.temp_path)
        self._ses=lt.session()
        self._ses.listen_on(0,0)
        #Cargamos el archivo de estado (si esxiste)
        if os.path.exists(os.path.join(self.temp_path,self.state_file)):
            try:
                f =  open(os.path.join(self.temp_path,self.state_file), "rb")
                state=pickle.load(f)
                self._ses.load_state(state)
                f.close()
            except:
                pass
                
        
        self._start_services()
        
        #Monitor & Dispatcher
        self._monitor= Monitor(self)
        if print_status:
            self._monitor.add_listener(self.print_status)
        self._monitor.add_listener(self._check_meta)
        self._monitor.add_listener(self.save_state)
        self._monitor.add_listener(self.priorize_start_file)
        self._monitor.add_listener(self.announce_torrent)
        
        if self.auto_shutdown:
            self._monitor.add_listener(self._auto_shutdown)
            
        self._dispatcher=Dispatcher(self)
        self._dispatcher.add_listener(self._update_ready_pieces)

        #Iniciamos la URL
        if url:
            self.start_url(url)

    def get_play_list(self):
        """
        Función encargada de generar el playlist
        """
        #Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)

        #Comprobamos que haya archivos de video
        if self.files:
            if len(self.files) > 1:
                return "http://" + self.ip + ":" + str(self.port) + "/playlist.pls"
            else:
                return "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(self.files[0].path)

    def get_files(self):
        """
        Función encargada de genera el listado de archivos
        """
        #Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)
        files = []

        #Comprobamos que haya archivos de video
        if self.files:
            #Creamos el dict con los archivos
            for file in self.files:
                n = file.path
                u = "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(n)
                s = file.size
                files.append({"name":n,"url":u,"size":s})

        return files


    def _find_files(self, files, search=None):
        """
        Función encargada de buscar los archivos reproducibles del torrent
        """
        #Obtenemos los archivos que la extension este en la lista
        videos=filter(lambda f: self.VIDEO_EXTS.has_key(os.path.splitext(f.path)[1]), files)

        if not videos:
            raise Exception('No video files in torrent')
        for v in videos:
            videos[videos.index(v)].index = files.index(v)
        return videos


    def set_file(self, f):
        """
        Función encargada de seleccionar el archivo que vamos a servir y por tanto, priorizar su descarga
        """
        #Seleccionamos el archivo que vamos a servir
        fmap=self.meta.map_file(f.index, 0,1)
        self.file=File(f.path, self.temp_path, f.index, f.size, fmap, self.meta.piece_length(), self)
        self.prioritize_file()
        

    def prioritize_piece(self, pc, idx):
        """
        Función encargada de priorizar una determinada pieza
        """
        piece_duration=1000
        min_deadline=2000
        dl=idx*piece_duration+min_deadline
        self._th.set_piece_deadline(pc, dl,lt.deadline_flags.alert_when_available)
        
        if idx==0: 
            tail_pieces=9
            #Piezas anteriores a la primera se desactivan
            if (self.file.last_piece - pc) > tail_pieces:
                for i in xrange(self.file.first_piece,pc):
                    self._th.piece_priority(i,0)
                    self._th.reset_piece_deadline(i)
                    
            #Piezas siguientes a la primera se activan
            for i in xrange(pc+1, self.file.last_piece+1):
                self._th.piece_priority(i,1)

    def prioritize_file(self):
        """
        Función encargada de priorizar las piezas correspondientes al archivo seleccionado en la funcion set_file()
        """
        priorities = []
        for i in xrange(self.meta.num_pieces()):
          if i>= self.file.first_piece and i<= self.file.last_piece:
            priorities.append(1)
          else:
            priorities.append(0)
        self._th.prioritize_pieces(priorities)


    def download_torrent(self,url):
        """
        Función encargada de descargar un archivo .torrent
        """
        from core import scrapertools
        
        data = scrapertools.downloadpage(url)
        return data


    def start_url(self, uri):
        """
        Función encargada iniciar la descarga del torrent desde la url, permite:
          - Url apuntando a un .torrent
          - Url magnet
          - Archivo .torrent local
        """

        if self._th:
            raise Exception('Torrent is already started')

        if uri.startswith('http://') or uri.startswith('https://'):
            torrent_data = self.download_torrent(uri)
            info = lt.torrent_info(lt.bdecode(torrent_data))
            tp = {'ti':info}
            resume_data= self._cache.get_resume(info_hash=str(info.info_hash()))
            if resume_data:
                tp['resume_data']=resume_data

        elif uri.startswith('magnet:'):
            tp={'url':uri}
            resume_data=self._cache.get_resume(info_hash=Cache.hash_from_magnet(uri))
            if resume_data:
                tp['resume_data']=resume_data

        elif os.path.isfile(uri):
            if os.access(uri,os.R_OK):
                info = lt.torrent_info(uri)
                tp= {'ti':info}
                resume_data= self._cache.get_resume(info_hash=str(info.info_hash()))
                if resume_data:
                    tp['resume_data']=resume_data
            else:
                raise ValueError('Invalid torrent path %s' % uri)
        else:
            raise ValueError("Invalid torrent %s" %uri)
        
        tp.update(self.torrent_paramss)
        self._th = self._ses.add_torrent(tp)
        

        for tr in self.INITIAL_TRACKERS:
            self._th.add_tracker({'url':tr})

        self._th.set_sequential_download(True)
        self._th.force_reannounce()
        self._th.force_dht_announce()

        self._monitor.start()
        self._dispatcher.do_start(self._th, self._ses)
        self.server.run()

        
    def stop(self):
        """
        Función encargada de de detener el torrent y salir
        """
        self._dispatcher.stop()
        self._dispatcher.join()
        self._monitor.stop()
        self.server.stop()
        self._dispatcher.stop()
        if self._ses:
            self._ses.pause()
            if self._th:
                self.save_resume()
            self.save_state()
        self._stop_services()
        self._ses.remove_torrent(self._th, self.auto_delete)
        del self._ses
        self.closed = True

    def _start_services(self):
        """
        Función encargada de iniciar los servicios de libtorrent: dht, lsd, upnp, natpnp
        """
        self._ses.add_dht_router("router.bittorrent.com",6881)
        self._ses.add_dht_router("router.bitcomet.com",554)
        self._ses.add_dht_router("router.utorrent.com",6881)
        self._ses.start_dht()
        self._ses.start_lsd()
        self._ses.start_upnp()
        self._ses.start_natpmp()
        
        
    def _stop_services(self):
        """
        Función encargada de detener los servicios de libtorrent: dht, lsd, upnp, natpnp
        """
        self._ses.stop_natpmp()
        self._ses.stop_upnp()
        self._ses.stop_lsd()
        self._ses.stop_dht()


    def save_resume(self):
        """
        Función encargada guardar los metadatos para continuar una descarga mas rapidamente
        """
        if self._th.need_save_resume_data() and self._th.is_valid() and self.meta:
            r = ResumeData(self)
            start=time.time()
            while (time.time() - start) <= 5 :
                if r.data or r.failed:
                    break
                time.sleep(0.1)
            if r.data:
                self._cache.save_resume(self.unique_file_id,lt.bencode(r.data))

    @property
    def status(self):
        """
        Función encargada de devolver el estado del torrent
        """
        if self._th:
            s = self._th.status()
            #Download Rate
            s._download_rate = s.download_rate / 1000
            
            #Progreso del archivo
            if self.file:
                pieces=s.pieces[self.file.first_piece:self.file.last_piece]
                progress= float(sum(pieces))/len(pieces)
            else:
                progress=0

            s.progress_file=progress * 100
            
            #Tamaño del archivo
            if self.file:
              s.file_size=self.file.size / 1048576.0 
            else: 
              s.file_size=0
            
            #Estado del buffer
            if self.file and self.file.cursor: #Con una conexion activa: Disponible vs Posicion del reproductor
                percent = len(self.file.cursor.cache)
                percent = percent * 100 / self.buffer_size
                s.buffer = int(percent)

            elif self.file: #Sin una conexion activa: Pre-buffer antes de iniciar
                #El Pre-buffer consta de dos partes_
                # 1. Buffer al inicio del archivo para que el reproductor empieze sin cortes
                # 2. Buffer al final del archivo (en algunos archivos el reproductor mira el final del archivo antes de comenzar)
                bp = []
                
                #El tamaño del buffer de inicio es el tamaño del buffer menos el tamaño del buffer del final
                first_pieces_priorize = self.buffer_size - self.last_pieces_priorize
                
                #Comprobamos que partes del buffer del inicio estan disponibles
                for x in range(first_pieces_priorize):
                  if self._th.have_piece(self.file.first_piece + x):
                    bp.append(True)
                  else:
                    bp.append(False)
                    
                #Comprobamos que partes del buffer del final estan disponibles
                for x in range(self.last_pieces_priorize):       
                  if self._th.have_piece(self.file.last_piece - x):
                    bp.append(True)
                  else:
                    bp.append(False)
                
                s.buffer = int(sum(bp) * 100 / self.buffer_size)

            else: #Si no hay ningun archivo seleccionado: No hay buffer
                s.buffer = 0
            
            #Tiempo restante para cerrar en caso de tener el timeout activo
            if self.auto_shutdown:
                if self.connected:
                    if self.timeout:
                        s.timeout = int(self.timeout - (time.time() - self.last_connect -1))
                        if self.file and self.file.cursor:
                             s.timeout = self.timeout
                        if s.timeout < 0: s.timeout = "Cerrando"
                    else:
                        s.timeout = "---"
                else:
                    if self.start_time and self.wait_time:
                        s.timeout = int(self.wait_time - (time.time() - self.start_time -1))
                        if s.timeout < 0: s.timeout = "Cerrando"
                    else:
                        s.timeout = "---"

            else:
                s.timeout = "Off"
            
            #Estado de la descarga
            STATE_STR = ['Queue', 'Verify', 'Download metadata', \
                    'Download', 'Finalizing', 'Seeding', 'Allocating', 'Verify fastresume']
            s.str_state = STATE_STR[s.state]
            
            #Estado DHT
            if self._ses.dht_state() is not None:
                s.dht_state = "On"
                s.dht_nodes=self._ses.status().dht_nodes
            else:
                s.dht_state = "Off"
                s.dht_nodes = 0
            
            #Cantidad de Trackers
            s.trackers= len(self._th.trackers())
            
            #Origen de los peers
            s.dht_peers = 0
            s.trk_peers = 0
            s.pex_peers = 0
            s.lsd_peers = 0

            for peer in self._th.get_peer_info():
                if peer.source & 1:
                    s.trk_peers +=1
                if peer.source & 2:
                    s.dht_peers +=1
                if peer.source & 4:
                    s.pex_peers +=1
                if peer.source & 8:
                    s.lsd_peers +=1

            return s
            
            
    """
    Servicios:
      - Estas funciones se ejecutan de forma automatica cada x tiempo en otro Thread.
      - Estas funciones son ejecutadas mientras el torrent esta activo algunas pueden desactivarse 
        segun la configuracion como por ejemplo la escritura en el log
    """

    def _auto_shutdown(self, *args, **kwargs):
        """
        Servicio encargado de autoapagar el servidor
        """
        if self.file and self.file.cursor:
            self.last_connect = time.time()
            self.connected = True

        if self.is_playing_fnc and self.is_playing_fnc():
            self.last_connect = time.time()
            self.connected = True

        if self.auto_shutdown:
            #shudown por haber cerrado el reproductor
            if self.connected and self.is_playing_fnc and not self.is_playing_fnc():
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

            #shutdown por no realizar ninguna conexion
            if (not self.file or not self.file.cursor) and self.start_time and self.wait_time and not self.connected:
                if time.time() - self.start_time - 1 > self.wait_time:
                    self.stop()

            #shutdown tras la ultima conexion
            if (not self.file or not self.file.cursor) and self.timeout and self.connected and not self.is_playing_fnc:
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

    def announce_torrent(self):
        """
        Servicio encargado de anunciar el torrent
        """
        self._th.force_reannounce()
        self._th.force_dht_announce()
        
    def save_state(self):
        """
        Servicio encargado de guardar el estado
        """
        state=self._ses.save_state()
        f =  open(os.path.join(self.temp_path,self.state_file), 'wb')
        pickle.dump(state,f)
        f.close()

     
    def _update_ready_pieces(self, alert_type, alert):
        """
        Servicio encargado de informar que hay una pieza disponible
        """
        if alert_type == 'read_piece_alert' and self.file:
            self.file.update_piece(alert.piece, alert.buffer)
            
            
    def _check_meta(self):
        '''
        Servicio encargado de comprobar si los metadatos se han descargado
        '''
        if self.status.state>=3 and  self.status.state <= 5 and not self.has_meta:

            #Guardamos los metadatos
            self.meta = self._th.get_torrent_info()

            #Obtenemos la lista de archivos del meta
            fs=self.meta.files()
            if isinstance(fs, list):
              files=fs  
            else:
              files = [fs.at(i) for i in xrange(fs.num_files())]

            #Guardamos la lista de archivos
            self.files=self._find_files(files)

            #Marcamos el primer archivo como activo
            self.set_file(self.files[0])

            #Damos por iniciada la descarga
            self.start_time = time.time()

            #Guardamos el .torrent en el cahce
            self._cache.file_complete(self._th.get_torrent_info())

            self.has_meta = True

    def priorize_start_file(self):
        '''
        Servicio encargado de priorizar el principio y final de archivo cuando no hay conexion
        '''
        if self.file and not self.file.cursor:
          num_start_pieces = self.buffer_size - self.last_pieces_priorize   #Cantidad de piezas a priorizar al inicio
          num_end_pieces = self.last_pieces_priorize                        #Canridad de piezas a priorizar al final
          
          pieces_count = 0
          #Priorizamos las ultimas piezas
          for y in range(self.file.last_piece - num_end_pieces, self.file.last_piece + 1):
            if not self._th.have_piece(y):
              self.prioritize_piece(y,pieces_count)
              pieces_count +=1
          
          #Priorizamos las primeras piezas
          for y in range(self.file.first_piece,self.file.last_piece + 1):
            if not self._th.have_piece(y):
              if pieces_count == self.buffer_size:
                break
              self.prioritize_piece(y,pieces_count)
              pieces_count +=1

    def print_status(self):
        """
        Servicio encargado de mostrar en el log el estado de la descarga
        """
        s = self.status
        if self.file:
          archivo = self.file.index
        else:
          archivo = "N/D"
        logger.info('%.2f%% de %.1fMB %s | %.1f kB/s | #%s %d%% | AutoClose: %s | S: %d(%d) P: %d(%d)) | TRK: %d DHT: %d PEX: %d LSD %d | DHT:%s (%d) | Trakers: %d' % \
                    (s.progress_file , s.file_size, s.str_state, s._download_rate, archivo, s.buffer, s.timeout , s.num_seeds,\
                     s.num_complete, s.num_peers, s.num_incomplete, s.trk_peers,s.dht_peers, s.pex_peers, s.lsd_peers, s.dht_state, s.dht_nodes, s.trackers))                   
Пример #5
0
class Client(object):
    INITIAL_TRACKERS=['udp://tracker.openbittorrent.com:80',
                  'udp://tracker.istole.it:80',
                  'udp://open.demonii.com:80',
                  'udp://tracker.coppersurfer.tk:80',
                  'udp://tracker.leechers-paradise.org:6969',
                  'udp://exodus.desync.com:6969',
                  'udp://tracker.publicbt.com:80']

    VIDEO_EXTS={'.avi':'video/x-msvideo','.mp4':'video/mp4','.mkv':'video/x-matroska',
            '.m4v':'video/mp4','.mov':'video/quicktime', '.mpg':'video/mpeg','.ogv':'video/ogg',
            '.ogg':'video/ogg', '.webm':'video/webm', '.ts': 'video/mp2t', '.3gp':'video/3gpp'}

    def __init__(self, url = None, port=None, ip=None, auto_shutdown=True, wait_time=20, timeout=5, auto_delete=True, temp_path=None, is_playing_fnc=None, print_status = False):

        #server
        self.port = port if port else random.randint(8000,8099)
        self.ip = ip if ip else "127.0.0.1"
        self.server = Server((self.ip,self.port), Handler, client=self)


        #Options
        self.temp_path = temp_path if temp_path else os.path.join(os.path.dirname(__file__),"tmp")
        self.is_playing_fnc = is_playing_fnc
        self.timeout = timeout
        self.auto_delete = auto_delete
        self.wait_time= wait_time
        self.auto_shutdown = auto_shutdown
        self.buffer_size = 10
        self.state_file="state"
        self.torrent_paramss={'save_path':self.temp_path,'storage_mode':lt.storage_mode_t.storage_mode_sparse}



        #State
        self.has_meta = False
        self.meta = None
        self.start_time = None
        self.last_connect = 0
        self.connected = False
        self.closed = False
        self.file = None
        self.files = None
        self._th=None


        #Sesion
        self._cache=Cache(self.temp_path)
        self._ses=lt.session()
        self._ses.listen_on(0,0)
        #Cargamos el archivo de estado (si esxiste)
        if os.path.exists(os.path.join(self.temp_path,self.state_file)):
            try:
                with open(os.path.join(self.temp_path,self.state_file), "rb") as f:
                    state=pickle.load(f)
                    self._ses.load_state(state)
            except:
                pass
                
        
        self._start_services()
        
        #Monitor & Dispatcher
        self._monitor= Monitor(self)
        if print_status:
            self._monitor.add_listener(self.print_status)
        self._monitor.add_listener(self._check_meta)
        self._monitor.add_listener(self.save_state)
        self._monitor.add_listener(self.priorize_start_file)
        self._monitor.add_listener(self.announce_torrent)
        
        if self.auto_shutdown:
            self._monitor.add_listener(self._auto_shutdown)
        self._dispatcher=Dispatcher(self)
        self._dispatcher.add_listener(self._update_ready_pieces)

        #Iniciamos la URL
        if url:
            self.start_url(url)

    def get_play_list(self):
        #Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)

        #Comprobamos que haya archivos de video
        if self.files:
            if len(self.files) > 1:
                return "http://" + self.ip + ":" + str(self.port) + "/playlist.pls"
            else:
                return "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(self.files[0].path)

    def get_files(self):
        #Esperamos a lo metadatos
        while not self.has_meta:
            time.sleep(1)
        files = []

        #Comprobamos que haya archivos de video
        if self.files:
            #Creamos el dict con los archivos
            for file in self.files:
                n = file.path
                u = "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(n)
                s = file.size
                files.append({"name":n,"url":u,"size":s})

        return files

    def announce_torrent(self):
        self._th.force_reannounce()
        self._th.force_dht_announce()

    def _auto_shutdown(self, *args, **kwargs):
        if self.file and self.file.cursor:
            self.last_connect = time.time()
            self.connected = True

        if self.is_playing_fnc and self.is_playing_fnc():
            self.last_connect = time.time()
            self.connected = True

        if self.auto_shutdown:
            #shudown por haber cerrado el reproductor
            if self.connected and self.is_playing_fnc and not self.is_playing_fnc():
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

            #shutdown por no realizar ninguna conexion
            if (not self.file or not self.file.cursor) and self.start_time and self.wait_time and not self.connected:
                if time.time() - self.start_time - 1 > self.wait_time:
                    self.stop()

            #shutdown tras la ultima conexion
            if (not self.file or not self.file.cursor) and self.timeout and self.connected and not self.is_playing_fnc:
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

    def save_state(self):
        state=self._ses.save_state()
        with open(os.path.join(self.temp_path,self.state_file), 'wb') as f:
            pickle.dump(state,f)

     
    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_meta(self):
        if self.status.state>=3 and  self.status.state <= 5 and not self.has_meta:

            #Guardamos los metadatos
            self.meta = self._th.get_torrent_info()

            #Obtenemos la lista de archivos del meta
            fs=self.meta.files()
            files=fs if isinstance(fs, list) else [fs.at(i) for i in xrange(fs.num_files())]

            #Guardamos la lista de archivos
            self.files=self._choose_files(files)

            #Marcamos el primer archivo como activo
            self.set_file(self.files[0])

            #Damos por iniciada la descarga
            self.start_time = time.time()

            #Guardamos el .torrent en el cahce
            self._cache.file_complete(self._th.get_torrent_info())

            self.has_meta = True



    def _choose_files(self, files, search=None):
        #Obtenemos los archivos que la extension este en la lista
        videos=filter(lambda f: self.VIDEO_EXTS.has_key(os.path.splitext(f.path)[1]), files)

        if not videos:
            raise Exception('No video files in torrent')
        for v in videos:
            videos[videos.index(v)].index = files.index(v)
        return videos


    def set_file(self, f):
        #Seleccionamos el archivo que vamos a servir
        fmap=self.meta.map_file(f.index, 0,1)
        self.file=File(f.path, self.temp_path, f.index, f.size, fmap, self.meta.piece_length(), self)
        self.prioritize_file()
        
    def priorize_start_file(self):
        #Si tenemos un archivo seleccionado, pero no hay conexion, priorizamos el inicio del archivo
        if self.file and not self.file.cursor:
          for x in range(self.file.first_piece,self.file.last_piece):

            if not self._th.have_piece(x):
                for y in range(x,x+self.buffer_size):
                  if y == x+self.buffer_size-1 and not self._th.have_piece(self.file.last_piece):
                    self.prioritize_piece(self.file.last_piece,y-x)
                  else:
                    self.prioritize_piece(y,y-x)
                break
            
        



    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,lt.deadline_flags.alert_when_available)
        
        if idx==0:  # it enough when first piece if prioritize, no need to repeat it for following pieces
            # we do not need to download pieces that are lower then current index, 
            # but last x pieces are special because players often  look at end of file
            # for mp4 metadata - so in this case,  we do not want to stop download of previous pieces
            tail_pieces=9
            if (self.file.last_piece - pc) > tail_pieces:
                for i in xrange(self.file.first_piece,pc):
                    self._th.piece_priority(i,0)
                    self._th.reset_piece_deadline(i)
                
            # If we are skipping back we'd like to re-enable all pieces after this one
            # e.g. give them at least priority 1
            for i in xrange(pc+1, self.file.last_piece+1):
                self._th.piece_priority(i,1)

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


    def download_torrent(self,url):
        from core import scrapertools
        
        data = scrapertools.downloadpage(url)
        return data


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

        if uri.startswith('http://') or uri.startswith('https://'):
            torrent_data = self.download_torrent(uri)
            info = lt.torrent_info(lt.bdecode(torrent_data))
            tp = {'ti':info}
            resume_data= self._cache.get_resume(info_hash=str(info.info_hash()))
            if resume_data:
                tp['resume_data']=resume_data

        elif uri.startswith('magnet:'):
            tp={'url':uri}
            resume_data=self._cache.get_resume(info_hash=Cache.hash_from_magnet(uri))
            if resume_data:
                tp['resume_data']=resume_data

        elif os.path.isfile(uri):
            if os.access(uri,os.R_OK):
                info = lt.torrent_info(uri)
                tp= {'ti':info}
                resume_data= self._cache.get_resume(info_hash=str(info.info_hash()))
                if resume_data:
                    tp['resume_data']=resume_data
            else:
                raise ValueError('Invalid torrent path %s' % uri)
        else:
            raise ValueError("Invalid torrent %s" %uri)
        
        tp.update(self.torrent_paramss)
        self._th = self._ses.add_torrent(tp)
        

        for tr in self.INITIAL_TRACKERS:
            self._th.add_tracker({'url':tr})

        self._th.set_sequential_download(True)
        self._th.force_reannounce()
        self._th.force_dht_announce()

        self._monitor.start()
        self._dispatcher.do_start(self._th, self._ses)
        self.server.run()

        
    def stop(self):
        self._dispatcher.stop()
        self._dispatcher.join()
        self._monitor.stop()
        self.server.stop()
        self._dispatcher.stop()
        if self._ses:
            self._ses.pause()
            if self._th:
                self.save_resume()
            self.save_state()
        self._stop_services()
        self._ses.remove_torrent(self._th, self.auto_delete)
        del self._ses
        self.closed = True

    def _start_services(self):
        self._ses.add_dht_router("router.bittorrent.com",6881)
        self._ses.add_dht_router("router.bitcomet.com",554)
        self._ses.add_dht_router("router.utorrent.com",6881)
        self._ses.start_dht()
        self._ses.start_lsd()
        self._ses.start_upnp()
        self._ses.start_natpmp()
        
        
    def _stop_services(self):
        self._ses.stop_natpmp()
        self._ses.stop_upnp()
        self._ses.stop_lsd()
        self._ses.stop_dht()


    def save_resume(self):
        if self._th.need_save_resume_data() and self._th.is_valid() and self.meta:
            r = ResumeData(self)
            start=time.time()
            while (time.time() - start) <= 5 :
                if r.data or r.failed:
                    break
                time.sleep(0.1)
            if r.data:
                self._cache.save_resume(self.unique_file_id,lt.bencode(r.data))

    @property
    def status(self):
        if self._th:
            s = self._th.status()
            s._download_rate = s.download_rate / 1000

            if self.file:
                pieces=s.pieces[self.file.first_piece:self.file.last_piece]
                progress= float(sum(pieces))/len(pieces)
            else:
                progress=0

            s.progress_file=progress * 100
            s.file_size=self.file.size / 1048576.0 if self.file else 0

            if self.file and self.file.cursor:
                percent = len(self.file.cursor.cache)
                percent = percent * 100 / self.buffer_size
                s.buffer = int(percent)

            elif self.file:
                bp = [True if (x == self.buffer_size -1 and self._th.have_piece(self.file.last_piece)) or (x < self.buffer_size -1 and self._th.have_piece(x)) else False for x in range(self.buffer_size ) ]
                percent = len([a for a in bp if a == True])
                percent = percent * 100 / self.buffer_size
                s.buffer = int(percent)

            else:
                s.buffer = 0

            if self.auto_shutdown:
                if self.connected:
                    if self.timeout:
                        s.timeout = int(self.timeout - (time.time() - self.last_connect -1))
                        if self.file and self.file.cursor:
                             s.timeout = self.timeout
                        if s.timeout < 0: s.timeout = "Chiusura"
                    else:
                        s.timeout = "---"
                else:
                    if self.start_time and self.wait_time:
                        s.timeout = int(self.wait_time - (time.time() - self.start_time -1))
                        if s.timeout < 0: s.timeout = "Chiusura"
                    else:
                        s.timeout = "---"

            else:
                s.timeout = "Off"

            STATE_STR = ['Coda', 'Test', 'Download metadata', \
                    'Download', 'Fatto', 'Seeding', 'Assegnazione', 'Controllo fastresume']

            s.str_state = STATE_STR[s.state]

            if self._ses.dht_state() is not None:
                s.dht_state = "On"
                s.dht_nodes=self._ses.status().dht_nodes
            else:
                s.dht_state = "Off"
                s.dht_nodes = 0

            s.trackers= len(self._th.trackers())

            s.dht_peers = 0
            s.trk_peers = 0
            s.pex_peers = 0
            s.lsd_peers = 0

            for peer in self._th.get_peer_info():
                if peer.source & 1:
                    s.trk_peers +=1
                if peer.source & 2:
                    s.dht_peers +=1
                if peer.source & 4:
                    s.pex_peers +=1
                if peer.source & 8:
                    s.lsd_peers +=1

            return s

    def print_status(self):
        s = self.status

        archivo = "N/D"
        percent = s.buffer

        if percent < 75: color= "33"
        if percent < 40: color= "31"
        if percent > 75: color= "32"

        buffer = "\033[%sm%s%s\033[39m" % ( color ,u"\u25A0" * (percent/10), u"\u25A1" *(10-percent/10))

        print '\r\033[39;m%.2f%% de %.1fMB %s | %.1f kB/s | #%s %s | AutoClose: %s | S: %d(%d) P: %d(%d)) | TRK: %d DHT: %d PEX: %d LSD %d | DHT:%s (%d) | Trakers: %d' % \
            (s.progress_file , s.file_size, s.str_state, s._download_rate ,archivo, buffer, s.timeout , s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.trk_peers,s.dht_peers, s.pex_peers, s.lsd_peers, s.dht_state, s.dht_nodes, s.trackers),
Пример #6
0
class Client(object):
    initial_trackers = [
        'udp://tracker.openbittorrent.com:80', 'udp://tracker.istole.it:80',
        'udp://open.demonii.com:80', 'udp://tracker.coppersurfer.tk:80',
        'udp://tracker.leechers-paradise.org:6969',
        'udp://exodus.desync.com:6969', 'udp://tracker.publicbt.com:80'
    ]

    video_exts = {
        '.avi': 'video/x-msvideo',
        '.mp4': 'video/mp4',
        '.mkv': 'video/x-matroska',
        '.m4v': 'video/mp4',
        '.mov': 'video/quicktime',
        '.mpg': 'video/mpeg',
        '.ogv': 'video/ogg',
        '.ogg': 'video/ogg',
        '.webm': 'video/webm',
        '.ts': 'video/mp2t',
        '.3gp': 'video/3gpp'
    }

    def __init__(self,
                 url=None,
                 port=None,
                 ip=None,
                 auto_shutdown=True,
                 wait_time=20,
                 timeout=5,
                 auto_delete=True,
                 temp_path=None,
                 is_playing_fnc=None,
                 print_status=False):

        # server
        self.port = port or random.randint(8000, 8099)
        self.ip = ip or "127.0.0.1"
        self.server = Server((self.ip, self.port), Handler, client=self)

        # Options
        self.temp_path = temp_path or os.path.join(os.path.dirname(__file__),
                                                   "tmp")
        self.is_playing_fnc = is_playing_fnc
        self.timeout = timeout
        self.auto_delete = auto_delete
        self.wait_time = wait_time
        self.auto_shutdown = auto_shutdown
        self.buffer_size = 10
        self.last_pieces_priorize = 1
        self.state_file = "state"
        self.torrent_paramss = {
            'save_path': self.temp_path,
            'storage_mode': lt.storage_mode_t.storage_mode_sparse
        }

        # State
        self.has_meta = False
        self.meta = None
        self.start_time = None
        self.last_connect = 0
        self.connected = False
        self.closed = False
        self.files = []
        self.th = None

        # Sesion
        self.cache = Cache(self.temp_path)
        self.ses = lt.session()
        self.ses.listen_on(0, 0)

        # Cargamos el archivo de estado (si esxiste)
        if os.path.exists(os.path.join(self.temp_path, self.state_file)):
            try:
                f = open(os.path.join(self.temp_path, self.state_file), "rb")
                state = pickle.load(f)
                self.ses.load_state(state)
                f.close()
            except Exception:
                pass

        self._start_services()

        # Monitor & Dispatcher
        self._monitor = Monitor(self)
        if print_status:
            self._monitor.add_listener(self.print_status)
        self._monitor.add_listener(self._check_meta)
        self._monitor.add_listener(self.save_state)
        self._monitor.add_listener(self.priorize_service)
        self._monitor.add_listener(self.announce_torrent)

        if self.auto_shutdown:
            self._monitor.add_listener(self._auto_shutdown)

        self._dispatcher = Dispatcher(self)
        self._dispatcher.add_listener(self._update_ready_pieces)

        # Iniciamos la URL
        if url:
            self.start_url(url)

    def wait_metadata(self):
        while not self.has_meta:
            time.sleep(1)

    def get_play_list(self):
        """
        Función encargada de generar el playlist
        """

        # Esperamos a lo metadatos
        self.wait_metadata()

        # Comprobamos que haya archivos de video
        if self.files:
            if len(self.files) > 1:
                return "http://" + self.ip + ":" + str(
                    self.port) + "/playlist.pls"
            else:
                return "http://" + self.ip + ":" + str(
                    self.port) + "/" + urllib.quote(self.files[0].path)

    def get_files(self):
        """
        Función encargada de genera el listado de archivos
        """

        # Esperamos a lo metadatos
        self.wait_metadata()

        files = []

        # Comprobamos que haya archivos de video
        if self.files:
            # Creamos el dict con los archivos
            for f in self.files:
                n = f.path
                u = "http://" + self.ip + ":" + str(
                    self.port) + "/" + urllib.quote(n)
                s = f.size
                files.append({"name": n, "url": u, "size": s})

        return files

    def _find_files(self, files):
        """
        Función encargada de buscar los archivos reproducibles del torrent
        """
        # Obtenemos los archivos que la extension este en la lista
        videos = filter(
            lambda f: os.path.splitext(f.path)[1] in self.video_exts, files)

        if not videos:
            raise Exception('No video files in torrent')

        result = []
        for v in videos:
            v.index = files.index(v)
            result.append(
                File(v, self.temp_path, self.meta.map_file(v.index, 0, 1),
                     self.meta.piece_length(), self))

        return result

    def set_file(self, f):
        """
        Función encargada de seleccionar el archivo que vamos a servir y por tanto precargar el buffer
        """
        if isinstance(f, str):
            for ff in self.files:
                if ff.path == f:
                    ff.wait_initial_buffer(True)
                    return ff
        else:
            f.wait_initial_buffer()

    @staticmethod
    def download_torrent(url):
        """
        Función encargada de descargar un archivo .torrent
        """
        data = httptools.downloadpage(url).data
        return data

    def start_url(self, uri):
        """
        Función encargada iniciar la descarga del torrent desde la url, permite:
          - Url apuntando a un .torrent
          - Url magnet
          - Archivo .torrent local
        """

        if self.th:
            raise Exception('Torrent is already started')

        if uri.startswith('http://') or uri.startswith('https://'):
            torrent_data = self.download_torrent(uri)
            info = lt.torrent_info(lt.bdecode(torrent_data))
            tp = {'ti': info}
            resume_data = self.cache.get_resume(
                info_hash=str(info.info_hash()))
            if resume_data:
                tp['resume_data'] = resume_data

        elif uri.startswith('magnet:'):
            tp = {'url': uri}
            resume_data = self.cache.get_resume(
                info_hash=Cache.hash_from_magnet(uri))
            if resume_data:
                tp['resume_data'] = resume_data

        elif os.path.isfile(uri):
            if os.access(uri, os.R_OK):
                info = lt.torrent_info(uri)
                tp = {'ti': info}
                resume_data = self.cache.get_resume(
                    info_hash=str(info.info_hash()))
                if resume_data:
                    tp['resume_data'] = resume_data
            else:
                raise ValueError('Invalid torrent path %s' % uri)
        else:
            raise ValueError("Invalid torrent %s" % uri)

        tp.update(self.torrent_paramss)
        self.th = self.ses.add_torrent(tp)

        for tr in self.initial_trackers:
            self.th.add_tracker({'url': tr})

        self.th.set_sequential_download(True)
        self.th.force_reannounce()
        self.th.force_dht_announce()

        self._monitor.start()
        self._dispatcher.do_start(self.th, self.ses)
        self.server.run()

    def stop(self):
        """
        Función encargada de de detener el torrent y salir
        """
        self._dispatcher.stop()
        self._dispatcher.join()
        self._monitor.stop()
        self.server.stop()
        if self.ses:
            self.ses.pause()
            if self.th:
                ResumeData(self).save_resume_data()
            self.save_state()
        self._stop_services()
        self.ses.remove_torrent(self.th, self.auto_delete)
        del self.ses
        self.closed = True

    def _start_services(self):
        """
        Función encargada de iniciar los servicios de libtorrent: dht, lsd, upnp, natpnp
        """
        self.ses.add_dht_router("router.bittorrent.com", 6881)
        self.ses.add_dht_router("router.bitcomet.com", 554)
        self.ses.add_dht_router("router.utorrent.com", 6881)
        self.ses.start_dht()
        self.ses.start_lsd()
        self.ses.start_upnp()
        self.ses.start_natpmp()

    def _stop_services(self):
        """
        Función encargada de detener los servicios de libtorrent: dht, lsd, upnp, natpnp
        """
        self.ses.stop_natpmp()
        self.ses.stop_upnp()
        self.ses.stop_lsd()
        self.ses.stop_dht()

    @property
    def buffer_status(self):
        s = []
        if not self.files:
            return 0

        for x in self.files:
            if x.buffering:
                s.append(
                    len([p for p in x.buffer if x.buffer[p]]) * 100 /
                    len(x.buffer))

        if s:
            return sum(s) / len(s)
        else:
            return 100

    @property
    def active_file(self):
        for f in self.files:
            if f.cursors:
                return f

    @property
    def status(self):
        """
        Función encargada de devolver el estado del torrent
        """
        if self.th:
            s = self.th.status()
            st = dict([(a, getattr(s, a)) for a in dir(s)
                       if not a.startswith('__')])
            # Download Rate
            st['download_rate'] = s.download_rate / 1000

            # Progreso del archivo
            if self.active_files:
                pieces = s.pieces[self.active_file.first_piece:self.
                                  active_file.last_piece]
                progress = float(sum(pieces)) / len(pieces)
            else:
                progress = 0

            st['progress_file'] = progress * 100

            # Tamaño del archivo
            if self.active_file:
                st['file_size'] = self.active_file.size / 1048576.0
            else:
                st['file_size'] = 0

            # Estado del buffer
            st['buffer'] = self.buffer_status

            # Tiempo restante para cerrar en caso de tener el timeout activo
            if self.auto_shutdown:
                if self.connected:
                    if self.timeout:
                        st['timeout'] = int(self.timeout -
                                            (time.time() - self.last_connect -
                                             1))
                        if self.active_file and self.active_file.cursors:
                            st['timeout'] = self.timeout
                        if st['timeout'] < 0:
                            st['timeout'] = "Cerrando"
                    else:
                        st['timeout'] = "---"
                else:
                    if self.start_time and self.wait_time:
                        st['timeout'] = int(self.wait_time -
                                            (time.time() - self.start_time -
                                             1))
                        if st['timeout'] < 0:
                            st['timeout'] = "Cerrando"
                    else:
                        st['timeout'] = "---"

            else:
                st['timeout'] = "Off"

            # Estado de la descarga
            STATE_STR = [
                'En cola', 'Comprobando', 'Descargando metadata',
                'Descargando', 'Finalizado', 'Seeding', 'Allocating',
                'Comprobando fastresume'
            ]
            st['str_state'] = STATE_STR[s.state]

            # Estado DHT
            if self.ses.dht_state() is not None:
                st['dht_state'] = "On"
                st['dht_nodes'] = self.ses.status().dht_nodes
            else:
                st['dht_state'] = "Off"
                st['dht_nodes'] = 0

            # Cantidad de Trackers
            st['trackers'] = len(self.th.trackers())

            # Origen de los peers
            st['dht_peers'] = 0
            st['trk_peers'] = 0
            st['pex_peers'] = 0
            st['lsd_peers'] = 0

            for peer in self.th.get_peer_info():
                if peer.source & 1:
                    st['dht_peers'] += 1
                if peer.source & 2:
                    st['trk_peers'] += 1
                if peer.source & 4:
                    st['pex_peers'] += 1
                if peer.source & 8:
                    st['lsd_peers'] += 1

            return type('', (), st)

    """
    Servicios:
      - Estas funciones se ejecutan de forma automatica cada x tiempo en otro Thread.
      - Estas funciones son ejecutadas mientras el torrent esta activo algunas pueden desactivarse 
        segun la configuracion como por ejemplo la escritura en el log
    """

    def _auto_shutdown(self, *args, **kwargs):
        """
        Servicio encargado de autoapagar el servidor
        """

        if self.active_file and self.active_file.cursors:
            self.last_connect = time.time()
            self.connected = True

        if self.is_playing_fnc and self.is_playing_fnc():
            self.last_connect = time.time()
            self.connected = True

        if self.auto_shutdown:
            # shudown por haber cerrado el reproductor
            if self.connected and self.is_playing_fnc and not self.is_playing_fnc(
            ):
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

            # shutdown por no realizar ninguna conexion
            if (
                    not self.active_file or not self.active_file.cursors
            ) and self.start_time and self.wait_time and not self.connected:
                if time.time() - self.start_time - 1 > self.wait_time:
                    self.stop()

            # shutdown tras la ultima conexion
            if (
                    not self.active_file or not self.active_file.cursors
            ) and self.timeout and self.connected and not self.is_playing_fnc:
                if time.time() - self.last_connect - 1 > self.timeout:
                    self.stop()

    def announce_torrent(self):
        """
        Servicio encargado de anunciar el torrent
        """
        self.th.force_reannounce()
        self.th.force_dht_announce()

    def save_state(self):
        """
        Servicio encargado de guardar el estado
        """
        state = self.ses.save_state()
        f = open(os.path.join(self.temp_path, self.state_file), 'wb')
        pickle.dump(state, f)
        f.close()

    def _update_ready_pieces(self, alert_type, alert):
        """
        Servicio encargado de informar que hay una pieza disponible
        """
        if alert_type == 'read_piece_alert':
            for f in self.files:
                f.add_to_buffer(alert.piece, alert.buffer)

    def _check_meta(self):
        """
        Servicio encargado de comprobar si los metadatos se han descargado
        """
        if 3 <= self.status.state <= 5 and not self.has_meta:

            # Guardamos los metadatos
            self.meta = self.th.get_torrent_info()

            # Obtenemos la lista de archivos del meta
            fs = self.meta.files()
            if isinstance(fs, list):
                files = fs
            else:
                files = [fs.at(i) for i in xrange(fs.num_files())]

            # Guardamos la lista de archivos
            self.files = self._find_files(files)

            # Marcamos el primer archivo como activo
            self.set_file(self.files[0])

            # Damos por iniciada la descarga
            self.start_time = time.time()

            # Guardamos el .torrent en el cahce
            self.cache.file_complete(self.th.get_torrent_info())
            print self.th.get_torrent_info()
            self.has_meta = True

    def priorize_service(self):
        """
        Servicio encargado de priorizar el principio y final de archivo cuando no hay conexion
        """
        for f in self.files:
            if f.buffering:
                if self.th.file_priority(f.index) != 7:
                    self.th.file_priority(f.index, 7)

            elif f.cursors:
                if self.th.file_priority(f.index) != 7:
                    self.th.file_priority(f.index, 7)
            else:
                if self.th.file_priority(f.index) != 1:
                    self.th.file_priority(f.index, 1)

    @property
    def active_files(self):
        return [f for f in self.files if f.cursors]

    def print_status(self):
        """
        Servicio encargado de mostrar en el log el estado de la descarga
        """
        s = self.status
        logger.debug(
            '%.2f%% de %.1fMB %s | %.1f kB/s | AutoClose: %s | S: %d(%d) P: %d(%d)) | '
            'TRK: %d DHT: %d PEX: %d LSD %d | DHT:%s (%d) | Trakers: %d' %
            (s.progress_file, s.file_size, s.str_state, s.download_rate,
             s.timeout, s.num_seeds, s.num_complete, s.num_peers,
             s.num_incomplete, s.trk_peers, s.dht_peers, s.pex_peers,
             s.lsd_peers, s.dht_state, s.dht_nodes, s.trackers))
        for f in self.files:
            for l in str(f).splitlines():
                logger.debug(l)
        logger.debug(
            '_____________________________________________________________________'
        )