# -*- 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 sys import urllib import urlparse import datetime 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(item.url, post=item.post, timeout=timeout).data) else: data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url, timeout=timeout).data) except: data = '' if not data: #no ha habido suerte, probamos con el siguiente canal válido logger.error("ERROR 01: " + item.action + ": La Web no responde o la URL es erronea: " + item.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 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: " + item.url + " / Patron: " + patron) web_intervenida(item, data) data = '' continue else: break #por fin !!! Este canal parece que funciona else: logger.error("ERROR 02: " + item.action + ": Ha cambiado la estructura de la Web: " + item.url + " / Patron: " + patron) web_intervenida(item, data) data = '' continue 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 ejemplo: ('1', 'mejortorrent', 'mejortorrent1', 'http://www.mejortorrent.com/', 'https://mejortorrent1.com/', 'auto') 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 #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 #Salvamos en nombre del canal o clone channel = "'%s'" % item.channel if channel in fail_over_list: #Si es un clone de Newpct1, se actualiza el canal item.channel = channel_py #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_alt != 'videolibrary': #Hacemos una lookup para ver si... 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: if activo == '1' and (canal_org == channel_alt or channel_alt == 'videolibrary'): #Es esta nuestra entrada? if channel_alt == 'videolibrary': #Viene de videolibrary.list_movies 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_def = canal_org canal_des_def = canal_des ow_force_def = ow_force if update_stat > 0: #Ha habido alguna actualización? Entonces salvamos if item.channel == channel_py or channel in fail_over_list: #Si es Newpct1... if item.contentType == "tvshow": url_total = re.sub(r'\/\d+\/?$', '', url_total) #parece que con el título ecuentra la serie, normalmente... if item.url: item.url = url_total #Salvamos la url convertida if item.library_urls: item.library_urls.pop(canal_org_def, None) item.library_urls = {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 #logger.debug(canal_org_def + canal_des_def + ow_force_def) if it.library_urls and path != False and ow_force_def != 'no': #Continuamos si hay .nfo, path, y queremos actualizarlo if item.update_next: del item.update_next #Borramos estos campos para forzar la actualización ya if it.update_next: del it.update_next # Listamos todos los ficheros de la serie, asi evitamos tener que comprobar si existe uno por uno 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 canal_erase = '[%s]' % canal_org_def for archivo in ficheros: if canal_erase in archivo: #Borramos los .json que sean del canal intervenido json_path = archivo.replace(canal_org_def, canal_des_def) #Salvamos el path del .json para luego crearlo filetools.remove(archivo) if item.contentType == "movie" and ".nfo" in archivo: 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() item_movie.channel = canal_des_def #mombre del canal migrado del item_movie.library_playcounts #Borramos lo que no es necesario en el .json del item_movie.library_urls item_movie.url = url_total #url migrada del item_movie.nfo del item_movie.path del item_movie.strm_path del item_movie.text_color 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 != 'no': logger.debug(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