# -*- coding: utf-8 -*- import sys # from builtins import str from builtins import range PY3 = False VFS = True if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int; VFS = False if PY3: #from future import standard_library #standard_library.install_aliases() import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo else: import urllib import time import os import traceback import re try: import xbmc import xbmcgui import xbmcaddon except: pass from core import filetools from core import httptools from core import scrapertools from core import jsontools from platformcode import logger from platformcode import config from platformcode import platformtools trackers = [ "udp://tracker.openbittorrent.com:80/announce", "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", ] # Returns an array of possible video url's from the page_url def get_video_url(page_url, premium=False, user="", password="", video_password=""): torrent_options = platformtools.torrent_client_installed(show_tuple=True) if len(torrent_options) == 0: from specials import elementum_download elementum_download.download() logger.info("server=torrent, the url is the good") if page_url.startswith("magnet:"): video_urls = [["magnet: [torrent]", page_url]] else: video_urls = [[".torrent [torrent]", page_url]] return video_urls class XBMCPlayer(xbmc.Player): def __init__(self, *args): pass xbmc_player = XBMCPlayer() def caching_torrents(url, referer=None, post=None, torrents_path=None, timeout=10, \ lookup=False, data_torrent=False, headers={}, proxy_retries=1): if torrents_path != None: logger.info("path = " + torrents_path) else: logger.info() if referer and post: logger.info('REFERER: ' + referer) torrent_file = '' t_hash = '' if referer: headers.update({'Content-Type': 'application/x-www-form-urlencoded', 'Referer': referer}) #Necesario para el Post del .Torrent """ Descarga en el path recibido el .torrent de la url recibida, y pasa el decode Devuelve el path real del .torrent, o el path vacío si la operación no ha tenido éxito """ videolibrary_path = config.get_videolibrary_path() #Calculamos el path absoluto a partir de la Videoteca if torrents_path == None: if not videolibrary_path: torrents_path = '' if data_torrent: return (torrents_path, torrent_file) return torrents_path #Si hay un error, devolvemos el "path" vacío torrents_path = filetools.join(videolibrary_path, 'temp_torrents_Alfa', 'cliente_torrent_Alfa.torrent') #path de descarga temporal if '.torrent' not in torrents_path: torrents_path += '.torrent' #path para dejar el .torrent #torrents_path_encode = filetools.encode(torrents_path) #encode utf-8 del path torrents_path_encode = torrents_path #if url.endswith(".rar") or url.startswith("magnet:"): #No es un archivo .torrent if url.endswith(".rar"): #No es un archivo .torrent logger.error('No es un archivo Torrent: ' + url) torrents_path = '' if data_torrent: return (torrents_path, torrent_file) return torrents_path #Si hay un error, devolvemos el "path" vacío try: #Descargamos el .torrent if url.startswith("magnet:"): if config.get_setting("magnet2torrent", server="torrent", default=False): torrent_file = magnet2torrent(url, headers=headers) #Convierte el Magnet en un archivo Torrent else: if data_torrent: return (url, torrent_file) return url if not torrent_file: logger.error('No es un archivo Magnet: ' + url) torrents_path = '' if data_torrent: return (torrents_path, torrent_file) return torrents_path #Si hay un error, devolvemos el "path" vacío else: if lookup: proxy_retries = 0 if post: #Descarga con POST response = httptools.downloadpage(url, headers=headers, post=post, \ follow_redirects=False, timeout=timeout, proxy_retries=proxy_retries) else: #Descarga sin post response = httptools.downloadpage(url, headers=headers, timeout=timeout, \ proxy_retries=proxy_retries) if not response.sucess: logger.error('Archivo .torrent no encontrado: ' + url) torrents_path = '' if data_torrent: return (torrents_path, torrent_file) return torrents_path #Si hay un error, devolvemos el "path" vacío torrent_file = response.data torrent_file_uncoded = response.data if PY3 and isinstance(torrent_file, bytes): torrent_file = "".join(chr(x) for x in bytes(torrent_file_uncoded)) #Si es un archivo .ZIP tratamos de extraer el contenido if torrent_file.startswith("PK"): logger.info("it's a zip archive: " + url) torrents_path_zip = filetools.join(videolibrary_path, 'temp_torrents_zip') #Carpeta de trabajo torrents_path_zip = filetools.encode(torrents_path_zip) torrents_path_zip_file = filetools.join(torrents_path_zip, 'temp_torrents_zip.zip') #Nombre del .zip import time filetools.rmdirtree(torrents_path_zip) #Borramos la carpeta temporal time.sleep(1) #Hay que esperar, porque si no da error filetools.mkdir(torrents_path_zip) #La creamos de nuevo if filetools.write(torrents_path_zip_file, torrent_file_uncoded, vfs=VFS): #Salvamos el .zip torrent_file = '' #Borramos el contenido en memoria try: #Extraemos el .zip from core import ziptools unzipper = ziptools.ziptools() unzipper.extract(torrents_path_zip_file, torrents_path_zip) except: import xbmc xbmc.executebuiltin('XBMC.Extract("%s", "%s")' % (torrents_path_zip_file, torrents_path_zip)) time.sleep(1) for root, folders, files in filetools.walk(torrents_path_zip): #Recorremos la carpeta para leer el .torrent for file in files: if file.endswith(".torrent"): input_file = filetools.join(root, file) #nombre del .torrent torrent_file = filetools.read(input_file, vfs=VFS) #leemos el .torrent torrent_file_uncoded = torrent_file if PY3 and isinstance(torrent_file, bytes): torrent_file = "".join(chr(x) for x in bytes(torrent_file_uncoded)) filetools.rmdirtree(torrents_path_zip) #Borramos la carpeta temporal #Si no es un archivo .torrent (RAR, HTML,..., vacío) damos error if not scrapertools.find_single_match(torrent_file, '^d\d+:.*?\d+:'): logger.error('No es un archivo Torrent: ' + url) torrents_path = '' if data_torrent: return (torrents_path, torrent_file) return torrents_path #Si hay un error, devolvemos el "path" vacío #Calculamos el Hash del Torrent y modificamos el path import bencode, hashlib decodedDict = bencode.bdecode(torrent_file_uncoded) if not PY3: t_hash = hashlib.sha1(bencode.bencode(decodedDict[b"info"])).hexdigest() else: t_hash = hashlib.sha1(bencode.bencode(decodedDict["info"])).hexdigest() if t_hash: torrents_path = filetools.join(filetools.dirname(torrents_path), t_hash + '.torrent') torrents_path_encode = filetools.join(filetools.dirname(torrents_path_encode), t_hash + '.torrent') #Salvamos el .torrent if not lookup: if not filetools.write(torrents_path_encode, torrent_file_uncoded, vfs=VFS): logger.error('ERROR: Archivo .torrent no escrito: ' + torrents_path_encode) torrents_path = '' #Si hay un error, devolvemos el "path" vacío torrent_file = '' #... y el buffer del .torrent if data_torrent: return (torrents_path, torrent_file) return torrents_path except: torrents_path = '' #Si hay un error, devolvemos el "path" vacío torrent_file = '' #... y el buffer del .torrent logger.error('Error en el proceso de descarga del .torrent: ' + url + ' / ' + torrents_path_encode) logger.error(traceback.format_exc()) #logger.debug(torrents_path) if data_torrent: return (torrents_path, torrent_file) return torrents_path def magnet2torrent(magnet, headers={}): logger.info() torrent_file = '' info = '' post = '' LIBTORRENT_PATH = config.get_setting("libtorrent_path", server="torrent", default="") LIBTORRENT_MAGNET_PATH = filetools.join(config.get_setting("downloadpath"), 'magnet') MAGNET2TORRENT = config.get_setting("magnet2torrent", server="torrent", default=False) btih = scrapertools.find_single_match(magnet, 'urn:btih:([\w\d]+)\&').upper() if magnet.startswith('magnet') and MAGNET2TORRENT: # Tratamos de convertir el magnet on-line (opción más rápida, pero no se puede convertir más de un magnet a la vez) url_list = [ ('https://itorrents.org/torrent/', 6, '', '.torrent') ] # Lista de servicios on-line testeados for url, timeout, id, sufix in url_list: if id: post = '%s=%s' % (id, magnet) else: url = '%s%s%s' % (url, btih, sufix) response = httptools.downloadpage(url, timeout=timeout, headers=headers, post=post) if not response.sucess: continue if not scrapertools.find_single_match(response.data, '^d\d+:.*?\d+:') and not response.data.startswith("PK"): continue torrent_file = response.data break #Usamos Libtorrent para la conversión del magnet como alternativa (es lento) if not torrent_file: lt, e, e1, e2 = import_libtorrent(LIBTORRENT_PATH) # Importamos Libtorrent if lt: ses = lt.session() # Si se ha importado bien, activamos Libtorrent ses.add_dht_router("router.bittorrent.com",6881) ses.add_dht_router("router.utorrent.com",6881) ses.add_dht_router("dht.transmissionbt.com",6881) if ses: filetools.mkdir(LIBTORRENT_MAGNET_PATH) # Creamos la carpeta temporal params = { 'save_path': LIBTORRENT_MAGNET_PATH, 'trackers': trackers, 'storage_mode': lt.storage_mode_t.storage_mode_allocate } # Creamos los parámetros de la sesión h = lt.add_magnet_uri(ses, magnet, params) # Abrimos la sesión i = 0 while not h.has_metadata() and not xbmc.abortRequested: # Esperamos mientras Libtorrent abre la sesión h.force_dht_announce() time.sleep(1) i += 1 logger.error(i) if i > 5: LIBTORRENT_PATH = '' # No puede convertir el magnet break if LIBTORRENT_PATH: info = h.get_torrent_info() # Obtiene la información del .torrent torrent_file = lt.bencode(lt.create_torrent(info).generate()) # Obtiene los datos del .torrent ses.remove_torrent(h) # Desactiva Libtorrent filetools.rmdirtree(LIBTORRENT_MAGNET_PATH) # Elimina la carpeta temporal return torrent_file def verify_url_torrent(url, timeout=5): """ Verifica si el archivo .torrent al que apunta la url está disponible, descargándolo en un area temporal Entrada: url Salida: True o False dependiendo del resultado de la operación """ if not url or url == 'javascript:;': #Si la url viene vacía... return False #... volvemos con error torrents_path = caching_torrents(url, timeout=timeout, lookup=True) #Descargamos el .torrent if torrents_path: #Si ha tenido éxito... return True else: return False # Reproductor Cliente Torrent propio (libtorrent) def bt_client(mediaurl, xlistitem, rar_files, subtitle=None, password=None, item=None): logger.info() # Importamos el cliente from btserver import Client played = False debug = False try: save_path_videos = '' save_path_videos = filetools.join(config.get_setting("bt_download_path", server="torrent", \ default=config.get_setting("downloadpath")), 'BT-torrents') except: pass if not config.get_setting("bt_download_path", server="torrent") and save_path_videos: config.set_setting("bt_download_path", filetools.join(config.get_data_path(), 'downloads'), server="torrent") if not save_path_videos: save_path_videos = filetools.join(config.get_data_path(), 'downloads', 'BT-torrents') config.set_setting("bt_download_path", filetools.join(config.get_data_path(), 'downloads'), server="torrent") UNRAR = config.get_setting("unrar_path", server="torrent", default="") BACKGROUND = config.get_setting("mct_background_download", server="torrent", default=True) RAR = config.get_setting("mct_rar_unpack", server="torrent", default=True) try: BUFFER = int(config.get_setting("bt_buffer", server="torrent", default="50")) except: BUFFER = 50 DOWNLOAD_LIMIT = config.get_setting("mct_download_limit", server="torrent", default="") if DOWNLOAD_LIMIT: try: DOWNLOAD_LIMIT = int(DOWNLOAD_LIMIT) except: DOWNLOAD_LIMIT = 0 else: DOWNLOAD_LIMIT = 0 UPLOAD_LIMIT = 100 torr_client = 'BT' rar_file = '' rar_names = [] rar = False rar_res = False bkg_user = False video_names = [] video_file = '' video_path = '' videourl = '' msg_header = 'KoD %s Client Torrent' % torr_client extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg', '.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg', '.mpe', '.mp4', '.ogg', '.rar', '.wmv', '.zip'] for entry in rar_files: for file, path in list(entry.items()): if file == 'path' and '.rar' in str(path): for file_r in path: rar_names += [file_r] rar = True if RAR and BACKGROUND: bkg_user = True elif file == 'path' and not '.rar' in str(path): for file_r in path: if os.path.splitext(file_r)[1] in extensions_list: video_names += [file_r] elif file == '__name': video_path = path video_file = path if rar: rar_file = '%s/%s' % (video_path, rar_names[0]) erase_file_path = filetools.join(save_path_videos, video_path) video_path = erase_file_path if video_names: video_file = video_names[0] if not video_file and mediaurl.startswith('magnet'): video_file = urllib.unquote_plus(scrapertools.find_single_match(mediaurl, '(?:\&|&)dn=([^\&]+)\&')) erase_file_path = filetools.join(save_path_videos, video_file) if rar and RAR and not UNRAR: if not platformtools.dialog_yesno(msg_header, config.get_localized_string(70791)): return # Iniciamos el cliente: c = Client(url=mediaurl, is_playing_fnc=xbmc_player.isPlaying, wait_time=None, auto_shutdown=False, timeout=10, temp_path=save_path_videos, print_status=debug, auto_delete=False) activo = True finalizado = False dp_cerrado = True # Mostramos el progreso if rar and RAR and BACKGROUND: # Si se descarga un RAR... progreso = platformtools.dialog_progress_bg(msg_header) platformtools.dialog_notification(config.get_localized_string(70790), config.get_localized_string(70769), time=10000) else: progreso = platformtools.dialog_progress('%s Torrent Client' % torr_client, '') dp_cerrado = False # Mientras el progreso no sea cancelado ni el cliente cerrado try: while not c.closed and not xbmc.abortRequested: # Obtenemos el estado del torrent s = c.status if debug: # Montamos las tres lineas con la info del torrent txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \ (s.progress_file, s.file_size, s.str_state, s._download_rate) txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d | Pi: %d(%d)' % \ (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes, s.trackers, s.pieces_sum, s.pieces_len) txt3 = 'Origen Peers TRK: %d DHT: %d PEX: %d LSD %d ' % \ (s.trk_peers, s.dht_peers, s.pex_peers, s.lsd_peers) else: txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \ (s.progress_file, s.file_size, s.str_state, s._download_rate) txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d | Pi: %d(%d)' % \ (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes, s.trackers, s.pieces_sum, s.pieces_len) txt3 = video_file if rar and RAR and BACKGROUND or bkg_user: progreso.update(s.buffer, txt, txt2) else: progreso.update(s.buffer, txt, txt2, txt3) time.sleep(1) if (not bkg_user and progreso.iscanceled()) and (not (rar and RAR and BACKGROUND) and progreso.iscanceled()): if not dp_cerrado: progreso.close() dp_cerrado = True if 'Finalizado' in s.str_state or 'Seeding' in s.str_state: """ if not rar and platformtools.dialog_yesno(msg_header, config.get_localized_string(70198)): played = False dp_cerrado = False progreso = platformtools.dialog_progress(msg_header, '') progreso.update(s.buffer, txt, txt2, txt3) else: """ dp_cerrado = False progreso = platformtools.dialog_progress(msg_header, '') break else: if not platformtools.dialog_yesno(msg_header, config.get_localized_string(30031), config.get_localized_string(30032)): dp_cerrado = False progreso = platformtools.dialog_progress(msg_header, '') break else: bkg_user = True if not dp_cerrado: progreso.close() dp_cerrado = False progreso = platformtools.dialog_progress_bg(msg_header) progreso.update(s.buffer, txt, txt2) if not c.closed: c.set_speed_limits(DOWNLOAD_LIMIT, UPLOAD_LIMIT) # Bajamos la velocidad en background # Si el buffer se ha llenado y la reproduccion no ha sido iniciada, se inicia if ((s.pieces_sum >= BUFFER or 'Finalizado' in s.str_state or 'Seeding' in s.str_state) and not rar and not bkg_user) or \ (s.pieces_sum >= s.pieces_len - 3 and s.pieces_len > 0 and ('Finalizado' in s.str_state or 'Seeding' \ in s.str_state) and (rar or bkg_user)) and not played: if rar and RAR and UNRAR: c.stop() activo = False finalizado = True bkg_user = False dp_cerrado = False video_file, rar_res, video_path, erase_file_path = extract_files(rar_file, \ save_path_videos, password, progreso, item, torr_client) # ... extraemos el vídeo del RAR if rar_res and not xbmc.abortRequested: time.sleep(1) else: break elif (rar and not UNRAR) or (rar and not RAR): break elif bkg_user: finalizado = True break # Cerramos el progreso if not dp_cerrado: progreso.close() dp_cerrado = True # Reproducimos el vídeo extraido, si no hay nada en reproducción if not c.closed: c.set_speed_limits(DOWNLOAD_LIMIT, UPLOAD_LIMIT) # Bajamos la velocidad en background bkg_auto = True while xbmc_player.isPlaying() and not xbmc.abortRequested: time.sleep(3) # Obtenemos el playlist del torrent #videourl = c.get_play_list() if not rar_res: # Es un Magnet ? video_file = filetools.join(save_path_videos, s.file_name) if erase_file_path == save_path_videos: erase_file_path = video_file videourl = video_file else: videourl = filetools.join(video_path, video_file) # Iniciamos el reproductor playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist.clear() playlist.add(videourl, xlistitem) # xbmc_player = xbmc_player log("##### videourl: %s" % videourl) xbmc_player.play(playlist) # Marcamos como reproducido para que no se vuelva a iniciar played = True mark_auto_as_watched(item) # Y esperamos a que el reproductor se cierre bkg_auto = True dp_cerrado = True while xbmc_player.isPlaying() and not xbmc.abortRequested: time.sleep(1) if xbmc.getCondVisibility('Player.Playing'): if not dp_cerrado: dp_cerrado = True progreso.close() if xbmc.getCondVisibility('Player.Paused') and not rar_res: if not c.closed: s = c.status txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \ (s.progress_file, s.file_size, s.str_state, s._download_rate) txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d | Pi: %d(%d)' % \ (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes, s.trackers, s.pieces_sum, s.pieces_len) txt3 = video_file[:99] if dp_cerrado: dp_cerrado = False progreso = xbmcgui.DialogProgressBG() progreso.create(msg_header) progreso.update(s.buffer, msg_header, '[CR][CR]' + txt + '[CR]' + txt2) if not dp_cerrado: dp_cerrado = True progreso.close() # Miramos si se ha completado la descarga para borrar o no los archivos if activo: s = c.status if s.pieces_sum == s.pieces_len: finalizado = True break if not platformtools.dialog_yesno(msg_header, config.get_localized_string(30031), config.get_localized_string(30032)): progreso = platformtools.dialog_progress(msg_header, '') dp_cerrado = False break else: bkg_user = True played = False if not dp_cerrado: progreso.close() progreso = platformtools.dialog_progress_bg(msg_header) progreso.update(s.buffer, txt, txt2) dp_cerrado = False continue # Cuando este cerrado, Volvemos a mostrar el dialogo if not (rar and bkg_user): progreso = platformtools.dialog_progress(msg_header, '') progreso.update(s.buffer, txt, txt2, txt3) dp_cerrado = False break except: logger.error(traceback.format_exc(1)) return if not dp_cerrado: if rar or bkg_user: progreso.update(100, config.get_localized_string(70200), " ") else: progreso.update(100, config.get_localized_string(70200), " ", " ") # Detenemos el cliente if activo and not c.closed: c.stop() activo = False # Cerramos el progreso if not dp_cerrado: progreso.close() dp_cerrado = True # Y borramos los archivos de descarga restantes time.sleep(1) if filetools.exists(erase_file_path) and not bkg_user: if finalizado and not platformtools.dialog_yesno(msg_header, config.get_localized_string(70792)): return log("##### erase_file_path: %s" % erase_file_path) for x in range(10): if filetools.isdir(erase_file_path): if erase_file_path != save_path_videos: filetools.rmdirtree(erase_file_path) else: break else: filetools.remove(erase_file_path) time.sleep(5) if not filetools.exists(erase_file_path): break def call_torrent_via_web(mediaurl, torr_client): # Usado para llamar a los clientes externos de Torrents para automatizar la descarga de archivos que contienen .RAR logger.info() post = '' ELEMENTUMD_HOST = "http://localhost:65220" if torr_client == 'elementum': try: ADDON = xbmcaddon.Addon("plugin.video.elementum") except: ADDON = False if ADDON: ELEMENTUMD_HOST = "http://" + ADDON.getSetting("remote_host") + ":" + ADDON.getSetting("remote_port") local_host = {"quasar": ["http://localhost:65251/torrents/", "add?uri"], \ "elementum": ["%s/torrents/" % ELEMENTUMD_HOST, "add"]} if torr_client == "quasar": uri = '%s%s=%s' % (local_host[torr_client][0], local_host[torr_client][1], mediaurl) elif torr_client == "elementum": uri = '%s%s' % (local_host[torr_client][0], local_host[torr_client][1]) post = 'uri=%s&file=null&all=1' % mediaurl if post: response = httptools.downloadpage(uri, post=post, timeout=5, alfa_s=True, ignore_response_code=True) else: response = httptools.downloadpage(uri, timeout=5, alfa_s=True, ignore_response_code=True) return response.sucess def mark_auto_as_watched(item): time_limit = time.time() + 150 #Marcamos el timepo máx. de buffering while not platformtools.is_playing() and time.time() < time_limit: #Esperamos mientra buffera time.sleep(5) #Repetimos cada intervalo #logger.debug(str(time_limit)) if item.subtitle: time.sleep(5) xbmc_player.setSubtitles(item.subtitle) #subt = xbmcgui.ListItem(path=item.url, thumbnailImage=item.thumbnail) #subt.setSubtitles([item.subtitle]) if item.strm_path and platformtools.is_playing(): #Sólo si es de Videoteca from platformcode import xbmc_videolibrary xbmc_videolibrary.mark_auto_as_watched(item) #Marcamos como visto al terminar #logger.debug("Llamado el marcado") def wait_for_download(item, mediaurl, rar_files, torr_client, password='', size='', rar_control={}): logger.info() from subprocess import Popen, PIPE, STDOUT # Analizamos los archivos dentro del .torrent rar = False rar_names = [] rar_names_abs = [] folder = '' if rar_control: for x, entry in enumerate(rar_control['rar_files']): if '__name' in entry: folder = rar_control['rar_files'][x]['__name'] break rar_names = [rar_control['rar_names'][0]] else: for entry in rar_files: for file, path in list(entry.items()): if file == 'path' and '.rar' in str(path): for file_r in path: rar_names += [file_r] rar = True elif file == '__name': folder = path if not folder: # Si no se detecta el folder... return ('', '', '') # ... no podemos hacer nada if not rar_names: return ('', '', folder) rar_file = '%s/%s' % (folder, rar_names[0]) log("##### rar_file: %s" % rar_file) if len(rar_names) > 1: log("##### rar_names: %s" % str(rar_names)) # Localizamos el path de descarga del .torrent save_path_videos = '' __settings__ = xbmcaddon.Addon(id="plugin.video.%s" % torr_client) # Apunta settings del cliente torrent if torr_client == 'torrenter': save_path_videos = str(xbmc.translatePath(__settings__.getSetting('storage'))) if not save_path_videos: save_path_videos = str(filetools.join(xbmc.translatePath("special://home/"), \ "cache", "xbmcup", "plugin.video.torrenter", "Torrenter")) else: save_path_videos = str(xbmc.translatePath(__settings__.getSetting('download_path'))) if __settings__.getSetting('download_storage') == '1': # Descarga en memoria? return ('', '', folder) # volvemos if not save_path_videos: # No hay path de descarga? return ('', '', folder) # Volvemos log("##### save_path_videos: %s" % save_path_videos) # Si es nueva descarga, ponemos un archivo de control para reiniciar el UNRar si ha habido cancelación de Kodi # Si ya existe el archivo (llamada), se reinicia el proceso de UNRar donde se quedó if rar_control: if 'downloading' not in rar_control['status']: log("##### Torrent DESCARGADO Anteriormente: %s" % str(folder)) return (rar_file, save_path_videos, folder) else: rar_control = { 'torr_client': torr_client, 'rar_files': rar_files, 'rar_names': rar_names, 'size': size, 'password': password, 'download_path': filetools.join(save_path_videos, folder), 'status': 'downloading', 'error': 0, 'error_msg': '', 'item': item.tourl(), 'mediaurl': mediaurl } if torr_client == 'quasar': # Quasar no copia en .torrent ret = filetools.copy(item.url, filetools.join(save_path_videos, 'torrents', \ filetools.basename(item.url)), silent=True) # Esperamos mientras el .torrent se descarga. Verificamos si el .RAR está descargado al completo platformtools.dialog_notification(config.get_localized_string(70803), "", time=10000) # Plan A: usar el monitor del cliente torrent para ver el status de la descarga loop = 3600 # Loop de 10 horas hasta crear archivo wait_time = 10 time.sleep(wait_time) fast = False ret = filetools.write(filetools.join(rar_control['download_path'], '_rar_control.json'), jsontools.dump(rar_control)) for x in range(loop): if xbmc.abortRequested: return ('', '', folder) torr_data, deamon_url, index = get_tclient_data(folder, torr_client) if not torr_data or not deamon_url: if len(filetools.listdir(rar_control['download_path'], silent=True)) <= 1: filetools.remove(filetools.join(rar_control['download_path'], '_rar_control.json'), silent=True) filetools.rmdir(rar_control['download_path'], silent=True) return ('', '', folder) # Volvemos if (torr_client in ['quasar'] or torr_client in ['elementum']) and not \ torr_data['label'].startswith('0.00%') and not fast: platformtools.dialog_notification(config.get_localized_string(60200), config.get_localized_string(70769), time=10000) fast = True if not torr_data['label'].startswith('100.00%'): log("##### Downloading: %s, ID: %s" % (scrapertools.find_single_match(torr_data['label'], '(^.*?\%)'), index)) time.sleep(wait_time) continue update_rar_control(rar_control['download_path'], status='downloaded') log("##### Torrent FINALIZED: %s" % str(folder)) return (rar_file, save_path_videos, folder) # Plan B: monitorizar con UnRAR si los archivos se han desacargado por completo unrar_path = config.get_setting("unrar_path", server="torrent", default="") if not unrar_path: # Si Unrar no está instalado... return ('', '', folder) # ... no podemos hacer nada cmd = [] for rar_name in rar_names: # Preparamos por si es un archivo multiparte cmd.append(['%s' % unrar_path, 'l', '%s' % filetools.join(save_path_videos, folder, rar_name)]) creationflags = '' if xbmc.getCondVisibility("system.platform.Windows"): creationflags = 0x08000000 loop = 30 # Loop inicial de 5 minutos hasta crear archivo wait_time = 10 loop_change = 0 loop_error = 6 part_name = '' y = 0 returncode = '' fast = False while rar and not xbmc.abortRequested: for x in range(loop): # Loop corto (5 min.) o largo (10 h.) if xbmc.abortRequested: return ('', '', folder) if not rar or loop_change > 0: loop = loop_change # Paso de loop corto a largo loop_change = 0 break try: responses = [] for z, command in enumerate(cmd): # Se prueba por cada parte if xbmc.getCondVisibility("system.platform.Windows"): data_rar = Popen(command, bufsize=0, stdout=PIPE, stdin=PIPE, \ stderr=STDOUT, creationflags=creationflags) else: data_rar = Popen(command, bufsize=0, stdout=PIPE, stdin=PIPE, \ stderr=STDOUT) out_, error_ = data_rar.communicate() responses.append([z, str(data_rar.returncode), out_, error_]) # Se guarda la respuesta de cada parte except: logger.error(traceback.format_exc(1)) # Error de incompatibilidad de UnRAR rar = False break else: dl_files = 0 for z, returncode, out__, error__ in responses: # Analizamos las respuestas if returncode == '0': # Ya se ha descargado... parte ... dl_files += 1 part_name = scrapertools.find_single_match(str(out__), '(\.part\d+.rar)') log("##### Torrent downloading: %s, %s" % (part_name, str(returncode))) if dl_files == len(cmd): # ... o todo fast = True rar = False break # ... o sólo una parte elif returncode == '10': # archivo no existe if loop != 30: # Si el archivo es borrado durante el proceso ... rar = False break #... abortamos elif returncode == '6': # En proceso de descarga y += 1 #if loop == 30 and y == len(responses): # Si es la primera vez en proceso ... if loop == 30 and y == 1: # Si es la primera vez en proceso ... if torr_client in ['quasar']: platformtools.dialog_notification(config.get_localized_string(60200), config.get_localized_string(70769), time=10000) loop_change = 3600 # ... pasamos a un loop de 10 horas elif loop <= 6: # Recuerado el error desconocido loop_change = 3600 # ... pasamos a un loop de 10 horas loop_error = 6 # Restauramos loop_error por si acaso break elif returncode == '1': # Ha alcanzado el fin de archivo ??? pasamos part_name = scrapertools.find_single_match(str(out__), '(\.part\d+.rar)') log("##### Torrent downloading: %s, %s" % (part_name, str(returncode))) else: # No entendemos el error loop_change = loop_error # ... pasamos a un loop de 1 minutos para reintentar loop_error += -1 break #... abortamos if str(returncode) in ['0', '6', '10']: log("##### Torrent downloading: %s" % str(returncode)) else: log("##### Torrent downloading: %s, %s" % (str(out__), str(returncode))) if not rar or fast: fast = False break time.sleep(wait_time) # Esperamos un poco y volvemos a empezar else: rar = False break if str(returncode) == '0': log("##### Torrent FINALIZED: %s" % str(returncode)) else: rar_file = '' logger.error('##### Torrent NO DESCARGADO: %s, %s' % (str(out__), str(returncode))) return (rar_file, save_path_videos, folder) def get_tclient_data(folder, torr_client): # Monitoriza el estado de descarga del torrent en Quasar y Elementum ELEMENTUMD_HOST = "http://localhost:65220" if torr_client == 'elementum': try: ADDON = xbmcaddon.Addon("plugin.video.elementum") except: ADDON = False if ADDON: ELEMENTUMD_HOST = "http://" + ADDON.getSetting("remote_host") + ":" + ADDON.getSetting("remote_port") local_host = {"quasar": "http://localhost:65251/torrents/", "elementum": "%s/torrents/" % ELEMENTUMD_HOST} torr = '' torr_id = '' x = 0 y = '' try: data = httptools.downloadpage(local_host[torr_client], timeout=5, alfa_s=True).data if not data: return '', local_host[torr_client], 0 data = jsontools.load(data) data = data['items'] for x, torr in enumerate(data): if not folder in torr['label']: continue if "elementum" in torr_client: torr_id = scrapertools.find_single_match(str(torr), 'torrents\/move\/(.*?)\)') break else: return '', local_host[torr_client], 0 except: log(traceback.format_exc(1)) return '', local_host[torr_client], 0 if torr_id: y = torr_id else: y = x return torr, local_host[torr_client], y def extract_files(rar_file, save_path_videos, password, dp, item=None, \ torr_client=None, rar_control={}, size='RAR', mediaurl=''): logger.info() from platformcode import custom_code if not rar_control: rar_control = { 'torr_client': torr_client, 'rar_files': [{"__name": "%s" % rar_file.split("/")[0]}], 'rar_names': [filetools.basename(rar_file)], 'size': size, 'password': password, 'download_path': save_path_videos, 'status': 'downloaded', 'error': 0, 'error_msg': '', 'item': item.tourl(), 'mediaurl': mediaurl } ret = filetools.write(filetools.join(rar_control['download_path'], '_rar_control.json'), jsontools.dump(rar_control)) #reload(sys) #sys.setdefaultencoding('utf-8') sys.path.insert(0, config.get_setting("unrar_path", server="torrent", default="")\ .replace('/unrar', '').replace('\\unrar,exe', '')) import rarfile # Verificamos si hay path para UnRAR rarfile.UNRAR_TOOL = config.get_setting("unrar_path", server="torrent", default="") if not rarfile.UNRAR_TOOL: if xbmc.getCondVisibility("system.platform.Android"): rarfile.UNRAR_TOOL = xbmc.executebuiltin("StartAndroidActivity(com.rarlab.rar)") return rar_file, False, '', '' log("##### unrar_path: %s" % rarfile.UNRAR_TOOL) rarfile.DEFAULT_CHARSET = 'utf-8' # Preparamos un path alternativo más corto para no sobrepasar la longitud máxima video_path = '' if item: if item.contentType == 'movie': video_path = '%s-%s' % (item.contentTitle, item.infoLabels['tmdb_id']) else: video_path = '%s-%sx%s-%s' % (item.contentSerieName, item.contentSeason, \ item.contentEpisodeNumber, item.infoLabels['tmdb_id']) video_path = video_path.replace("á", "a").replace("é", "e").replace("í", "i").replace("ó", "o")\ .replace("ú", "u").replace("ü", "u").replace("ñ", "n")\ .replace("Á", "A").replace("É", "E").replace("Í", "I").replace("Ó", "O")\ .replace("Ú", "U").replace("Ü", "U").replace("Ñ", "N") # Renombramos el path dejado en la descarga a uno más corto rename_status = False org_rar_file = rar_file org_save_path_videos = save_path_videos if video_path and '/' in rar_file: log("##### rar_file: %s" % rar_file) rename_status, rar_file = rename_rar_dir(org_rar_file, org_save_path_videos, video_path, torr_client) # Calculamos el path para del RAR if "/" in rar_file: folders = rar_file.split("/") erase_file_path = filetools.join(save_path_videos, folders[0]) file_path = save_path_videos for f in folders: file_path = filetools.join(file_path, f) else: file_path = save_path_videos erase_file_path = save_path_videos # Calculamos el path para la extracción if "/" in rar_file: folders = rar_file.split("/") for f in folders: if not '.rar' in f: save_path_videos = filetools.join(save_path_videos, f) save_path_videos = filetools.join(save_path_videos, 'Extracted') if not filetools.exists(save_path_videos): filetools.mkdir(save_path_videos) log("##### save_path_videos: %s" % save_path_videos) rar_control = update_rar_control(erase_file_path, status='UnRARing') # Permite hasta 5 pasadas de extracción de .RARs anidados platformtools.dialog_notification(config.get_localized_string(70793), rar_file, time=5000) for x in range(5): try: if not PY3: archive = rarfile.RarFile(file_path.decode("utf8")) else: archive = rarfile.RarFile(file_path) except: log("##### ERROR in rar archive: %s" % rar_file) log("##### ERROR in rar folder: %s" % file_path) log(traceback.format_exc()) error_msg = config.get_localized_string(70796) error_msg1 = config.get_localized_string(60015) platformtools.dialog_notification(error_msg, error_msg1) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR') return rar_file, False, '', '' # Analizamos si es necesaria una contraseña, que debería estar en item.password if archive.needs_password(): if not password: pass_path = filetools.split(file_path)[0] password = last_password_search(pass_path, erase_file_path) if not password : password = platformtools.dialog_input(heading=config.get_localized_string(70794) % pass_path) if not password: error_msg = config.get_localized_string(60309) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR') dp.close() return custom_code.reactivate_unrar(init=False, mute=False) archive.setpassword(password) log("##### Password rar: %s" % password) # Miramos el contenido del RAR a extraer files = archive.infolist() info = [] for idx, i in enumerate(files): if i.file_size == 0: files.pop(idx) continue filename = i.filename if "/" in filename: filename = filename.rsplit("/", 1)[1] info.append("%s - %.2f MB" % (filename, i.file_size / 1048576.0)) if info: info.append(config.get_localized_string(70801)) else: error_msg = config.get_localized_string(70797) error_msg1 = config.get_localized_string(70798) platformtools.dialog_notification(error_msg, error_msg1) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR') dp.close() return custom_code.reactivate_unrar(init=False, mute=False) # Seleccionamos extraer TODOS los archivos del RAR #selection = xbmcgui.Dialog().select("Selecciona el fichero a extraer y reproducir", info) selection = len(info) - 1 if selection < 0: error_msg = config.get_localized_string(70797) platformtools.dialog_notification(error_msg) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR') return rar_file, False, '', '' else: try: log("##### RAR Extract INI #####") if selection == len(info) - 1: log("##### rar_file 1: %s" % file_path) log("##### save_path_videos 1: %s" % save_path_videos) dp.update(99, config.get_localized_string(70803), config.get_localized_string(70802)) archive.extractall(save_path_videos) else: log("##### rar_file 2: %s" % file_path) log("##### save_path_videos 2: %s" % save_path_videos) dp.update(99, config.get_localized_string(70802), config.get_localized_string(70803) + " %s" % info[selection]) archive.extract(files[selection], save_path_videos) log("##### RAR Extract END #####") except (rarfile.RarWrongPassword, rarfile.RarCRCError): log(traceback.format_exc(1)) error_msg = config.get_localized_string(70799) error_msg1 = config.get_localized_string(60309) platformtools.dialog_notification(error_msg, error_msg1) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg1, status='ERROR') dp.close() return custom_code.reactivate_unrar(init=False, mute=False) except rarfile.BadRarFile: log(traceback.format_exc(1)) error_msg = config.get_localized_string(70799) error_msg1 = config.get_localized_string(60800) platformtools.dialog_notification(error_msg, error_msg1) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg1, status='ERROR') #return rar_file, False, '', erase_file_path dp.close() return custom_code.reactivate_unrar(init=False, mute=False) except: log(traceback.format_exc(1)) error_msg = config.get_localized_string(70799) error_msg1 = config.get_localized_string(60015) platformtools.dialog_notification(error_msg, error_msg1) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR') dp.close() return custom_code.reactivate_unrar(init=False, mute=False) extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg', '.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg', '.mpe', '.mp4', '.ogg', '.wmv'] # Localizamos el path donde se ha dejado la extracción folder = True file_result = filetools.listdir(save_path_videos) while folder: for file_r in file_result: if filetools.isdir(filetools.join(save_path_videos, file_r)): file_result_alt = filetools.listdir(filetools.join(save_path_videos, file_r)) if file_result_alt: file_result = file_result_alt save_path_videos = filetools.join(save_path_videos, file_r) else: folder = False break else: folder = False # Si hay RARs anidados, ajustamos los paths para la siguiente pasada if '.rar' in str(file_result): for file_r in file_result: if '.rar' in file_r: rar_file = file_r file_path = str(filetools.join(save_path_videos, rar_file)) save_path_videos = filetools.join(save_path_videos, 'Extracted') rar_control = update_rar_control(erase_file_path, newextract=(rar_file)) if not filetools.exists(save_path_videos): filetools.mkdir(save_path_videos) platformtools.dialog_notification(config.get_localized_string(70804), rar_file, time=5000) # Si ya se ha extraido todo, preparamos el retorno else: video_list = [] for file_r in file_result: if os.path.splitext(file_r)[1] in extensions_list: video_list += [file_r] if len(video_list) == 0: error_msg = config.get_localized_string(70797) error_msg1 = config.get_localized_string(70798) platformtools.dialog_notification(error_msg, error_msg1) rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR') dp.close() return custom_code.reactivate_unrar(init=False, mute=False) else: log("##### Archive extracted: %s" % video_list[0]) platformtools.dialog_notification(config.get_localized_string(70795), video_list[0], time=10000) log("##### Archive removes: %s" % file_path) #rar_control = update_rar_control(erase_file_path, status='DONE') ret = filetools.remove(filetools.join(erase_file_path, '_rar_control.json'), silent=True) return str(video_list[0]), True, save_path_videos, erase_file_path def rename_rar_dir(rar_file, save_path_videos, video_path, torr_client): logger.info() rename_status = False folders = rar_file.split("/") if filetools.exists(filetools.join(save_path_videos, folders[0])) and video_path not in folders[0]: if not PY3: src = filetools.join(save_path_videos, folders[0]).decode("utf8") dst = filetools.join(save_path_videos, video_path).decode("utf8") dst_file = video_path.decode("utf8") else: src = filetools.join(save_path_videos, folders[0]) dst = filetools.join(save_path_videos, video_path) dst_file = video_path for x in range(20): if xbmc.abortRequested: return rename_status, rar_file xbmc.sleep(1000) # Se para la actividad para que libere los archivos descargados if torr_client in ['quasar', 'elementum']: torr_data, deamon_url, index = get_tclient_data(folders[0], torr_client) if torr_data and deamon_url: log("##### Client URL: %s" % '%spause/%s' % (deamon_url, index)) data = httptools.downloadpage('%spause/%s' % (deamon_url, index), timeout=5, alfa_s=True).data try: if filetools.exists(src): filetools.rename(src, dst_file, silent=True, strict=True) elif not filetools.exists(dst_file): break except: log("##### Rename ERROR: SRC: %s" % src) log(traceback.format_exc(1)) else: if filetools.exists(dst): log("##### Renamed: SRC: %s" % src) log("##### TO: DST: %s" % dst) rar_file = video_path + '/' + folders[1] rename_status = True update_rar_control(dst, newpath=dst) break return rename_status, rar_file def last_password_search(pass_path, erase_file_path=''): logger.info(pass_path) if not erase_file_path: erase_file_path = pass_path # Busca en el Path de extracción si hay algún archivo que contenga la URL donde pueda estar la CONTRASEÑA password = '' patron_url = '(http.*\:\/\/(?:www.)?\w+\.\w+\/.*?)[\n|\r|$]' patron_pass = ')", "", httptools.downloadpage(url).data) password = scrapertools.find_single_match(data, patron_pass) if password: update_rar_control(erase_file_path, password=password, status='UnRARing: Password update') break except: log(traceback.format_exc(1)) log("##### Password Extracted: %s" % password) return password def update_rar_control(path, newpath='', newextract='', password='', error='', error_msg='', status=''): try: rar_control = {} rar_control = jsontools.load(filetools.read(filetools.join(path, '_rar_control.json'))) if rar_control: if newpath: rar_control['download_path'] = newpath for x, entry in enumerate(rar_control['rar_files']): if '__name' in entry: rar_control['rar_files'][x]['__name'] = filetools.basename(newpath) break if newextract: for x, entry in enumerate(rar_control['rar_files']): if '__name' in entry: #rar_control['rar_files'][x]['__name'] = filetools.join(rar_control['rar_files'][x]['__name'], 'Extracted') rar_control['rar_files'][x]['__name'] = rar_control['rar_files'][x]['__name'] + '/Extracted' break rar_control['rar_names'] = [newextract] if password: rar_control['password'] = password if error: rar_control['error'] += 1 if error_msg: rar_control['error_msg'] = error_msg if status and status not in rar_control['status']: rar_control['status'] = status ret = filetools.write(filetools.join(rar_control['download_path'], '_rar_control.json'), \ jsontools.dump(rar_control)) logger.debug('%s, %s, %s, %s, %s, %s' % (rar_control['download_path'], \ rar_control['rar_names'][0], rar_control['password'], \ str(rar_control['error']), rar_control['error_msg'], rar_control['status'])) except: log(traceback.format_exc(1)) return rar_control def import_libtorrent(LIBTORRENT_PATH): logger.info(LIBTORRENT_PATH) e = '' e1 = '' e2 = '' fp = '' pathname = '' description = '' lt = '' try: sys.path.insert(0, LIBTORRENT_PATH) if LIBTORRENT_PATH: try: if not xbmc.getCondVisibility("system.platform.android"): import libtorrent as lt pathname = LIBTORRENT_PATH else: import imp from ctypes import CDLL dll_path = os.path.join(LIBTORRENT_PATH, 'liblibtorrent.so') liblibtorrent = CDLL(dll_path) path_list = [LIBTORRENT_PATH, xbmc.translatePath('special://xbmc')] fp, pathname, description = imp.find_module('libtorrent', path_list) # Esta parte no funciona en Android. Por algún motivo da el error "dlopen failed: library "liblibtorrent.so" not found" # Hay que encontrar un hack para rodear el problema. Lo siguiente ha sido probado sin éxito: #if fp: fp.close() #fp = filetools.file_open(filetools.join(LIBTORRENT_PATH, 'libtorrent.so'), mode='rb') # Usa XbmcVFS #fp = open(os.path.join(LIBTORRENT_PATH, 'libtorrent.so'), 'rb') try: lt = imp.load_module('libtorrent', fp, pathname, description) finally: if fp: fp.close() except Exception as e1: logger.error(traceback.format_exc(1)) log('fp = ' + str(fp)) log('pathname = ' + str(pathname)) log('description = ' + str(description)) if fp: fp.close() from lib.python_libtorrent.python_libtorrent import get_libtorrent lt = get_libtorrent() except Exception as e2: try: logger.error(traceback.format_exc()) if fp: fp.close() e = e1 or e2 ok = platformtools.dialog_ok(config.get_localized_string(30035), config.get_localized_string(30036), config.get_localized_string(60015), str(e2)) except: pass try: if not e1 and e2: e1 = e2 except: try: if e2: e1 = e2 else: e1 = '' e2 = '' except: e1 = '' e2 = '' return lt, e, e1, e2 def log(texto): try: xbmc.log(texto, xbmc.LOGNOTICE) except: pass