# -*- coding: utf-8 -*- # ------------------------------------------------------------ # GenericTools # ------------------------------------------------------------ # Código reusable de diferentes partes de los canales que pueden # ser llamadados desde otros canales, y así carificar el formato # y resultado de cada canal y reducir el costo su mantenimiento # ------------------------------------------------------------ import re import os import sys import urllib import urlparse import datetime import time from channelselector import get_thumb from core import httptools from core import scrapertools from core import servertools from core import channeltools from core import filetools from core.item import Item from platformcode import config, logger, platformtools from core import tmdb channel_py = "newpct1" intervenido_judicial = 'Dominio intervenido por la Autoridad Judicial' intervenido_policia = ')", "", httptools.downloadpage(url, post=item.post, timeout=timeout).data) else: data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(url, timeout=timeout).data) data_comillas = data.replace("'", "\"") except: data = '' if not data: #no ha habido suerte, probamos con la siguiente url logger.error("ERROR 01: " + item.action + ": La Web no responde o la URL es erronea: " + url) continue #Hemos logrado leer la web, validamos si encontramos un línk válido en esta estructura #Evitar páginas engañosas que puede meter al canal en un loop infinito if (not ".com/images/no_imagen.jpg" in data and not ".com/images/imagen-no-disponible.jpg" in data) or item.action != "episodios": if patron: data_alt = scrapertools.find_single_match(data, patron) if not data_alt: data_alt = scrapertools.find_single_match(data_comillas, patron) if patron2 != None: data_alt = scrapertools.find_single_match(data_alt, patron2) if not data_alt: #no ha habido suerte, probamos con el siguiente canal logger.error("ERROR 02: " + item.action + ": Ha cambiado la estructura de la Web: " + url + " / Patron: " + patron) web_intervenida(item, data) data = '' continue else: item.url = url #guardamos la url que funciona break #por fin !!! Este canal parece que funciona else: logger.error("ERROR 02: " + item.action + ": Ha cambiado la estructura de la Web: " + url + " / Patron: " + patron) web_intervenida(item, data) data = '' continue if not data: #no ha habido suerte, probamos con el siguiente clone url_alt = [] continue else: break del item.extra2 #Borramos acción temporal excluyente if not data: #Si no ha logrado encontrar nada, salimos limpiando variables if item.channel == channel_py: if item.channel_alt: item.category = item.channel_alt.capitalize() del item.channel_alt else: if item.channel_alt: item.channel = item.channel_alt del item.channel_alt if item.url_alt: item.url = item.url_alt del item.url_alt item.channel_host = channel_host_failed #logger.debug(item) return (item, data) def web_intervenida(item, data, desactivar=True): logger.info() """ Llamada para verificar si la caída de un clone de Newpct1 es debido a una intervención judicial La llamada al método desde es: from lib import generictools item = generictools.web_intervenida(item, data[, desactivar=True]) - Entrada: data: resultado de la descarga. Nos permite analizar si se trata de una intervención - Entrada: desactivar=True: indica que desactiva el canal o clone en caso de intervención judicial - Salida: item.intervencion: devuele un array con el nombre del clone intervenido y el thumb de la autoridad que interviene. El canal puede anunciarlo. - Salida: Si es un clone de Newpct1, se desactiva el clone en el .json del Canal. Si es otro canal, se desactiva el canal en su .json. """ intervencion = () judicial = '' #Verificamos que sea una intervención judicial if intervenido_policia in data or intervenido_guardia in data or intervenido_sucuri in data: if intervenido_guardia in data: judicial = 'intervenido_gc.png' #thumb de la Benemérita if intervenido_policia in data: judicial = 'intervenido_pn.jpeg' #thumb de la Policia Nacional if intervenido_sucuri in data: judicial = 'intervenido_sucuri.png' #thumb de Sucuri category = item.category if not item.category: category = item.channel intervencion = (category, judicial) #Guardamos el nombre canal/categoría y el thumb judicial if not item.intervencion: item.intervencion = [] #Si no existe el array, lo creamos item.intervencion += [intervencion] #Añadimos esta intervención al array logger.error("ERROR 99: " + category + ": " + judicial + ": " + item.url + ": DESACTIVADO=" + str(desactivar) + " / DATA: " + data) if desactivar == False: #Si no queremos desactivar el canal, nos vamos return item #Cargamos en .json del canal para ver las listas de valores en settings. Carga las claves desordenadas !!! from core import filetools import json json_data = channeltools.get_channel_json(item.channel) if item.channel == channel_py: #Si es un clone de Newpct1, lo desactivamos for settings in json_data['settings']: #Se recorren todos los settings if settings['id'] == "clonenewpct1_channels_list": #Encontramos en setting action_excluded = scrapertools.find_single_match(settings['default'], "\('\d', '%s', '[^']+', '[^']*', '([^']*)'\)" % item.category.lower()) #extraemos el valor de action_excluded if action_excluded: if "intervenido" not in action_excluded: action_excluded += ', %s' % judicial #Agregamos el thumb de la autoridad judicial else: action_excluded = '%s' % judicial #Reemplazamos el estado a desactivado y agregamos el thumb de la autoridad judicial settings['default'] = re.sub(r"\('\d', '%s', ('[^']+', '[^']*'), '[^']*'\)" % item.category.lower(), r"('0', '%s', \1, '%s')" % (item.category.lower(), action_excluded), settings['default']) break else: #json_data['active'] = False #Se desactiva el canal json_data['thumbnail'] = ', thumb_%s' % judicial #Guardamos el thumb de la autoridad judicial #Guardamos los cambios hechos en el .json try: if item.channel != channel_py: disabled = config.set_setting('enabled', False, item.channel) #Desactivamos el canal disabled = config.set_setting('include_in_global_search', False, item.channel) #Lo sacamos de las búquedas globales channel_path = filetools.join(config.get_runtime_path(), "channels", item.channel + ".json") with open(channel_path, 'w') as outfile: #Grabamos el .json actualizado json.dump(json_data, outfile, sort_keys = True, indent = 2, ensure_ascii = False) except: logger.error("ERROR 98 al salvar el archivo: %s" % channel_path) #logger.debug(item) return item def redirect_clone_newpct1(item, head_nfo=None, it=None, path=False, overwrite=False, lookup=False): logger.info() """ Llamada para redirigir cualquier llamada a un clone de NewPct1 a NewPct1.py, o de una url de un canal caido a una alternativa Incluye las llamadas estándar del canal y la llamadas externas: - Play fron Library - Videolibrary Update La lógica es reemplazar item.channel por "newpct1" y dejar el nombre del clone en item.category. De esta forma utiliza siempre el código de NewPct1.py, aunque con las urls y apariencia del clone seleccionado por el usuario. En el caso de un canal/clone caído o intervenido judicialmente, puede reemplazar el canal en item.channel, o el clone en item.category, y la parte de item.url que se introduzca en una tabla. Esta conversión sólo se realiza si el canal original está inactivo, pero lo realiza siempre para los clones, o si el canal de origen y destino son los mismos. Este método interroga el .json de NewPct1 para extraer la lista de canales clones. Si item.channel es un clone de NewPct1 y está en esa lista, actualiza item.channel='newpct1' También en este .json está la tabla para la conversión de canales y urls: - activo: está o no activa esta entrada - canal_org: canal o clone de origen - canal_des: canal o clone de destino (puede ser el mismo) - url_org: parte de la url a sustituir de canal o clone de origen - url_des: parte de la url a sustituir de canal o clone de destino - patron1: expresión Regex aplicable a la url (opcional) - patron2: expresión Regex aplicable a la url (opcional) - patron3: expresión Regex aplicable a la url (opcional) - patron4: expresión Regex aplicable a la url (opcional) - patron5: expresión Regex aplicable a la url (opcional) - content_inc: contenido al que aplica esta entrada, o * (item.contentType o item.extra) - content_exc: contenido que se excluye de esta entrada (item.contentType) (opcional) - ow_force: indicador para la acción de "videolibrary_service.py". Puede crear la variable item.ow_force: - force: indica al canal que analize toda la serie y que videolibrary_service la reescriba - auto: indica a videolibrary_service que la reescriba - no: no acción para videolibrary_service, solo redirige en visionado de videolibrary - del: borra las estrucuturas de un determinado canal en videolibrary_service, quizás creadas por errores de un canal ejemplos: ('1', 'mejortorrent', 'mejortorrent1', 'http://www.mejortorrent.com/', 'https://mejortorrent1.com/', '(http.?:\/\/.*?\/)', 'http.?:\/\/.*?\/.*?-torrent.?-[^-]+-(?:[^-]+-)([^0-9]+-)', 'http.?:\/\/.*?\/.*?-torrent.?-[^-]+-(?:[^-]+-)[^0-9]+-\\d+-(Temporada-).html', 'http.?:\/\/.*?\/.*?-torrent.?-[^-]+-(?:[^-]+-)[^0-9]+-(\\d+)-', '', 'tvshow, season', '', 'force'), ('1', 'mejortorrent', 'mejortorrent1', 'http://www.mejortorrent.com/', 'https://mejortorrent1.com/', '(http.?:\/\/.*?\/)', 'http.?:\/\/.*?\/.*?-torrent.?-[^-]+-([^.]+).html', '', '', '', 'movie', '', 'force')", ('1', 'torrentrapid', 'torrentlocura', 'http://torrentrapid.com/', 'http://torrentlocura.com/', '', '', '', '', '', '*', '', 'no'), ('1', 'newpct1', '', '', '', '', '', '', '', '', '*', '', 'del'), La llamada recibe el parámetro Item, el .nfo y los devuleve actualizados, así como opcionalmente el parámetro "overwrite· que puede forzar la reescritura de todos los archivos de la serie, y el parámetro "path" si viene de videolibrary_service. Por último, recibe opcionalmente el parámetro "lookup" si se quiere solo averigurar si habrá migración para ese título, pero sin realizarla. """ if not it: it = Item() #logger.debug(item) ow_force_param = True channel_enabled = False update_stat = 0 delete_stat = 0 canal_org_des_list = [] json_path_list = [] if item.ow_force == '1': #Ha podido qudar activado de una pasada anteriores del item.ow_force logger.error('** item.ow_force: ' + item.path) #aviso que ha habido una incidencia if it.ow_force == '1': #Ha podido qudar activado de una pasada anteriores del it.ow_force if path and it.contentType != 'movies': try: nfo = filetools.join(path, '/tvshow.nfo') filetools.write(nfo, head_nfo + it.tojson()) #escribo el .nfo de la peli por si aborta update logger.error('** .nfo ACTUALIZADO: it.ow_force: ' + nfo) #aviso que ha habido una incidencia except: logger.error('** .nfo ERROR actualizar: it.ow_force: ' + nfo) #aviso que ha habido una incidencia #Array con los datos de los canales alternativos #Cargamos en .json de Newpct1 para ver las listas de valores en settings fail_over_list = channeltools.get_channel_json(channel_py) for settings in fail_over_list['settings']: #Se recorren todos los settings if settings['id'] == "clonenewpct1_channels_list": #Encontramos en setting fail_over_list = settings['default'] #Carga lista de clones if settings['id'] == "intervenidos_channels_list": #Encontramos en setting intervencion = settings['default'] #Carga lista de clones y canales intervenidos #primero tratamos los clones de Newpct1 channel_alt = item.channel if item.url: channel_alt = scrapertools.find_single_match(item.url, 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').lower() #Salvamos en nombre del canal o clone if not channel_alt: channel_alt = item.channel channel = "'%s'" % channel_alt category = '' if channel_alt != 'videolibrary': item.category = channel_alt.capitalize() category = "'%s'" % channel_alt channel_py_alt = '' if channel in fail_over_list : #Si es un clone de Newpct1, se actualiza el canal y la categoría item.channel = channel_py channel_py_alt = "'%s'" % channel_py if item.channel_host: #y se borran resto de pasadas anteriores del item.channel_host #Ahora tratamos las webs intervenidas, tranformamos la url, el nfo y borramos los archivos obsoletos de la serie if channel not in intervencion and channel_py_alt not in intervencion and category not in intervencion and channel_alt != 'videolibrary': #lookup return (item, it, overwrite) #... el canal/clone está listado import ast intervencion_list = ast.literal_eval(intervencion) #Convertir a Array el string #logger.debug(intervencion_list) if lookup == True: overwrite = False #Solo avisamos si hay cambios for activo, canal_org, canal_des, url_org, url_des, patron1, patron2, patron3, patron4, patron5, content_inc, content_exc, ow_force in intervencion_list: #Es esta nuestra entrada? if activo == '1' and (canal_org == channel_alt or canal_org == item.channel or channel_alt == 'videolibrary' or ow_force == 'del'): if ow_force == 'del': #Si es un borrado de estructuras erroneas, hacemos un proceso aparte canal_des_def = canal_des #Si hay canal de sustitución para item.library_urls, lo usamos if not canal_des_def and canal_org in item.library_urls and len(item.library_urls) == 1: #Si no, lo extraemos de la url canal_des_def = scrapertools.find_single_match(item.library_urls[canal_org], 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').lower() #salvamos la url actual de la estructura a borrar url_total = '' if item.url: url_total = item.url #Si existe item.url, lo salvamos para futuro uso if item.library_urls and canal_org in item.library_urls: #Si existe una entrada con el canal a borrar, lo procesamos if lookup == True: #Queremos que el canal solo visualice sin migración? overwrite = True #Avisamos que hay cambios continue logger.error('** REGLA: ' + canal_org + ', ' + canal_des+ ', ' + ow_force) logger.error('item.library_urls PREVIA: ' + str(item.library_urls)) url_total = item.library_urls[canal_org] #salvamos la url actual de la estructura a borrar url_total_status = False if len(item.library_urls) == 1 or canal_des: #si el nuevo canal no existe ya... item.library_urls.update({canal_des_def: url_total}) #restauramos la url con el nuevo canal url_total_status = True #marcamos esta url como válida overwrite = True #Le decimos que sobreescriba todos los .jsons item.ow_force = '1' #Le decimos que revise todas las temporadas if len(item.library_urls) > 1: item.library_urls.pop(canal_org, None) #borramos la url del canal a borrar overwrite = True #Le decimos que sobreescriba todos los .jsons item.ow_force = '1' #Le decimos que revise todas las temporadas if it.library_urls: it.library_urls = item.library_urls #lo salvamos en el .nfo, si lo hay if item.url and item.url == url_total and url_total_status == False: #si la url es la del canal borrado... for canal_vid, url_vid in item.library_urls.items(): canal_vid_alt = "'%s'" % canal_vid if canal_vid_alt not in intervencion: #... la sustituimos por la primera válida item.url = url_vid break if canal_vid_alt in fail_over_list: #Si es un clone de Newpct1, salvamos la nueva categoría item.category = scrapertools.find_single_match(item.url, 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').lower() #Salvamos categoría else: item.category = canal_vid.capitalize() #si no, salvamos nueva categoría logger.error('item.library_urls ACTUALIZADA: ' + str(item.library_urls)) if lookup == False: #si es migración completa... delete_stat += 1 #Ya hemos actualizado algo, o habrá que hacerlo... canal_org_des_list += [(canal_org, canal_des, url_total, ow_force)] #salvamos el resultado para su proceso else: if channel_alt == 'videolibrary': #Viene de videolibrary.list_movies: IMPRESCINDIBLE for canal_vid, url_vid in item.library_urls.items(): if canal_org != canal_vid: #Miramos si canal_org de la regla está en item.library_urls continue else: channel_alt = canal_org #Sí, ponermos el nombre del canal de origen channel_b = "'%s'" % canal_org if channel_b in fail_over_list: #Si es un clone de Newpct1, se actualiza a newpct1 channel_alt = channel_py if channel_alt == 'videolibrary': continue if item.contentType == "list": #Si viene de Videolibrary, le cambiamos ya el canal if item.channel != channel_py: item.channel = canal_des #Cambiamos el canal. Si es clone, lo hace el canal continue #Salimos sin hacer nada más. item está casi vacío if item.contentType not in content_inc and "*" not in content_inc: #Está el contenido el la lista de incluidos continue if item.contentType in content_exc: #Está el contenido excluido? continue if item.channel != channel_py: channel_enabled = channeltools.is_enabled(channel_alt) #Verificamos que el canal esté inactivo channel_enabled_alt = config.get_setting('enabled', channel_alt) channel_enabled = channel_enabled * channel_enabled_alt #Si está inactivo en algún sitio, tomamos eso if channel_enabled == 1 and canal_org != canal_des: #Si el canal está activo, puede ser solo... continue #... una intervención que afecte solo a una región if ow_force == 'no' and it.library_urls: #Esta regla solo vale para findvideos... continue #... salidmos si estamos actualizando if lookup == True: #Queremos que el canal solo visualice sin migración? if ow_force != 'no': overwrite = True #Avisamos que hay cambios continue #Salimos sin tocar archivos url_total = '' if item.url: url_total = item.url elif not item.url and item.library_urls: url_total = item.library_urls[canal_org] url_total = url_total.replace(url_org, url_des) #reemplazamos una parte de url url = '' if patron1: #Hay expresión regex? url += scrapertools.find_single_match(url_total, patron1) #La aplicamos a url if patron2: #Hay más expresión regex? url += scrapertools.find_single_match(url_total, patron2) #La aplicamos a url if patron3: #Hay más expresión regex? url += scrapertools.find_single_match(url_total, patron3) #La aplicamos a url if patron4: #Hay más expresión regex? url += scrapertools.find_single_match(url_total, patron4) #La aplicamos a url if patron5: #Hay más expresión regex? url += scrapertools.find_single_match(url_total, patron5) #La aplicamos a url if url: url_total = url #Guardamos la suma de los resultados intermedios update_stat += 1 #Ya hemos actualizado algo canal_org_des_list += [(canal_org, canal_des, url_total, ow_force)] #salvamos el resultado para su proceso if update_stat > 0 or delete_stat > 0: #Ha habido alguna actualización o borrado? Entonces salvamos if (update_stat > 0 and path != False) or item.ow_force == '1': logger.error('** Lista de Actualizaciones a realizar: ' + str(canal_org_des_list)) for canal_org_def, canal_des_def, url_total, ow_force_def in canal_org_des_list: #pasamos por todas las "parejas" cambiadas if ow_force_def != 'del': url_total_def = url_total if item.channel == channel_py or channel in fail_over_list: #Si es Newpct1... if item.contentType == "tvshow": url_total_def = re.sub(r'\/\d+\/?$', '', url_total) #parece que con el título encuentra la serie, normalmente... if item.url: item.url = url_total_def #Salvamos la url convertida if item.library_urls: item.library_urls.pop(canal_org_def, None) item.library_urls.update({canal_des_def: url_total}) it.library_urls = item.library_urls if item.channel != channel_py and item.channel != 'videolibrary': item.channel = canal_des_def #Cambiamos el canal. Si es clone, lo hace el canal if channel_alt == item.category.lower(): #Actualizamos la Categoría y si la tenía item.category = item.channel.capitalize() if ow_force_def == 'force' and item.contentType != 'movie': #Queremos que el canal revise la serie entera? item.ow_force = '1' #Se lo decimos if ow_force_def in ['force', 'auto']: #Sobreescribir la series? overwrite = True #Sí, lo marcamos if it.library_urls and path != False and ow_force_def != 'no': #Continuamos si hay .nfo, path, y queremos actualizarlo item.update_next = '1' del item.update_next #Borramos estos campos para forzar la actualización ya it.update_next = '1' del it.update_next # Listamos todos los ficheros de la serie, asi evitamos tener que comprobar si existe uno por uno canal_erase_list = [] raiz, carpetas_series, ficheros = filetools.walk(path).next() ficheros = [filetools.join(path, f) for f in ficheros] #Almacenamos la lista de archivos de la carpeta for archivo in ficheros: for canal_org_def, canal_des_def, url_total, ow_force_def in canal_org_des_list: #pasamos por todas las "parejas" a borrar canal_erase = '[%s]' % canal_org_def canal_new = '[%s]' % canal_des_def if canal_erase in archivo: #Borramos los .json que sean de los canal afectados if canal_des_def: item_json = Item().fromjson(filetools.read(archivo)) #leemos el .json ante de borrarlo para salvar... title = item_json.title #... el título con su formato language = item_json.language #... los idiomas, que no están en el .nfo wanted = item_json.wanted #... y wanted con el título original json_path = archivo.replace(canal_erase, canal_new) #Salvamos el path del .json para luego crearlo json_path_list += [(canal_org_def, canal_des_def, url_total, json_path, title, language, wanted)] filetools.remove(archivo) #Borramos el .json logger.error('** BORRAMOS: ' + str(archivo)) if ow_force_def == 'del': #Si la función es 'del' ... overwrite = True #Le decimos que sobreescriba todos los .jsons item.ow_force = '1' #Le decimos que revise todas las temporadas #Si se ha cambiado algo, se actualizan los .nfo if item.contentType == "movie" and ".nfo" in archivo: if it.ow_force: del it.ow_force filetools.write(archivo, head_nfo + it.tojson()) #escribo el .nfo de la peli por si aborta update if item.contentType != "movie" and "tvshow.nfo" in archivo: filetools.write(archivo, head_nfo + it.tojson()) #escribo el tvshow.nfo por si aborta update #Aquí convertimos las películas. Después de borrado el .json, dejamos que videolibrarytools lo regenere if item.contentType == "movie": #Dejamos que regenere el archivo .json item_movie = item.clone() if item_movie.ow_force: del item_movie.ow_force item_movie.update_last = '1' del item_movie.update_last del item_movie.library_playcounts #Borramos lo que no es necesario en el .json del item_movie.library_urls del item_movie.nfo del item_movie.path del item_movie.strm_path del item_movie.text_color if not item_movie.context: item_movie.context = "['buscar_trailer']" if not item_movie.extra: item_movie.extra = "peliculas" if json_path_list: logger.error('** .json LIST: ' + str(json_path_list)) for canal_org_def, canal_des_def, url_total, json_path, title, language, wanted in json_path_list: #pasamos por todas canales logger.error('** ESCRIBIMOS: ' + json_path) item_movie.channel = canal_des_def #mombre del canal migrado if not item_movie.category: item_movie.category = canal_des_def.capitalize() #categoría item_movie.url = url_total #url migrada if title: item_movie.title = title #restaurmos el título con formato if language: item_movie.language = language #restaurmos los idiomas if wanted: item_movie.wanted = wanted #restaurmos wanted item_movie.added_replacing = canal_org_def #guardamos la traza del canal reemplazado filetools.write(json_path, item_movie.tojson()) #Salvamos el nuevo .json de la película if (update_stat > 0 and path != False and ow_force_def in ['force', 'auto']) or item.ow_force == '1' or len(json_path_list) > 0: logger.error('ITEM cambiado') logger.error(item) return (item, it, overwrite) def dejuice(data): logger.info() # Metodo para desobfuscar datos de JuicyCodes import base64 from lib import jsunpack juiced = scrapertools.find_single_match(data, 'JuicyCodes.Run\((.*?)\);') b64_data = juiced.replace('+', '').replace('"', '') b64_decode = base64.b64decode(b64_data) dejuiced = jsunpack.unpack(b64_decode) return dejuiced