From 86ec11849b093d5f304bf4aa68181640417df75f Mon Sep 17 00:00:00 2001 From: danielr460 Date: Sat, 31 Mar 2018 13:47:16 -0500 Subject: [PATCH 01/22] Danimados: Correccion en la busqueda de capitulos y de enlaces a los servidores --- plugin.video.alfa/channels/danimados.py | 28 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugin.video.alfa/channels/danimados.py b/plugin.video.alfa/channels/danimados.py index e8b1c74b..9f2f7ad0 100644 --- a/plugin.video.alfa/channels/danimados.py +++ b/plugin.video.alfa/channels/danimados.py @@ -117,12 +117,12 @@ def episodios(item): itemlist = [] data = httptools.downloadpage(item.url).data - data = re.sub(r"\n|\r|\t|\s{2}| ", "", data) - + data = re.sub(r"\n|\r|\t|\s{2}| ", "", data) data_lista = scrapertools.find_single_match(data, '' #Filtrado por url, compatibilidad con mispelisy.series.com + #logger.debug("patron: " + patron + " / data: " + data) + if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com + data = ' Documentales' + else: + data = scrapertools.get_match(data, patron) - patron = '([^>]+)' + patron = '<.*?href="([^"]+)".*?>([^>]+)' matches = re.compile(patron, re.DOTALL).findall(data) for scrapedurl, scrapedtitle in matches: @@ -55,12 +60,12 @@ def submenu(item): itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra="pelilist")) itemlist.append( Item(channel=item.channel, action="alfabeto", title=title + " [A-Z]", url=url, extra="pelilist")) - + if item.extra == "peliculas": itemlist.append(Item(channel=item.channel, action="listado", title="Películas 4K", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) itemlist.append( Item(channel=item.channel, action="alfabeto", title="Películas 4K" + " [A-Z]", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) - + return itemlist @@ -91,16 +96,16 @@ def listado(item): itemlist = [] url_next_page ='' - data = httptools.downloadpage(item.url).data + data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) + #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - #logger.debug(data) + logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': logger.debug('item.title: %s'% item.title) patron = '' - logger.debug("patron=" + patron) fichas = scrapertools.get_match(data, patron) page_extra = item.extra else: @@ -109,11 +114,13 @@ def listado(item): patron = '(.*?)<\/b><\/font>') + real_title = scrapertools.find_single_match(title, r'(.*?)Temporada.*?<\/strong>') #series + if real_title == "": + real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') + real_title = scrapertools.htmlclean(real_title) + calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?]+>[\[]\s*(?P.*?)\s*[\]]<\/span>') #series + if calidad == "": + calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies + year = scrapertools.find_single_match(thumb, r'-(\d{4})') + + # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ") + title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") + title = re.sub(r'(Calidad.*?\])', '', title) + + if real_title == "": + real_title = title + if calidad == "": + calidad = title + context = "movie" # no mostramos lo que no sean videos - if "/juego/" in url or "/varios/" in url: + if "juego/" in url: continue - if ".com/series" in url: + # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie + if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: + calidad_mps = "series/" + if "seriehd" in url: + calidad_mps = "series-hd/" + if "serievo" in url: + calidad_mps = "series-vo/" + if "serie-vo" in url: + calidad_mps = "series-vo/" + + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + + if "/0_" not in thumb: + serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P\d+).*?.*') + if len(serieid) > 5: + serieid = "" + else: + serieid = "" + + url = host + calidad_mps + real_title_mps + "/" + serieid + + real_title_mps = real_title_mps.replace("-", " ") + #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + real_title = real_title_mps + + show = real_title - show = real_title + if ".com/serie" in url and "/miniseries" not in url: + + context = "tvshow" - itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, - context=["buscar_trailer"], contentSerieName=show)) + itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: - itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, - context=["buscar_trailer"])) - + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / show: " + show + " / calidad: " + calidad) + + tmdb.set_infoLabels(itemlist, True) + if post: itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", thumbnail=get_thumb("next.png"))) @@ -253,7 +314,6 @@ def listado_busqueda(item): def findvideos(item): logger.info() itemlist = [] - ## Cualquiera de las tres opciones son válidas # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") @@ -263,32 +323,36 @@ def findvideos(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("$!", "#!").replace("'", "\"").replace("ñ", "ñ").replace("//pictures", "/pictures") - - title = scrapertools.find_single_match(data, "

([^<]+)<\/strong>[^<]+<\/h1>") - title += scrapertools.find_single_match(data, "

[^<]+<\/strong>([^<]+)<\/h1>") - caratula = scrapertools.find_single_match(data, '
.*?src="([^"]+)"') - - #
Descarga tu Archivo torrent!
+ + title = scrapertools.find_single_match(data, "([^<]+)<\/strong>.*?<\/h1>") #corregido para adaptarlo a mispelisy.series.com + title += scrapertools.find_single_match(data, "[^<]+<\/strong>([^<]+)<\/h1>") #corregido para adaptarlo a mispelisy.series.com + #caratula = scrapertools.find_single_match(data, '
.*?src="([^"]+)"') + caratula = scrapertools.find_single_match(data, ']+>.*?' #patron_ver = '
]+>.*?' @@ -309,37 +373,48 @@ def findvideos(item): patron = '
<\/div[^<]+
([^<]+)?<\/div[^<]+
([^<]+)?' patron += '<\/div[^<]+
([^<]+)?<\/div[^<]+
0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: servidor = servidor.replace("streamin", "streaminto") - titulo = titulo + " [" + servidor + "]" + titulo = title mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=item.title, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) except: pass + if len(enlaces_descargar) > 0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") + titulo = "Partes " p = 1 + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " [" + servidor + "]" + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title p += 1 mostrar_server = True if config.get_setting("hidepremium"): @@ -349,11 +424,12 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=item.title, url=enlace, thumbnail=logo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) except: pass + return itemlist @@ -363,6 +439,8 @@ def episodios(item): infoLabels = item.infoLabels data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + calidad = item.quality + pattern = '
    (.*?)
' % "pagination" # item.pattern pagination = scrapertools.find_single_match(data, pattern) if pagination: @@ -381,22 +459,43 @@ def episodios(item): logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - + data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
    (.*?)
' % "buscar-list" # item.pattern data = scrapertools.get_match(data, pattern) + #logger.debug("data: " + data) - pattern = ']*>
]+>(?P.*?)

' + if "pelisyseries.com" in host: + pattern = ']*>
]+>(?P.*?)?<\/h3>.*?<\/li>' + else: + pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) + #logger.debug("patron: " + pattern) + #logger.debug(matches) + + season = "1" for url, thumb, info in matches: + if "pelisyseries.com" in host: + interm = url + url = thumb + thumb = interm + if "\d+)?)<.+?]+>(?P.*?)\s*Calidad\s*]+>" \ - "[\[]\s*(?P.*?)\s*[\]]" + pattern = ".*?[^>]+>.*?Temporada\s*(?P\d+)?.*?Capitulo(?:s)?\s*(?P\d+)?" \ + "(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>" \ + "[\[]\s*(?P.*?)?\s*[\]]<\/span>" + if "Especial" in info: # Capitulos Especiales + pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P\d+).*?\].*?Capitulo.*?\[\s*(?P\d+).*?\]?(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>[\[]\s*(?P.*?)?\s*[\]]<\/span>" + logger.debug("patron: " + pattern) + logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] + if match['season'] is None: match['season'] = season + if match['episode'] is None: match['episode'] = "0" + if match['quality']: item.quality = match['quality'] + if match["episode2"]: multi = True title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), @@ -408,12 +507,17 @@ def episodios(item): match["lang"], match["quality"]) else: # old style - pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+)(?P\d{2})(?:_(?P\d+)" \ + pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+).*?(?P\d{2})(?:_(?P\d+)" \ "(?P\d{2}))?.*?\].*?(?:\[(?P.*?)\])?" - + logger.debug("patron: " + pattern) + logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - # logger.debug("data %s" % match) + #logger.debug("data %s" % match) + + #if match['season'] is "": match['season'] = season + #if match['episode'] is "": match['episode'] = "0" + #logger.debug(match) str_lang = "" if match["lang"] is not None: @@ -436,18 +540,19 @@ def episodios(item): season = match['season'] episode = match['episode'] + logger.debug("title: " + title + " / url: " + url + " / calidad: " + item.quality + " / multi: " + str(multi) + " / Season: " + str(season) + " / EpisodeNumber: " + str(episode)) itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=item.quality, multi=multi, contentSeason=season, contentEpisodeNumber=episode, infoLabels = infoLabels)) - # order list + #tmdb.set_infoLabels(itemlist, True) tmdb.set_infoLabels_itemlist(itemlist, seekTmdb = True) if len(itemlist) > 1: itemlist = sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber))) if config.get_videolibrary_support() and len(itemlist) > 0: itemlist.append( - item.clone(title="[COLOR orange][B]Añadir esta serie a la videoteca[/B][/COLOR]", action="add_serie_to_library", extra="episodios")) + item.clone(title="Añadir esta serie a la videoteca", action="add_serie_to_library", extra="episodios", quality=calidad)) return itemlist diff --git a/plugin.video.alfa/channels/mispelisyseries.json b/plugin.video.alfa/channels/mispelisyseries.json index 9d5ae057..73b0db8b 100755 --- a/plugin.video.alfa/channels/mispelisyseries.json +++ b/plugin.video.alfa/channels/mispelisyseries.json @@ -9,7 +9,8 @@ "categories": [ "torrent", "movie", - "tvshow" + "tvshow", + "documentary" ], "settings": [ { diff --git a/plugin.video.alfa/channels/mispelisyseries.py b/plugin.video.alfa/channels/mispelisyseries.py index ba65968e..0e4d44cd 100644 --- a/plugin.video.alfa/channels/mispelisyseries.py +++ b/plugin.video.alfa/channels/mispelisyseries.py @@ -1,137 +1,75 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re -import urllib -import urlparse +from channelselector import get_thumb from core import httptools from core import scrapertools from core import servertools from core.item import Item -from platformcode import logger -from channelselector import get_thumb +from platformcode import config, logger +from core import tmdb host = 'http://mispelisyseries.com/' + def mainlist(item): logger.info() itemlist = [] - itemlist.append(Item(channel=item.channel, action="menu", title="Películas", url=host, - extra="Peliculas", folder=True, thumbnail=get_thumb('movies', auto=True))) + + thumb_pelis=get_thumb("channels_movie.png") + thumb_series=get_thumb("channels_tvshow.png") + thumb_search = get_thumb("search.png") + + itemlist.append(Item(channel=item.channel, action="submenu", title="Películas", url=host, + extra="peliculas", thumbnail=thumb_pelis )) + + itemlist.append(Item(channel=item.channel, action="submenu", title="Series", url=host, extra="series", + thumbnail=thumb_series)) + + itemlist.append(Item(channel=item.channel, action="submenu", title="Documentales", url=host, extra="varios", + thumbnail=thumb_series)) itemlist.append( - Item(channel=item.channel, action="menu", title="Series", url=host, extra="Series", - folder=True, thumbnail=get_thumb('tvshows', auto=True))) - itemlist.append(Item(channel=item.channel, action="search", title="Buscar", url=host + 'buscar', - thumbnail=get_thumb('search', auto=True))) + Item(channel=item.channel, action="search", title="Buscar", url=host + "buscar", thumbnail=thumb_search)) + return itemlist - -def menu(item): +def submenu(item): logger.info() itemlist = [] - data = httptools.downloadpage(item.url).data - # logger.info("data="+data) + data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com - data = scrapertools.find_single_match(data, item.extra + "") - # logger.info("data="+data) + #patron = '
  • .*?
      (.*?)
    ' + patron = '
  • <.*?href="'+item.url+item.extra + '/">.*?(.*?)' #Filtrado por url, compatibilidad con mispelisy.series.com + #logger.debug("patron: " + patron + " / data: " + data) + if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com + data = ' Documentales' + else: + data = scrapertools.get_match(data, patron) - patron = "
  • ]+>([^<]+)
  • " + patron = '<.*?href="([^"]+)".*?>([^>]+)' matches = re.compile(patron, re.DOTALL).findall(data) for scrapedurl, scrapedtitle in matches: - title = scrapedtitle - url = urlparse.urljoin(item.url, scrapedurl) - thumbnail = "" - plot = "" - itemlist.append(Item(channel=item.channel, action="lista", title=title, url=url, thumbnail=thumbnail, plot=plot, - folder=True)) - - - if title != "Todas las Peliculas": - itemlist.append( - Item(channel=item.channel, action="alfabetico", title=title + " [A-Z]", url=url, thumbnail=thumbnail, - plot=plot, folder=True)) - + title = scrapedtitle.strip() + url = scrapedurl + itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra="pelilist")) itemlist.append( - Item(channel=item.channel, action="alfabetico", title=title + " [A-Z]", url=url, thumbnail=thumbnail, - plot=plot, - folder=True)) - - if 'películas' in item.title.lower(): - new_item = item.clone(title='Peliculas 4K', url=host+'buscar', post='q=4k', action='listado2', - pattern='buscar-list') - itemlist.append(new_item) - + Item(channel=item.channel, action="alfabeto", title=title + " [A-Z]", url=url, extra="pelilist")) + + if item.extra == "peliculas": + itemlist.append(Item(channel=item.channel, action="listado", title="Películas 4K", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) + itemlist.append( + Item(channel=item.channel, action="alfabeto", title="Películas 4K" + " [A-Z]", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) + return itemlist -def search(item, texto): - logger.info("search:" + texto) - # texto = texto.replace(" ", "+") - - #try: - item.post = "q=%s" % texto - item.pattern = "buscar-list" - itemlist = listado2(item) - - return itemlist - - # Se captura la excepción, para no interrumpir al buscador global si un canal falla - # except: - # import sys - # for line in sys.exc_info(): - # logger.error("%s" % line) - # return [] - -def newest(categoria): - itemlist = [] - item = Item() - try: - if categoria in ['peliculas', 'torrent']: - item.url = host+"peliculas" - - elif categoria == 'series': - item.url = host+"series" - - if categoria == '4k': - - item.url = Host + '/buscar' - - item.post = 'q=4k' - - item.pattern = 'buscar-list' - - action = listado2(item) - - else: - return [] - - itemlist = lista(item) - if itemlist[-1].title == ">> Página siguiente": - itemlist.pop() - - # Esta pagina coloca a veces contenido duplicado, intentamos descartarlo - dict_aux = {} - for i in itemlist: - if not i.url in dict_aux: - dict_aux[i.url] = i - else: - itemlist.remove(i) - - # Se captura la excepción, para no interrumpir al canal novedades si un canal falla - except: - import sys - for line in sys.exc_info(): - logger.error("{0}".format(line)) - return [] - - # return dict_aux.values() - return itemlist - - -def alfabetico(item): +def alfabeto(item): logger.info() itemlist = [] @@ -148,93 +86,137 @@ def alfabetico(item): title = scrapedtitle.upper() url = scrapedurl - itemlist.append(Item(channel=item.channel, action="lista", title=title, url=url)) + itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra=item.extra)) return itemlist -def lista(item): +def listado(item): logger.info() itemlist = [] + url_next_page ='' - # Descarga la pagina - data = httptools.downloadpage(item.url, post=item.extra).data - # logger.info("data="+data) + data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) + #data = httptools.downloadpage(item.url).data + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + logger.debug('item.modo: %s'%item.modo) + logger.debug('item.extra: %s'%item.extra) + + if item.modo != 'next' or item.modo =='': + logger.debug('item.title: %s'% item.title) + patron = '
      (.*?)
    ' + fichas = scrapertools.get_match(data, patron) + page_extra = item.extra + else: + fichas = data + page_extra = item.extra - bloque = scrapertools.find_single_match(data, '(?:' + #patron_ver = '
    ]+>.*?' + + #match_ver = scrapertools.find_single_match(data, patron_ver) + #match_descargar = scrapertools.find_single_match(data, patron_descargar) + + #patron = '
    0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) + + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: + if "Ver" in titulo: + servidor = servidor.replace("streamin", "streaminto") + titulo = title + mostrar_server = True + if config.get_setting("hidepremium"): + mostrar_server = servertools.is_server_enabled(servidor) + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + if mostrar_server: + try: + devuelve = servertools.findvideosbyserver(enlace, servidor) + if devuelve: + enlace = devuelve[0][1] + itemlist.append( + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + except: + pass + + if len(enlaces_descargar) > 0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: + if "Ver" not in titulo: + servidor = servidor.replace("uploaded", "uploadedto") + partes = enlace.split(" ") + titulo = "Partes " + p = 1 + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + for enlace in partes: + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title + p += 1 + mostrar_server = True + if config.get_setting("hidepremium"): + mostrar_server = servertools.is_server_enabled(servidor) + if mostrar_server: + try: + devuelve = servertools.findvideosbyserver(enlace, servidor) + if devuelve: + enlace = devuelve[0][1] + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, + plot=item.plot, folder=False)) + except: + pass + + return itemlist def episodios(item): logger.info() itemlist = [] + infoLabels = item.infoLabels + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + calidad = item.quality + + pattern = '
      (.*?)
    ' % "pagination" # item.pattern + pagination = scrapertools.find_single_match(data, pattern) + if pagination: + pattern = '
  • Last<\/a>' + full_url = scrapertools.find_single_match(pagination, pattern) + url, last_page = scrapertools.find_single_match(full_url, r'(.*?\/pg\/)(\d+)') + list_pages = [item.url] + for x in range(2, int(last_page) + 1): + response = httptools.downloadpage('%s%s'% (url,x)) + if response.sucess: + list_pages.append("%s%s" % (url, x)) + else: + list_pages = [item.url] - # Descarga la pagina - data = httptools.downloadpage(item.url, post=item.extra).data - # logger.info("data="+data) + for index, page in enumerate(list_pages): + logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com + pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern + data = scrapertools.get_match(data, pattern) + #logger.debug("data: " + data) - patron = '
    \d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>" \ + "[\[]\s*(?P.*?)?\s*[\]]<\/span>" + if "Especial" in info: # Capitulos Especiales + pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P\d+).*?\].*?Capitulo.*?\[\s*(?P\d+).*?\]?(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>[\[]\s*(?P.*?)?\s*[\]]<\/span>" + logger.debug("patron: " + pattern) + logger.debug(info) + r = re.compile(pattern) + match = [m.groupdict() for m in r.finditer(info)][0] + + if match['season'] is None: match['season'] = season + if match['episode'] is None: match['episode'] = "0" + if match['quality']: item.quality = match['quality'] + + if match["episode2"]: + multi = True + title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"], + match["quality"]) + else: + multi = False + title = "%s (%sx%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"], match["quality"]) + + else: # old style + pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+).*?(?P\d{2})(?:_(?P\d+)" \ + "(?P\d{2}))?.*?\].*?(?:\[(?P.*?)\])?" + logger.debug("patron: " + pattern) + logger.debug(info) + r = re.compile(pattern) + match = [m.groupdict() for m in r.finditer(info)][0] + #logger.debug("data %s" % match) + + #if match['season'] is "": match['season'] = season + #if match['episode'] is "": match['episode'] = "0" + #logger.debug(match) + + str_lang = "" + if match["lang"] is not None: + str_lang = "[%s]" % match["lang"] + + if match["season2"] and match["episode2"]: + multi = True + if match["season"] == match["season2"]: + + title = "%s (%sx%s-%s) %s[%s]" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang, match["quality"]) + else: + title = "%s (%sx%s-%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang, + match["quality"]) + else: + title = "%s (%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], str_lang, + match["quality"]) + multi = False + + season = match['season'] + episode = match['episode'] + logger.debug("title: " + title + " / url: " + url + " / calidad: " + item.quality + " / multi: " + str(multi) + " / Season: " + str(season) + " / EpisodeNumber: " + str(episode)) + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, + quality=item.quality, multi=multi, contentSeason=season, + contentEpisodeNumber=episode, infoLabels = infoLabels)) + # order list + #tmdb.set_infoLabels(itemlist, True) + tmdb.set_infoLabels_itemlist(itemlist, seekTmdb = True) + if len(itemlist) > 1: + itemlist = sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber))) + + if config.get_videolibrary_support() and len(itemlist) > 0: itemlist.append( - Item(channel=item.channel, action="findvideos", title=title, fulltitle=title, url=url, thumbnail=thumbnail, - plot=plot, folder=True)) - - next_page_url = scrapertools.find_single_match(data, "(.*?)
    ') - item.plot = scrapertools.htmlclean(item.plot).strip() - item.contentPlot = item.plot - al_url_fa = scrapertools.find_single_match(data, 'location\.href.*?=.*?"http:\/\/(?:tumejorserie|tumejorjuego).*?link=(.*?)"') - if al_url_fa == "": - al_url_fa = scrapertools.find_single_match(data, 'location\.href.*?=.*?"%s(.*?)" ' % host) - if al_url_fa != "": - al_url_fa = host + al_url_fa - logger.info("torrent=" + al_url_fa) - itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title="Vídeo en torrent", fulltitle=item.title, - url=al_url_fa, thumbnail=servertools.guess_server_thumbnail("torrent"), plot=item.plot, folder=False, - parentContent=item)) + itemlist = listado(item) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + item.url = host+'series/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() - patron = '
  • .*?
      (.*?)
    ' - patron = '
  • .*?
      (.*?)
    ' #Filtrado por url - data = scrapertools.get_match(data, patron) + patron = '
  • <.*?href="'+item.url+item.extra + '/">.*?(.*?)' #Filtrado por url, compatibilidad con mispelisy.series.com + #logger.debug("patron: " + patron + " / data: " + data) + if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com + data = ' Documentales' + else: + data = scrapertools.get_match(data, patron) - patron = '([^>]+)' + patron = '<.*?href="([^"]+)".*?>([^>]+)' matches = re.compile(patron, re.DOTALL).findall(data) for scrapedurl, scrapedtitle in matches: @@ -92,15 +97,15 @@ def listado(item): url_next_page ='' data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) + #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - #logger.debug(data) + logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': logger.debug('item.title: %s'% item.title) patron = '
      (.*?)
    ' - logger.debug("patron=" + patron) fichas = scrapertools.get_match(data, patron) page_extra = item.extra else: @@ -109,11 +114,13 @@ def listado(item): patron = '(.*?)<\/b><\/font>') + real_title = scrapertools.find_single_match(title, r'(.*?)Temporada.*?<\/strong>') #series + if real_title == "": + real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') + real_title = scrapertools.htmlclean(real_title) + calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?]+>[\[]\s*(?P.*?)\s*[\]]<\/span>') #series + if calidad == "": + calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies + year = scrapertools.find_single_match(thumb, r'-(\d{4})') + + # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ") + title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") + title = re.sub(r'(Calidad.*?\])', '', title) + + if real_title == "": + real_title = title + if calidad == "": + calidad = title + context = "movie" # no mostramos lo que no sean videos - if "/juego/" in url or "/varios/" in url: + if "juego/" in url: continue - if ".com/series" in url: + # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie + if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: + calidad_mps = "series/" + if "seriehd" in url: + calidad_mps = "series-hd/" + if "serievo" in url: + calidad_mps = "series-vo/" + if "serie-vo" in url: + calidad_mps = "series-vo/" + + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + + if "/0_" not in thumb: + serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P\d+).*?.*') + if len(serieid) > 5: + serieid = "" + else: + serieid = "" + + url = host + calidad_mps + real_title_mps + "/" + serieid + + real_title_mps = real_title_mps.replace("-", " ") + #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + real_title = real_title_mps + + show = real_title - show = real_title + if ".com/serie" in url and "/miniseries" not in url: + + context = "tvshow" - itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, - context=["buscar_trailer"], contentSerieName=show)) + itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: - itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, - context=["buscar_trailer"])) - + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / show: " + show + " / calidad: " + calidad) + + tmdb.set_infoLabels(itemlist, True) + if post: - itemlist.append(item.clone(channel=item.channel, action="listado2", title=">> Página siguiente", + itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", thumbnail=get_thumb("next.png"))) return itemlist @@ -253,7 +314,6 @@ def listado2(item): def findvideos(item): logger.info() itemlist = [] - ## Cualquiera de las tres opciones son válidas # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") @@ -263,32 +323,36 @@ def findvideos(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("$!", "#!").replace("'", "\"").replace("ñ", "ñ").replace("//pictures", "/pictures") - - title = scrapertools.find_single_match(data, "

    ([^<]+)<\/strong>[^<]+<\/h1>") - title += scrapertools.find_single_match(data, "

    [^<]+<\/strong>([^<]+)<\/h1>") - caratula = scrapertools.find_single_match(data, '
    .*?src="([^"]+)"') - - #
    Descarga tu Archivo torrent!
    + + title = scrapertools.find_single_match(data, "([^<]+)<\/strong>.*?<\/h1>") #corregido para adaptarlo a mispelisy.series.com + title += scrapertools.find_single_match(data, "[^<]+<\/strong>([^<]+)<\/h1>") #corregido para adaptarlo a mispelisy.series.com + #caratula = scrapertools.find_single_match(data, '
    .*?src="([^"]+)"') + caratula = scrapertools.find_single_match(data, ']+>.*?' #patron_ver = '
    ]+>.*?' @@ -309,37 +373,48 @@ def findvideos(item): patron = '
    <\/div[^<]+
    ([^<]+)?<\/div[^<]+
    ([^<]+)?' patron += '<\/div[^<]+
    ([^<]+)?<\/div[^<]+
    0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: servidor = servidor.replace("streamin", "streaminto") - titulo = titulo + " [" + servidor + "]" + titulo = title mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=item.title, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) except: pass + if len(enlaces_descargar) > 0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") + titulo = "Partes " p = 1 + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " [" + servidor + "]" + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title p += 1 mostrar_server = True if config.get_setting("hidepremium"): @@ -349,11 +424,12 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=item.title, url=enlace, thumbnail=logo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) except: pass + return itemlist @@ -363,6 +439,8 @@ def episodios(item): infoLabels = item.infoLabels data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + calidad = item.quality + pattern = '
      (.*?)
    ' % "pagination" # item.pattern pagination = scrapertools.find_single_match(data, pattern) if pagination: @@ -381,22 +459,43 @@ def episodios(item): logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - + data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern data = scrapertools.get_match(data, pattern) + #logger.debug("data: " + data) - pattern = ']*>
    ]+>(?P.*?)

    ' + if "pelisyseries.com" in host: + pattern = ']*>
    ]+>(?P.*?)?<\/h3>.*?<\/li>' + else: + pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) + #logger.debug("patron: " + pattern) + #logger.debug(matches) + + season = "1" for url, thumb, info in matches: + if "pelisyseries.com" in host: + interm = url + url = thumb + thumb = interm + if "\d+)?)<.+?]+>(?P.*?)\s*Calidad\s*]+>" \ - "[\[]\s*(?P.*?)\s*[\]]" + pattern = ".*?[^>]+>.*?Temporada\s*(?P\d+)?.*?Capitulo(?:s)?\s*(?P\d+)?" \ + "(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>" \ + "[\[]\s*(?P.*?)?\s*[\]]<\/span>" + if "Especial" in info: # Capitulos Especiales + pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P\d+).*?\].*?Capitulo.*?\[\s*(?P\d+).*?\]?(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>[\[]\s*(?P.*?)?\s*[\]]<\/span>" + logger.debug("patron: " + pattern) + logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] + if match['season'] is None: match['season'] = season + if match['episode'] is None: match['episode'] = "0" + if match['quality']: item.quality = match['quality'] + if match["episode2"]: multi = True title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), @@ -408,12 +507,17 @@ def episodios(item): match["lang"], match["quality"]) else: # old style - pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+)(?P\d{2})(?:_(?P\d+)" \ + pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+).*?(?P\d{2})(?:_(?P\d+)" \ "(?P\d{2}))?.*?\].*?(?:\[(?P.*?)\])?" - + logger.debug("patron: " + pattern) + logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - # logger.debug("data %s" % match) + #logger.debug("data %s" % match) + + #if match['season'] is "": match['season'] = season + #if match['episode'] is "": match['episode'] = "0" + #logger.debug(match) str_lang = "" if match["lang"] is not None: @@ -436,18 +540,19 @@ def episodios(item): season = match['season'] episode = match['episode'] + logger.debug("title: " + title + " / url: " + url + " / calidad: " + item.quality + " / multi: " + str(multi) + " / Season: " + str(season) + " / EpisodeNumber: " + str(episode)) itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=item.quality, multi=multi, contentSeason=season, contentEpisodeNumber=episode, infoLabels = infoLabels)) - # order list + #tmdb.set_infoLabels(itemlist, True) tmdb.set_infoLabels_itemlist(itemlist, seekTmdb = True) if len(itemlist) > 1: itemlist = sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber))) if config.get_videolibrary_support() and len(itemlist) > 0: itemlist.append( - item.clone(title="[COLOR orange][B]Añadir esta serie a la videoteca[/B][/COLOR]", action="add_serie_to_library", extra="episodios")) + item.clone(title="Añadir esta serie a la videoteca", action="add_serie_to_library", extra="episodios", quality=calidad)) return itemlist @@ -458,7 +563,7 @@ def search(item, texto): try: item.post = "q=%s" % texto item.pattern = "buscar-list" - itemlist = listado2(item) + itemlist = listado_busqueda(item) return itemlist diff --git a/plugin.video.alfa/channels/torrentrapid.json b/plugin.video.alfa/channels/torrentrapid.json index 0362f46d..303149a0 100644 --- a/plugin.video.alfa/channels/torrentrapid.json +++ b/plugin.video.alfa/channels/torrentrapid.json @@ -10,7 +10,8 @@ "movie", "tvshow", "anime", - "torrent" + "torrent", + "documentary" ], "settings": [ { @@ -21,6 +22,22 @@ "enabled": true, "visible": true }, + { + "id": "include_in_newest_peliculas", + "type": "bool", + "label": "Incluir en Novedades - Peliculas", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_series", + "type": "bool", + "label": "Incluir en Novedades - Episodios de series", + "default": true, + "enabled": true, + "visible": true + }, { "id": "include_in_newest_torrent", "type": "bool", @@ -28,6 +45,14 @@ "default": true, "enabled": true, "visible": true + }, + { + "id": "include_in_newest_4k", + "type": "bool", + "label": "Incluir en Novedades - 4K", + "default": true, + "enabled": true, + "visible": true } ] } \ No newline at end of file diff --git a/plugin.video.alfa/channels/torrentrapid.py b/plugin.video.alfa/channels/torrentrapid.py index ae0e174f..fa93fce0 100644 --- a/plugin.video.alfa/channels/torrentrapid.py +++ b/plugin.video.alfa/channels/torrentrapid.py @@ -10,7 +10,7 @@ from core.item import Item from platformcode import config, logger from core import tmdb -host = 'http://torrentrapid.com/' # Cambiar manualmente "xx" en línea 287 ".com/xx/library" por tl para torrentrapid, tr para torrentrapid, d20 para descargas2020 +host = 'http://torrentrapid.com/' def mainlist(item): logger.info() @@ -40,12 +40,17 @@ def submenu(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com #patron = '
  • .*?
      (.*?)
    ' - patron = '
  • .*?
      (.*?)
    ' #Filtrado por url - data = scrapertools.get_match(data, patron) + patron = '
  • <.*?href="'+item.url+item.extra + '/">.*?(.*?)' #Filtrado por url, compatibilidad con mispelisy.series.com + #logger.debug("patron: " + patron + " / data: " + data) + if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com + data = ' Documentales' + else: + data = scrapertools.get_match(data, patron) - patron = '([^>]+)' + patron = '<.*?href="([^"]+)".*?>([^>]+)' matches = re.compile(patron, re.DOTALL).findall(data) for scrapedurl, scrapedtitle in matches: @@ -92,15 +97,15 @@ def listado(item): url_next_page ='' data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) + #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - #logger.debug(data) + logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': logger.debug('item.title: %s'% item.title) patron = '
      (.*?)
    ' - logger.debug("patron=" + patron) fichas = scrapertools.get_match(data, patron) page_extra = item.extra else: @@ -109,11 +114,13 @@ def listado(item): patron = '(.*?)<\/b><\/font>') + real_title = scrapertools.find_single_match(title, r'(.*?)Temporada.*?<\/strong>') #series + if real_title == "": + real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') + real_title = scrapertools.htmlclean(real_title) + calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?]+>[\[]\s*(?P.*?)\s*[\]]<\/span>') #series + if calidad == "": + calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies + year = scrapertools.find_single_match(thumb, r'-(\d{4})') + + # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ") + title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") + title = re.sub(r'(Calidad.*?\])', '', title) + + if real_title == "": + real_title = title + if calidad == "": + calidad = title + context = "movie" # no mostramos lo que no sean videos - if "/juego/" in url or "/varios/" in url: + if "juego/" in url: continue - if ".com/series" in url: + # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie + if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: + calidad_mps = "series/" + if "seriehd" in url: + calidad_mps = "series-hd/" + if "serievo" in url: + calidad_mps = "series-vo/" + if "serie-vo" in url: + calidad_mps = "series-vo/" + + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + + if "/0_" not in thumb: + serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P\d+).*?.*') + if len(serieid) > 5: + serieid = "" + else: + serieid = "" + + url = host + calidad_mps + real_title_mps + "/" + serieid + + real_title_mps = real_title_mps.replace("-", " ") + #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + real_title = real_title_mps + + show = real_title - show = real_title + if ".com/serie" in url and "/miniseries" not in url: + + context = "tvshow" - itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, - context=["buscar_trailer"], contentSerieName=show)) + itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: - itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, - context=["buscar_trailer"])) - + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / show: " + show + " / calidad: " + calidad) + + tmdb.set_infoLabels(itemlist, True) + if post: - itemlist.append(item.clone(channel=item.channel, action="listado2", title=">> Página siguiente", + itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", thumbnail=get_thumb("next.png"))) return itemlist @@ -253,7 +314,6 @@ def listado2(item): def findvideos(item): logger.info() itemlist = [] - ## Cualquiera de las tres opciones son válidas # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") @@ -263,32 +323,36 @@ def findvideos(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("$!", "#!").replace("'", "\"").replace("ñ", "ñ").replace("//pictures", "/pictures") - - title = scrapertools.find_single_match(data, "

    ([^<]+)<\/strong>[^<]+<\/h1>") - title += scrapertools.find_single_match(data, "

    [^<]+<\/strong>([^<]+)<\/h1>") - caratula = scrapertools.find_single_match(data, '
    .*?src="([^"]+)"') - - #
    Descarga tu Archivo torrent!
    + + title = scrapertools.find_single_match(data, "([^<]+)<\/strong>.*?<\/h1>") #corregido para adaptarlo a mispelisy.series.com + title += scrapertools.find_single_match(data, "[^<]+<\/strong>([^<]+)<\/h1>") #corregido para adaptarlo a mispelisy.series.com + #caratula = scrapertools.find_single_match(data, '
    .*?src="([^"]+)"') + caratula = scrapertools.find_single_match(data, ']+>.*?' #patron_ver = '
    ]+>.*?' @@ -309,37 +373,48 @@ def findvideos(item): patron = '
    <\/div[^<]+
    ([^<]+)?<\/div[^<]+
    ([^<]+)?' patron += '<\/div[^<]+
    ([^<]+)?<\/div[^<]+
    0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: servidor = servidor.replace("streamin", "streaminto") - titulo = titulo + " [" + servidor + "]" + titulo = title mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=item.title, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) except: pass + if len(enlaces_descargar) > 0: + itemlist.append(item.clone(title="", action="", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") + titulo = "Partes " p = 1 + logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " [" + servidor + "]" + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title p += 1 mostrar_server = True if config.get_setting("hidepremium"): @@ -349,11 +424,12 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=item.title, url=enlace, thumbnail=logo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) except: pass + return itemlist @@ -363,6 +439,8 @@ def episodios(item): infoLabels = item.infoLabels data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + calidad = item.quality + pattern = '
      (.*?)
    ' % "pagination" # item.pattern pagination = scrapertools.find_single_match(data, pattern) if pagination: @@ -381,22 +459,43 @@ def episodios(item): logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - + data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern data = scrapertools.get_match(data, pattern) + #logger.debug("data: " + data) - pattern = ']*>
    ]+>(?P.*?)

    ' + if "pelisyseries.com" in host: + pattern = ']*>
    ]+>(?P.*?)?<\/h3>.*?<\/li>' + else: + pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) + #logger.debug("patron: " + pattern) + #logger.debug(matches) + + season = "1" for url, thumb, info in matches: + if "pelisyseries.com" in host: + interm = url + url = thumb + thumb = interm + if "\d+)?)<.+?]+>(?P.*?)\s*Calidad\s*]+>" \ - "[\[]\s*(?P.*?)\s*[\]]" + pattern = ".*?[^>]+>.*?Temporada\s*(?P\d+)?.*?Capitulo(?:s)?\s*(?P\d+)?" \ + "(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>" \ + "[\[]\s*(?P.*?)?\s*[\]]<\/span>" + if "Especial" in info: # Capitulos Especiales + pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P\d+).*?\].*?Capitulo.*?\[\s*(?P\d+).*?\]?(?:.*?(?P\d+)?)<.+?]+>(?P.*?)?<\/span>\s*Calidad\s*]+>[\[]\s*(?P.*?)?\s*[\]]<\/span>" + logger.debug("patron: " + pattern) + logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] + if match['season'] is None: match['season'] = season + if match['episode'] is None: match['episode'] = "0" + if match['quality']: item.quality = match['quality'] + if match["episode2"]: multi = True title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), @@ -408,12 +507,17 @@ def episodios(item): match["lang"], match["quality"]) else: # old style - pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+)(?P\d{2})(?:_(?P\d+)" \ + pattern = "\[(?P.*?)\].*?\[Cap.(?P\d+).*?(?P\d{2})(?:_(?P\d+)" \ "(?P\d{2}))?.*?\].*?(?:\[(?P.*?)\])?" - + logger.debug("patron: " + pattern) + logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - # logger.debug("data %s" % match) + #logger.debug("data %s" % match) + + #if match['season'] is "": match['season'] = season + #if match['episode'] is "": match['episode'] = "0" + #logger.debug(match) str_lang = "" if match["lang"] is not None: @@ -436,18 +540,19 @@ def episodios(item): season = match['season'] episode = match['episode'] + logger.debug("title: " + title + " / url: " + url + " / calidad: " + item.quality + " / multi: " + str(multi) + " / Season: " + str(season) + " / EpisodeNumber: " + str(episode)) itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=item.quality, multi=multi, contentSeason=season, contentEpisodeNumber=episode, infoLabels = infoLabels)) - # order list + #tmdb.set_infoLabels(itemlist, True) tmdb.set_infoLabels_itemlist(itemlist, seekTmdb = True) if len(itemlist) > 1: itemlist = sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber))) if config.get_videolibrary_support() and len(itemlist) > 0: itemlist.append( - item.clone(title="[COLOR orange][B]Añadir esta serie a la videoteca[/B][/COLOR]", action="add_serie_to_library", extra="episodios")) + item.clone(title="Añadir esta serie a la videoteca", action="add_serie_to_library", extra="episodios", quality=calidad)) return itemlist @@ -458,7 +563,7 @@ def search(item, texto): try: item.post = "q=%s" % texto item.pattern = "buscar-list" - itemlist = listado2(item) + itemlist = listado_busqueda(item) return itemlist From 3ff15fe156bbc4c660b64738cc10270639c5e4fc Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Wed, 4 Apr 2018 11:28:27 +0200 Subject: [PATCH 04/22] Videolibray: permitir etiquetas-comentarios MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite añadir etiquetas o comentarios separadores en la lista de servidores, separando servidores Torrent de Ver Directo y Descargas. Actualmente llega a ser dificil de leer si hay muchos servidores --- plugin.video.alfa/channels/videolibrary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin.video.alfa/channels/videolibrary.py b/plugin.video.alfa/channels/videolibrary.py index 69b8382f..a23f3c2f 100644 --- a/plugin.video.alfa/channels/videolibrary.py +++ b/plugin.video.alfa/channels/videolibrary.py @@ -398,8 +398,8 @@ def findvideos(item): # Cambiarle el titulo a los servers añadiendoles el nombre del canal delante y # las infoLabels y las imagenes del item si el server no tiene for server in list_servers: - if not server.action: # Ignorar las etiquetas - continue + #if not server.action: # Ignorar/PERMITIR las etiquetas + # continue server.contentChannel = server.channel server.channel = "videolibrary" From beedfb6ab54721cee12980f54c831826e1692cf5 Mon Sep 17 00:00:00 2001 From: danielr460 Date: Wed, 4 Apr 2018 10:00:46 -0500 Subject: [PATCH 05/22] Animemovil: Solucionado renumbertools --- plugin.video.alfa/channels/animemovil.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugin.video.alfa/channels/animemovil.py b/plugin.video.alfa/channels/animemovil.py index 8d84eb5e..f5347e4e 100644 --- a/plugin.video.alfa/channels/animemovil.py +++ b/plugin.video.alfa/channels/animemovil.py @@ -204,14 +204,16 @@ def episodios(item): matches = scrapertools.find_multiple_matches(bloque, '
  • Date: Wed, 4 Apr 2018 10:03:23 -0500 Subject: [PATCH 06/22] SeriesLan: Arreglado renumbertools --- plugin.video.alfa/channels/serieslan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin.video.alfa/channels/serieslan.py b/plugin.video.alfa/channels/serieslan.py index 6a3e3279..fc33b1b8 100644 --- a/plugin.video.alfa/channels/serieslan.py +++ b/plugin.video.alfa/channels/serieslan.py @@ -71,11 +71,11 @@ def lista(item): context2 = autoplay.context context.extend(context2) - itemlist.append(item.clone(title=title, url=url, action="episodios", thumbnail=scrapedthumbnail, show=title, + itemlist.append(item.clone(title=title, url=url, action="episodios", thumbnail=scrapedthumbnail, show=title,contentSerieName=title, context=context)) if b<29: a=a+1 - url="https://serieslan.com/pag-"+str(a) + url=host+"/pag-"+str(a) if b>10: itemlist.append( Item(channel=item.channel, title="[COLOR cyan]Página Siguiente >>[/COLOR]", url=url, action="lista", page=0)) @@ -116,14 +116,14 @@ def episodios(item): for pos in name.split(pat): i = i + 1 total_episode += 1 - season, episode = renumbertools.numbered_for_tratk(item.channel, item.show, 1, total_episode) + season, episode = renumbertools.numbered_for_tratk(item.channel, item.contentSerieName, 1, total_episode) if len(name.split(pat)) == i: title += "%sx%s " % (season, str(episode).zfill(2)) else: title += "%sx%s_" % (season, str(episode).zfill(2)) else: total_episode += 1 - season, episode = renumbertools.numbered_for_tratk(item.channel, item.show, 1, total_episode) + season, episode = renumbertools.numbered_for_tratk(item.channel,item.contentSerieName, 1, total_episode) title += "%sx%s " % (season, str(episode).zfill(2)) From d427949252ce7d0655c45a0f8051809ddab99bc5 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Wed, 4 Apr 2018 18:50:10 +0200 Subject: [PATCH 07/22] Unifu permite los titulos en Series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En accion=Findvideos despreciaba el títulos de episodios de series. Solo daba la calidad, que es muy insuficiente para valorar el video que se va a ver --- plugin.video.alfa/platformcode/unify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin.video.alfa/platformcode/unify.py b/plugin.video.alfa/platformcode/unify.py index ecc63199..f83a4855 100644 --- a/plugin.video.alfa/platformcode/unify.py +++ b/plugin.video.alfa/platformcode/unify.py @@ -412,6 +412,7 @@ def title_format(item): # Compureba si estamos en findvideos, y si hay server, si es asi no se muestra el # titulo sino el server, en caso contrario se muestra el titulo normalmente. + # MODIFICADO: muestra también los títulos para findvideos, sino la información es muy escasa #logger.debug('item.title antes de server: %s'%item.title) if item.action != 'play' and item.server: @@ -420,7 +421,7 @@ def title_format(item): if item.quality == 'default': quality = '' #logger.debug('language_color: %s'%language_color) - item.title = '%s %s' % (server, set_color(quality,'quality')) + item.title = '%s %s' % (server, item.title) #EL TITULO ES NECESARIO porque la calidad sola es insuficiente if lang: item.title = add_languages(item.title, simple_language) #logger.debug('item.title: %s' % item.title) From 74a8bfa5da5dfbab69a850229388ec0b4ed41d72 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Wed, 4 Apr 2018 20:11:25 +0200 Subject: [PATCH 08/22] Torrentrapid, Torrentlocura, Mispelisyseries, Descargas2020: mejoras en pantalla de Servidores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mejora la legibilidad de los títulos en la pantalla de Servidores. Unido a mejoras en Unify y Videolibrary --- plugin.video.alfa/channels/descargas2020.py | 50 ++++++++++++++---- plugin.video.alfa/channels/mispelisyseries.py | 50 ++++++++++++++---- plugin.video.alfa/channels/torrentlocura.py | 50 ++++++++++++++---- plugin.video.alfa/channels/torrentrapid.py | 52 ++++++++++++++----- 4 files changed, 157 insertions(+), 45 deletions(-) diff --git a/plugin.video.alfa/channels/descargas2020.py b/plugin.video.alfa/channels/descargas2020.py index e6902ffd..7266760b 100644 --- a/plugin.video.alfa/channels/descargas2020.py +++ b/plugin.video.alfa/channels/descargas2020.py @@ -334,15 +334,27 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año + + if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + year = '[%s]' % str(item.infoLabels['year']) + else: + year = "" + + if item.contentType == "episode": + item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título + if "Temp" in title and item.quality != "": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = str(item.contentSeason) + "x" + str(item.contentEpisodeNumber) + " - " + item.contentTitle + ", " + title + title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + + itemlist.append(item.clone(title=title, action="", folder=False)) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, quality=title, fulltitle=title, - url=url, thumbnail=caratula, plot=item.plot, folder=False)) + Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) @@ -380,8 +392,7 @@ def findvideos(item): #logger.debug(enlaces_ver) if len(enlaces_ver) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -397,14 +408,13 @@ def findvideos(item): if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: @@ -424,9 +434,9 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, - plot=item.plot, folder=False)) + plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -590,6 +600,24 @@ def newest(categoria): itemlist.extend(listado(item)) if itemlist[-1].title == ">> Página siguiente": itemlist.pop() + + if categoria == 'peliculas 4k': + item.url = host+'peliculas-hd/4kultrahd/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'anime': + item.url = host+'anime/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'documentales': + item.url = host+'documentales/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() # Se captura la excepción, para no interrumpir al canal novedades si un canal falla except: diff --git a/plugin.video.alfa/channels/mispelisyseries.py b/plugin.video.alfa/channels/mispelisyseries.py index 0e4d44cd..14e37ff8 100644 --- a/plugin.video.alfa/channels/mispelisyseries.py +++ b/plugin.video.alfa/channels/mispelisyseries.py @@ -334,15 +334,27 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año + + if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + year = '[%s]' % str(item.infoLabels['year']) + else: + year = "" + + if item.contentType == "episode": + item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título + if "Temp" in title and item.quality != "": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = str(item.contentSeason) + "x" + str(item.contentEpisodeNumber) + " - " + item.contentTitle + ", " + title + title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + + itemlist.append(item.clone(title=title, action="", folder=False)) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, quality=title, fulltitle=title, - url=url, thumbnail=caratula, plot=item.plot, folder=False)) + Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) @@ -380,8 +392,7 @@ def findvideos(item): #logger.debug(enlaces_ver) if len(enlaces_ver) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -397,14 +408,13 @@ def findvideos(item): if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: @@ -424,9 +434,9 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, - plot=item.plot, folder=False)) + plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -590,6 +600,24 @@ def newest(categoria): itemlist.extend(listado(item)) if itemlist[-1].title == ">> Página siguiente": itemlist.pop() + + if categoria == 'peliculas 4k': + item.url = host+'peliculas-hd/4kultrahd/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'anime': + item.url = host+'anime/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'documentales': + item.url = host+'documentales/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() # Se captura la excepción, para no interrumpir al canal novedades si un canal falla except: diff --git a/plugin.video.alfa/channels/torrentlocura.py b/plugin.video.alfa/channels/torrentlocura.py index 6dce5ad7..4b89efc7 100755 --- a/plugin.video.alfa/channels/torrentlocura.py +++ b/plugin.video.alfa/channels/torrentlocura.py @@ -334,15 +334,27 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año + + if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + year = '[%s]' % str(item.infoLabels['year']) + else: + year = "" + + if item.contentType == "episode": + item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título + if "Temp" in title and item.quality != "": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = str(item.contentSeason) + "x" + str(item.contentEpisodeNumber) + " - " + item.contentTitle + ", " + title + title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + + itemlist.append(item.clone(title=title, action="", folder=False)) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, quality=title, fulltitle=title, - url=url, thumbnail=caratula, plot=item.plot, folder=False)) + Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) @@ -380,8 +392,7 @@ def findvideos(item): #logger.debug(enlaces_ver) if len(enlaces_ver) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -397,14 +408,13 @@ def findvideos(item): if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: @@ -424,9 +434,9 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, - plot=item.plot, folder=False)) + plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -590,6 +600,24 @@ def newest(categoria): itemlist.extend(listado(item)) if itemlist[-1].title == ">> Página siguiente": itemlist.pop() + + if categoria == 'peliculas 4k': + item.url = host+'peliculas-hd/4kultrahd/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'anime': + item.url = host+'anime/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'documentales': + item.url = host+'documentales/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() # Se captura la excepción, para no interrumpir al canal novedades si un canal falla except: diff --git a/plugin.video.alfa/channels/torrentrapid.py b/plugin.video.alfa/channels/torrentrapid.py index fa93fce0..830d9b2a 100644 --- a/plugin.video.alfa/channels/torrentrapid.py +++ b/plugin.video.alfa/channels/torrentrapid.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re @@ -334,15 +334,27 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año + + if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + year = '[%s]' % str(item.infoLabels['year']) + else: + year = "" + + if item.contentType == "episode": + item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título + if "Temp" in title and item.quality != "": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = str(item.contentSeason) + "x" + str(item.contentEpisodeNumber) + " - " + item.contentTitle + ", " + title + title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + + itemlist.append(item.clone(title=title, action="", folder=False)) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, quality=title, fulltitle=title, - url=url, thumbnail=caratula, plot=item.plot, folder=False)) + Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) @@ -380,8 +392,7 @@ def findvideos(item): #logger.debug(enlaces_ver) if len(enlaces_ver) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -397,14 +408,13 @@ def findvideos(item): if devuelve: enlace = devuelve[0][1] itemlist.append( - Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, quality=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, folder=False)) + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, + fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title="", action="", folder=False)) - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", text_color="green", folder=False)) + itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: @@ -424,9 +434,9 @@ def findvideos(item): devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] - itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, quality=parte_titulo, + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, - plot=item.plot, folder=False)) + plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -590,6 +600,24 @@ def newest(categoria): itemlist.extend(listado(item)) if itemlist[-1].title == ">> Página siguiente": itemlist.pop() + + if categoria == 'peliculas 4k': + item.url = host+'peliculas-hd/4kultrahd/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'anime': + item.url = host+'anime/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'documentales': + item.url = host+'documentales/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() # Se captura la excepción, para no interrumpir al canal novedades si un canal falla except: From 535c0ff4a7eb194f1ef805eaa5b6ca5454969c26 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Thu, 5 Apr 2018 09:15:42 +0200 Subject: [PATCH 09/22] =?UTF-8?q?Clonaci=C3=B3n=20de=20Mispeliculasyseries?= =?UTF-8?q?,=20descargas2020,=20torrentrapid=20y=20torrentlocura=20sobre?= =?UTF-8?q?=20c=C3=B3digo=20mejorado=20de=20newpct1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mejoras en la legibilidad de títulos en pantalla de servidores --- plugin.video.alfa/channels/descargas2020.py | 16 +++++++--------- plugin.video.alfa/channels/mispelisyseries.py | 16 +++++++--------- plugin.video.alfa/channels/torrentlocura.py | 16 +++++++--------- plugin.video.alfa/channels/torrentrapid.py | 18 ++++++++---------- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/plugin.video.alfa/channels/descargas2020.py b/plugin.video.alfa/channels/descargas2020.py index 7266760b..904ce44e 100644 --- a/plugin.video.alfa/channels/descargas2020.py +++ b/plugin.video.alfa/channels/descargas2020.py @@ -334,23 +334,21 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify year = '[%s]' % str(item.infoLabels['year']) else: year = "" - - if item.contentType == "episode": - item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título - if "Temp" in title and item.quality != "": #scrapear información duplicada en Series + if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) - - itemlist.append(item.clone(title=title, action="", folder=False)) + title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo + + if item.contentType != "episode": + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis + if url != "": #Torrent itemlist.append( Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, diff --git a/plugin.video.alfa/channels/mispelisyseries.py b/plugin.video.alfa/channels/mispelisyseries.py index 14e37ff8..0613dd36 100644 --- a/plugin.video.alfa/channels/mispelisyseries.py +++ b/plugin.video.alfa/channels/mispelisyseries.py @@ -334,23 +334,21 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify year = '[%s]' % str(item.infoLabels['year']) else: year = "" - - if item.contentType == "episode": - item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título - if "Temp" in title and item.quality != "": #scrapear información duplicada en Series + if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) - - itemlist.append(item.clone(title=title, action="", folder=False)) + title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo + + if item.contentType != "episode": + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis + if url != "": #Torrent itemlist.append( Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, diff --git a/plugin.video.alfa/channels/torrentlocura.py b/plugin.video.alfa/channels/torrentlocura.py index 4b89efc7..adc94465 100755 --- a/plugin.video.alfa/channels/torrentlocura.py +++ b/plugin.video.alfa/channels/torrentlocura.py @@ -334,23 +334,21 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify year = '[%s]' % str(item.infoLabels['year']) else: year = "" - - if item.contentType == "episode": - item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título - if "Temp" in title and item.quality != "": #scrapear información duplicada en Series + if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) - - itemlist.append(item.clone(title=title, action="", folder=False)) + title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo + + if item.contentType != "episode": + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis + if url != "": #Torrent itemlist.append( Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, diff --git a/plugin.video.alfa/channels/torrentrapid.py b/plugin.video.alfa/channels/torrentrapid.py index 830d9b2a..2ef3d953 100644 --- a/plugin.video.alfa/channels/torrentrapid.py +++ b/plugin.video.alfa/channels/torrentrapid.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re @@ -334,23 +334,21 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad y año - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify year = '[%s]' % str(item.infoLabels['year']) else: year = "" - - if item.contentType == "episode": - item.contentType = "tvshow" #forzar contenido a "tvshow" para que Unify no destroce el título - if "Temp" in title and item.quality != "": #scrapear información duplicada en Series + if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s, %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) - - itemlist.append(item.clone(title=title, action="", folder=False)) + title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo + + if item.contentType != "episode": + title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis + if url != "": #Torrent itemlist.append( Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, From 0a121b7f73235fcd8cf5b7862da4048ef3949e24 Mon Sep 17 00:00:00 2001 From: Intel1 <25161862+Intel11@users.noreply.github.com> Date: Thu, 5 Apr 2018 14:52:25 -0500 Subject: [PATCH 10/22] kbagi: fix --- plugin.video.alfa/servers/kbagi.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugin.video.alfa/servers/kbagi.py b/plugin.video.alfa/servers/kbagi.py index b74162d1..4467e870 100644 --- a/plugin.video.alfa/servers/kbagi.py +++ b/plugin.video.alfa/servers/kbagi.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from channels import kbagi from core import httptools from core import jsontools from core import scrapertools @@ -8,15 +9,16 @@ from platformcode import logger def test_video_exists(page_url): logger.info("(page_url='%s')" % page_url) + domain = "diskokosmiko.mx" if "kbagi.com" in page_url: - from channels import kbagi - logueado, error_message = kbagi.login("kbagi.com") - if not logueado: - return False, error_message + domain = "kbagi.com" + logueado, error_message = kbagi.login(domain) + if not logueado: + return False, error_message data = httptools.downloadpage(page_url).data if ("File was deleted" or "Not Found" or "File was locked by administrator") in data: - return False, "[kbagi] El archivo no existe o ha sido borrado" + return False, "[%s] El archivo no existe o ha sido borrado" %domain return True, "" From c71b7a6c27c3bbc71ee6bffce05103eb1b3a709d Mon Sep 17 00:00:00 2001 From: Intel1 <25161862+Intel11@users.noreply.github.com> Date: Thu, 5 Apr 2018 14:53:22 -0500 Subject: [PATCH 11/22] httptools: update --- plugin.video.alfa/core/httptools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.video.alfa/core/httptools.py b/plugin.video.alfa/core/httptools.py index d1a553e2..ffda92c4 100755 --- a/plugin.video.alfa/core/httptools.py +++ b/plugin.video.alfa/core/httptools.py @@ -25,7 +25,7 @@ ficherocookies = os.path.join(config.get_data_path(), "cookies.dat") # Headers por defecto, si no se especifica nada default_headers = dict() -default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3163.100 Safari/537.36" +default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3163.100 Safari/537.36" default_headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" default_headers["Accept-Language"] = "es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3" default_headers["Accept-Charset"] = "UTF-8" From 0654a1eabd4fa858bd05b3ebd8029e24bf43624c Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 5 Apr 2018 16:59:31 -0300 Subject: [PATCH 12/22] Correcciones --- plugin.video.alfa/channels/pelisplus.py | 4 +-- plugin.video.alfa/channels/seriespapaya.py | 23 ++++++++++++++--- plugin.video.alfa/channels/setting.py | 29 ++++++++++++---------- plugin.video.alfa/core/trakt_tools.py | 11 ++++++++ plugin.video.alfa/platformcode/launcher.py | 5 ++++ plugin.video.alfa/servers/openload.py | 3 +-- 6 files changed, 55 insertions(+), 20 deletions(-) diff --git a/plugin.video.alfa/channels/pelisplus.py b/plugin.video.alfa/channels/pelisplus.py index 48c408c4..4a301af2 100644 --- a/plugin.video.alfa/channels/pelisplus.py +++ b/plugin.video.alfa/channels/pelisplus.py @@ -286,7 +286,7 @@ def newest(categoria): item = Item() try: if categoria in ['peliculas','latino']: - item.url = host + 'peliculas/ultimas-agregadas/' + item.url = host + 'peliculas/ultimas-peliculas/' elif categoria == 'infantiles': item.url = host + 'peliculas/animacion/' @@ -297,7 +297,7 @@ def newest(categoria): elif categoria == 'documentales': item.url = host + 'documentales/' - itemlist = lista(item) + itemlist = list_all(item) if itemlist[-1].title == 'Siguiente >>>': itemlist.pop() except: diff --git a/plugin.video.alfa/channels/seriespapaya.py b/plugin.video.alfa/channels/seriespapaya.py index 522180e4..13d02b25 100644 --- a/plugin.video.alfa/channels/seriespapaya.py +++ b/plugin.video.alfa/channels/seriespapaya.py @@ -12,6 +12,7 @@ from core import jsontools from core import scrapertools from core import servertools from core import tmdb +from channels import autoplay from core.item import Item from platformcode import config, logger @@ -20,12 +21,16 @@ HOST = "http://www.seriespapaya.com" IDIOMAS = {'es': 'Español', 'lat': 'Latino', 'in': 'Inglés', 'ca': 'Catalán', 'sub': 'VOSE', 'Español Latino':'lat', 'Español Castellano':'es', 'Sub Español':'VOSE'} list_idiomas = IDIOMAS.values() -CALIDADES = ['360p', '480p', '720p HD', '1080p HD'] +CALIDADES = ['360p', '480p', '720p HD', '1080p HD', 'default'] +list_servers = ['powvideo', 'streamplay', 'filebebo', 'flashx', 'gamovideo', 'nowvideo', 'openload', 'streamango', + 'streamcloud', 'vidzi', 'clipwatching', ] def mainlist(item): logger.info() + autoplay.init(item.channel, list_servers, CALIDADES) + thumb_series = get_thumb("channels_tvshow.png") thumb_series_az = get_thumb("channels_tvshow_az.png") thumb_buscar = get_thumb("search.png") @@ -39,6 +44,8 @@ def mainlist(item): itemlist = filtertools.show_option(itemlist, item.channel, list_idiomas, CALIDADES) + autoplay.show_option(item.channel, itemlist) + return itemlist @@ -205,13 +212,23 @@ def findvideos(item): server=server.rstrip(), quality=quality, uploader=uploader), + server=server.rstrip(), url=urlparse.urljoin(HOST, url), - language=IDIOMAS.get(lang, lang), - quality=quality, + language=IDIOMAS.get(lang,lang), + quality=quality ) for lang, date, server, url, linkType, quality, uploader in links] + + + + # Requerido para FilterTools + itemlist = filtertools.get_links(itemlist, item, list_idiomas, CALIDADES) + # Requerido para AutoPlay + + autoplay.start(itemlist, item) + return itemlist diff --git a/plugin.video.alfa/channels/setting.py b/plugin.video.alfa/channels/setting.py index 24a563ca..aa7a274f 100644 --- a/plugin.video.alfa/channels/setting.py +++ b/plugin.video.alfa/channels/setting.py @@ -170,25 +170,26 @@ def servers_blacklist(item): server_list = servertools.get_servers_list() dict_values = {} - list_controls = [{'id': 'filter_servers', - 'type': "bool", - 'label': "@30068", - 'default': False, - 'enabled': True, - 'visible': True}] + list_controls = [{"id": "filter_servers", + "type": "bool", + "label": "@30068", + "default": False, + "enabled": True, + "visible": True}] dict_values['filter_servers'] = config.get_setting('filter_servers') - + if dict_values['filter_servers'] == None: + dict_values['filter_servers'] = False for i, server in enumerate(sorted(server_list.keys())): server_parameters = server_list[server] controls, defaults = servertools.get_server_controls_settings(server) dict_values[server] = config.get_setting("black_list", server=server) - control = {'id': server, - 'type': "bool", - 'label': ' %s' % server_parameters["name"], - 'default': defaults.get("black_list", False), - 'enabled': "eq(-%s,True)" % (i + 1), - 'visible': True} + control = {"id": server, + "type": "bool", + "label": ' %s' % server_parameters["name"], + "default": defaults.get("black_list", False), + "enabled": "eq(-%s,True)" % (i + 1), + "visible": True} list_controls.append(control) return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, @@ -228,6 +229,8 @@ def servers_favorites(item): 'enabled': True, 'visible': True}] dict_values['favorites_servers'] = config.get_setting('favorites_servers') + if dict_values['favorites_servers'] == None: + dict_values['favorites_servers'] = False server_names = ['Ninguno'] diff --git a/plugin.video.alfa/core/trakt_tools.py b/plugin.video.alfa/core/trakt_tools.py index d9adf674..914de6dc 100644 --- a/plugin.video.alfa/core/trakt_tools.py +++ b/plugin.video.alfa/core/trakt_tools.py @@ -129,6 +129,17 @@ def token_trakt(item): return itemlist +def set_trakt_info(item): + logger.info() + import xbmcgui + # Envia los datos a trakt + try: + info = item.infoLabels + ids = jsontools.dump({'tmdb': info['tmdb_id'] , 'imdb': info['imdb_id'], 'slug': info['title']}) + xbmcgui.Window(10000).setProperty('script.trakt.ids', ids) + except: + pass + def get_trakt_watched(id_type, mediatype, update=False): logger.info() diff --git a/plugin.video.alfa/platformcode/launcher.py b/plugin.video.alfa/platformcode/launcher.py index e9cc9317..c23f0ffe 100644 --- a/plugin.video.alfa/platformcode/launcher.py +++ b/plugin.video.alfa/platformcode/launcher.py @@ -136,6 +136,11 @@ def run(item=None): # Special play action if item.action == "play": + #define la info para trakt + try: + trakt_tools.set_trakt_info(item) + except: + pass logger.info("item.action=%s" % item.action.upper()) # logger.debug("item_toPlay: " + "\n" + item.tostring('\n')) diff --git a/plugin.video.alfa/servers/openload.py b/plugin.video.alfa/servers/openload.py index 73f49c79..81fd95c2 100644 --- a/plugin.video.alfa/servers/openload.py +++ b/plugin.video.alfa/servers/openload.py @@ -31,11 +31,10 @@ def get_video_url(page_url, premium=False, user="", password="", video_password= data = httptools.downloadpage(page_url, cookies=False, headers=header).data - subtitle = scrapertools.find_single_match(data, ']+id="[^"]+">([^<]{40,})' ) + code = scrapertools.find_single_match(data, '

    (.*?)

    ' ) _0x59ce16 = eval(scrapertools.find_single_match(data, '_0x59ce16=([^;]+)').replace('parseInt', 'int')) _1x4bfb36 = eval(scrapertools.find_single_match(data, '_1x4bfb36=([^;]+)').replace('parseInt', 'int')) parseInt = eval(scrapertools.find_single_match(data, '_0x30725e,(\(parseInt.*?)\),').replace('parseInt', 'int')) From 745f5c7a242f793eb60a013e8574a6c8dc670371 Mon Sep 17 00:00:00 2001 From: Alfa <30527549+alfa-addon@users.noreply.github.com> Date: Thu, 5 Apr 2018 15:54:08 -0500 Subject: [PATCH 13/22] Update unify.py --- plugin.video.alfa/platformcode/unify.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin.video.alfa/platformcode/unify.py b/plugin.video.alfa/platformcode/unify.py index f83a4855..ecc63199 100644 --- a/plugin.video.alfa/platformcode/unify.py +++ b/plugin.video.alfa/platformcode/unify.py @@ -412,7 +412,6 @@ def title_format(item): # Compureba si estamos en findvideos, y si hay server, si es asi no se muestra el # titulo sino el server, en caso contrario se muestra el titulo normalmente. - # MODIFICADO: muestra también los títulos para findvideos, sino la información es muy escasa #logger.debug('item.title antes de server: %s'%item.title) if item.action != 'play' and item.server: @@ -421,7 +420,7 @@ def title_format(item): if item.quality == 'default': quality = '' #logger.debug('language_color: %s'%language_color) - item.title = '%s %s' % (server, item.title) #EL TITULO ES NECESARIO porque la calidad sola es insuficiente + item.title = '%s %s' % (server, set_color(quality,'quality')) if lang: item.title = add_languages(item.title, simple_language) #logger.debug('item.title: %s' % item.title) From 58d8709534afc3a74acfb639b88fa054e0fee219 Mon Sep 17 00:00:00 2001 From: Alfa <30527549+alfa-addon@users.noreply.github.com> Date: Thu, 5 Apr 2018 16:05:18 -0500 Subject: [PATCH 14/22] v2.5.7 --- plugin.video.alfa/addon.xml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugin.video.alfa/addon.xml b/plugin.video.alfa/addon.xml index 8510f408..5bd05fdc 100755 --- a/plugin.video.alfa/addon.xml +++ b/plugin.video.alfa/addon.xml @@ -1,5 +1,5 @@ - + @@ -19,12 +19,16 @@ [B]Estos son los cambios para esta versión:[/B] [COLOR green][B]Canales agregados y arreglos[/B][/COLOR] - » cinemahd » seriesdanko - » doramasmp4 » pelisplus - » descargas2020 + » diskokosmiko » gamovideo + » mispelisyseries » pelisplus + » seriespapaya » descargas2020 + » openload » torrentlocura + » torrentrapid » streamcloud + » danimados » animemovil + » serieslan ¤ arreglos internos - ¤ Gracias a la colaboración de @t1254362 en esta versión + ¤ Gracias a la colaboración de @pipcat y @lopezvg en ésta versión Navega con Kodi por páginas web para ver sus videos de manera fácil. Browse web pages using Kodi From cf5ffb42944e01794592f9f9833c37adc9439f71 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Sat, 7 Apr 2018 20:40:50 +0200 Subject: [PATCH 15/22] Ajustes de titulos en Torrentrapid, Torrentlocura, Mispelisyseries y Descargas2020 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alinea el formato de los títulos entre los diferentes contextos de Kodi y Alfa: títulos inteligentes sí/no, pantalla de servidores desde findvideo completa/pop-up, selección desde menú/buscar. Mejora en la recuperación de episodios en Mispelisyseries --- plugin.video.alfa/channels/descargas2020.py | 141 +++++++++++------ plugin.video.alfa/channels/mispelisyseries.py | 143 ++++++++++++------ plugin.video.alfa/channels/torrentlocura.py | 141 +++++++++++------ plugin.video.alfa/channels/torrentrapid.py | 141 +++++++++++------ 4 files changed, 377 insertions(+), 189 deletions(-) diff --git a/plugin.video.alfa/channels/descargas2020.py b/plugin.video.alfa/channels/descargas2020.py index 904ce44e..7947d4c7 100644 --- a/plugin.video.alfa/channels/descargas2020.py +++ b/plugin.video.alfa/channels/descargas2020.py @@ -99,7 +99,6 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) @@ -167,8 +166,9 @@ def listado(item): title = title_alt context_title = title_alt show = title_alt - #if item.extra != "buscar-list": - # title = title + '[' + calidad + "]" + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + ' [' + calidad + "]" #Este bucle parece obsoleto: #context = "" @@ -257,6 +257,7 @@ def listado_busqueda(item): if calidad == "": calidad = title context = "movie" + url_real = True # no mostramos lo que no sean videos if "juego/" in url: @@ -281,22 +282,46 @@ def listado_busqueda(item): serieid = "" else: serieid = "" - - url = host + calidad_mps + real_title_mps + "/" + serieid + + #detectar si la url creada de tvshow es válida o hay que volver atras + url_tvshow = host + calidad_mps + real_title_mps + "/" + url_id = host + calidad_mps + real_title_mps + "/" + serieid + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_id).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern + if not scrapertools.find_single_match(data_serie, pattern): + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_tvshow).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + if not scrapertools.find_single_match(data_serie, pattern): + context = "movie" + url_real = False + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + else: + url = url_tvshow + else: + url = url_id real_title_mps = real_title_mps.replace("-", " ") - #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps + " / context : " + context) real_title = real_title_mps show = real_title - if ".com/serie" in url and "/miniseries" not in url: - + if ".com/serie" in url and "/miniseries" not in url and url_real: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" context = "tvshow" itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: + if config.get_setting("unify"): #Si Titulos Inteligentes SI seleccionados: + title = real_title itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) @@ -307,7 +332,7 @@ def listado_busqueda(item): if post: itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", - thumbnail=get_thumb("next.png"))) + text_color='yellow', text_bold=True, thumbnail=get_thumb("next.png"))) return itemlist @@ -318,6 +343,8 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") + #logger.debug("item: ") + #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) @@ -334,27 +361,46 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + if item.infoLabels['year']: #añadir el año al título general year = '[%s]' % str(item.infoLabels['year']) else: year = "" + if item.infoLabels['aired'] and item.contentType == "episode": #añadir el año de episodio para series + year = scrapertools.find_single_match(str(item.infoLabels['aired']), r'\/(\d{4})') + year = '[%s]' % year + + title_gen = title if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + title_epi = '%sx%s - %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle) + title_gen = '%s %s, %s' % (title_epi, year, title) + title_torrent = '%s, %s' % (title_epi, item.contentSerieName) + else: + title_torrent = item.contentTitle + + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_torrent = '%s [%s]' %(title_torrent, item.infoLabels['quality']) + else: + title_torrent = '%s (%s)' %(title_torrent, item.infoLabels['quality']) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_gen = '[COLOR gold]**- Título: [/COLOR]%s -**' % (title_gen) + else: + title_gen = '[COLOR gold]Título: [/COLOR]%s' % (title_gen) + if config.get_setting("quit_channel_name", "videolibrary") == 1 and item.contentChannel == "videolibrary": + title_gen = '%s: %s' % (item.channel.capitalize(), title_gen) + itemlist.append(item.clone(title=title_gen, action="", folder=False)) #Título con todos los datos del vídeo - itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo - - if item.contentType != "episode": - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis - + title = title_torrent + title_torrent = '[COLOR yellow][Torrent]- [/COLOR]%s [online]' % (title_torrent) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + Item(channel=item.channel, action="play", server="torrent", title=title_torrent, fulltitle=title, url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) - logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) + logger.debug("TORRENT: url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) # escraped ver vídeos, descargar vídeos un link, múltiples liks @@ -362,23 +408,6 @@ def findvideos(item): data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?descargas2020.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) #logger.debug("matar %s" % data) - # Antiguo sistema de scrapeo de servidores usado por Newpct1. Como no funciona con torrent.locura, se sustituye por este más común - #patron_descargar = '
    ]+>.*?' - #patron_ver = '
    ]+>.*?' - - #match_ver = scrapertools.find_single_match(data, patron_ver) - #match_descargar = scrapertools.find_single_match(data, patron_descargar) - - #patron = '
    0: - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Ver: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Ver: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -399,7 +431,9 @@ def findvideos(item): mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + titulo = '[COLOR yellow][%s]-[/COLOR] %s [online]' % (servidor.capitalize(), titulo) + logger.debug("VER: url: " + enlace + " / title: " + titulo + " / servidor: " + servidor + " / idioma: " + idioma) + if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) @@ -407,33 +441,42 @@ def findvideos(item): enlace = devuelve[0][1] itemlist.append( Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Descargar: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Descargar: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") - titulo = "Partes " + titulo = "Descarga " p = 1 - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + logger.debug("DESCARGAR: url: " + enlace + " / title: " + titulo + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) p += 1 mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + parte_titulo = '[COLOR yellow][%s]-[/COLOR] %s' % (servidor.capitalize(), parte_titulo) + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + parte_titulo = '%s [%s]' %(parte_titulo, item.infoLabels['quality']) + else: + parte_titulo = '%s (%s)' %(parte_titulo, item.infoLabels['quality']) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, + title=parte_titulo, fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -469,16 +512,20 @@ def episodios(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern - data = scrapertools.get_match(data, pattern) - #logger.debug("data: " + data) + if scrapertools.find_single_match(data, pattern): + data = scrapertools.get_match(data, pattern) + else: + logger.debug(item) + logger.debug("data: " + data) + return itemlist if "pelisyseries.com" in host: pattern = ']*>
    ]+>(?P.*?)?<\/h3>.*?<\/li>' else: pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) + logger.debug("patron: " + pattern) + logger.debug(matches) season = "1" diff --git a/plugin.video.alfa/channels/mispelisyseries.py b/plugin.video.alfa/channels/mispelisyseries.py index 0613dd36..fc8f4bda 100644 --- a/plugin.video.alfa/channels/mispelisyseries.py +++ b/plugin.video.alfa/channels/mispelisyseries.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re @@ -99,7 +99,6 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) @@ -167,8 +166,9 @@ def listado(item): title = title_alt context_title = title_alt show = title_alt - #if item.extra != "buscar-list": - # title = title + '[' + calidad + "]" + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + ' [' + calidad + "]" #Este bucle parece obsoleto: #context = "" @@ -257,6 +257,7 @@ def listado_busqueda(item): if calidad == "": calidad = title context = "movie" + url_real = True # no mostramos lo que no sean videos if "juego/" in url: @@ -281,22 +282,46 @@ def listado_busqueda(item): serieid = "" else: serieid = "" - - url = host + calidad_mps + real_title_mps + "/" + serieid + + #detectar si la url creada de tvshow es válida o hay que volver atras + url_tvshow = host + calidad_mps + real_title_mps + "/" + url_id = host + calidad_mps + real_title_mps + "/" + serieid + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_id).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern + if not scrapertools.find_single_match(data_serie, pattern): + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_tvshow).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + if not scrapertools.find_single_match(data_serie, pattern): + context = "movie" + url_real = False + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + else: + url = url_tvshow + else: + url = url_id real_title_mps = real_title_mps.replace("-", " ") - #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps + " / context : " + context) real_title = real_title_mps show = real_title - if ".com/serie" in url and "/miniseries" not in url: - + if ".com/serie" in url and "/miniseries" not in url and url_real: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" context = "tvshow" itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: + if config.get_setting("unify"): #Si Titulos Inteligentes SI seleccionados: + title = real_title itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) @@ -307,7 +332,7 @@ def listado_busqueda(item): if post: itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", - thumbnail=get_thumb("next.png"))) + text_color='yellow', text_bold=True, thumbnail=get_thumb("next.png"))) return itemlist @@ -318,6 +343,8 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") + #logger.debug("item: ") + #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) @@ -334,27 +361,46 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + if item.infoLabels['year']: #añadir el año al título general year = '[%s]' % str(item.infoLabels['year']) else: year = "" + if item.infoLabels['aired'] and item.contentType == "episode": #añadir el año de episodio para series + year = scrapertools.find_single_match(str(item.infoLabels['aired']), r'\/(\d{4})') + year = '[%s]' % year + + title_gen = title if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + title_epi = '%sx%s - %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle) + title_gen = '%s %s, %s' % (title_epi, year, title) + title_torrent = '%s, %s' % (title_epi, item.contentSerieName) + else: + title_torrent = item.contentTitle + + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_torrent = '%s [%s]' %(title_torrent, item.infoLabels['quality']) + else: + title_torrent = '%s (%s)' %(title_torrent, item.infoLabels['quality']) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_gen = '[COLOR gold]**- Título: [/COLOR]%s -**' % (title_gen) + else: + title_gen = '[COLOR gold]Título: [/COLOR]%s' % (title_gen) + if config.get_setting("quit_channel_name", "videolibrary") == 1 and item.contentChannel == "videolibrary": + title_gen = '%s: %s' % (item.channel.capitalize(), title_gen) + itemlist.append(item.clone(title=title_gen, action="", folder=False)) #Título con todos los datos del vídeo - itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo - - if item.contentType != "episode": - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis - + title = title_torrent + title_torrent = '[COLOR yellow][Torrent]- [/COLOR]%s [online]' % (title_torrent) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + Item(channel=item.channel, action="play", server="torrent", title=title_torrent, fulltitle=title, url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) - logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) + logger.debug("TORRENT: url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) # escraped ver vídeos, descargar vídeos un link, múltiples liks @@ -362,23 +408,6 @@ def findvideos(item): data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?mispelisyseries.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) #logger.debug("matar %s" % data) - # Antiguo sistema de scrapeo de servidores usado por Newpct1. Como no funciona con torrent.locura, se sustituye por este más común - #patron_descargar = '
    ]+>.*?' - #patron_ver = '
    ]+>.*?' - - #match_ver = scrapertools.find_single_match(data, patron_ver) - #match_descargar = scrapertools.find_single_match(data, patron_descargar) - - #patron = '
    0: - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Ver: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Ver: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -399,7 +431,9 @@ def findvideos(item): mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + titulo = '[COLOR yellow][%s]-[/COLOR] %s [online]' % (servidor.capitalize(), titulo) + logger.debug("VER: url: " + enlace + " / title: " + titulo + " / servidor: " + servidor + " / idioma: " + idioma) + if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) @@ -407,33 +441,42 @@ def findvideos(item): enlace = devuelve[0][1] itemlist.append( Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Descargar: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Descargar: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") - titulo = "Partes " + titulo = "Descarga " p = 1 - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + logger.debug("DESCARGAR: url: " + enlace + " / title: " + titulo + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) p += 1 mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + parte_titulo = '[COLOR yellow][%s]-[/COLOR] %s' % (servidor.capitalize(), parte_titulo) + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + parte_titulo = '%s [%s]' %(parte_titulo, item.infoLabels['quality']) + else: + parte_titulo = '%s (%s)' %(parte_titulo, item.infoLabels['quality']) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, + title=parte_titulo, fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -469,16 +512,20 @@ def episodios(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern - data = scrapertools.get_match(data, pattern) - #logger.debug("data: " + data) + if scrapertools.find_single_match(data, pattern): + data = scrapertools.get_match(data, pattern) + else: + logger.debug(item) + logger.debug("data: " + data) + return itemlist if "pelisyseries.com" in host: pattern = ']*>
    ]+>(?P.*?)?<\/h3>.*?<\/li>' else: pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) + logger.debug("patron: " + pattern) + logger.debug(matches) season = "1" diff --git a/plugin.video.alfa/channels/torrentlocura.py b/plugin.video.alfa/channels/torrentlocura.py index adc94465..e6b2d413 100755 --- a/plugin.video.alfa/channels/torrentlocura.py +++ b/plugin.video.alfa/channels/torrentlocura.py @@ -99,7 +99,6 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) @@ -167,8 +166,9 @@ def listado(item): title = title_alt context_title = title_alt show = title_alt - #if item.extra != "buscar-list": - # title = title + '[' + calidad + "]" + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + ' [' + calidad + "]" #Este bucle parece obsoleto: #context = "" @@ -257,6 +257,7 @@ def listado_busqueda(item): if calidad == "": calidad = title context = "movie" + url_real = True # no mostramos lo que no sean videos if "juego/" in url: @@ -281,22 +282,46 @@ def listado_busqueda(item): serieid = "" else: serieid = "" - - url = host + calidad_mps + real_title_mps + "/" + serieid + + #detectar si la url creada de tvshow es válida o hay que volver atras + url_tvshow = host + calidad_mps + real_title_mps + "/" + url_id = host + calidad_mps + real_title_mps + "/" + serieid + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_id).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern + if not scrapertools.find_single_match(data_serie, pattern): + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_tvshow).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + if not scrapertools.find_single_match(data_serie, pattern): + context = "movie" + url_real = False + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + else: + url = url_tvshow + else: + url = url_id real_title_mps = real_title_mps.replace("-", " ") - #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps + " / context : " + context) real_title = real_title_mps show = real_title - if ".com/serie" in url and "/miniseries" not in url: - + if ".com/serie" in url and "/miniseries" not in url and url_real: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" context = "tvshow" itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: + if config.get_setting("unify"): #Si Titulos Inteligentes SI seleccionados: + title = real_title itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) @@ -307,7 +332,7 @@ def listado_busqueda(item): if post: itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", - thumbnail=get_thumb("next.png"))) + text_color='yellow', text_bold=True, thumbnail=get_thumb("next.png"))) return itemlist @@ -318,6 +343,8 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") + #logger.debug("item: ") + #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) @@ -334,27 +361,46 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + if item.infoLabels['year']: #añadir el año al título general year = '[%s]' % str(item.infoLabels['year']) else: year = "" + if item.infoLabels['aired'] and item.contentType == "episode": #añadir el año de episodio para series + year = scrapertools.find_single_match(str(item.infoLabels['aired']), r'\/(\d{4})') + year = '[%s]' % year + + title_gen = title if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + title_epi = '%sx%s - %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle) + title_gen = '%s %s, %s' % (title_epi, year, title) + title_torrent = '%s, %s' % (title_epi, item.contentSerieName) + else: + title_torrent = item.contentTitle + + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_torrent = '%s [%s]' %(title_torrent, item.infoLabels['quality']) + else: + title_torrent = '%s (%s)' %(title_torrent, item.infoLabels['quality']) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_gen = '[COLOR gold]**- Título: [/COLOR]%s -**' % (title_gen) + else: + title_gen = '[COLOR gold]Título: [/COLOR]%s' % (title_gen) + if config.get_setting("quit_channel_name", "videolibrary") == 1 and item.contentChannel == "videolibrary": + title_gen = '%s: %s' % (item.channel.capitalize(), title_gen) + itemlist.append(item.clone(title=title_gen, action="", folder=False)) #Título con todos los datos del vídeo - itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo - - if item.contentType != "episode": - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis - + title = title_torrent + title_torrent = '[COLOR yellow][Torrent]- [/COLOR]%s [online]' % (title_torrent) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + Item(channel=item.channel, action="play", server="torrent", title=title_torrent, fulltitle=title, url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) - logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) + logger.debug("TORRENT: url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) # escraped ver vídeos, descargar vídeos un link, múltiples liks @@ -362,23 +408,6 @@ def findvideos(item): data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?torrentlocura.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) #logger.debug("matar %s" % data) - # Antiguo sistema de scrapeo de servidores usado por Newpct1. Como no funciona con torrent.locura, se sustituye por este más común - #patron_descargar = '
    ]+>.*?' - #patron_ver = '
    ]+>.*?' - - #match_ver = scrapertools.find_single_match(data, patron_ver) - #match_descargar = scrapertools.find_single_match(data, patron_descargar) - - #patron = '
    0: - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Ver: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Ver: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -399,7 +431,9 @@ def findvideos(item): mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + titulo = '[COLOR yellow][%s]-[/COLOR] %s [online]' % (servidor.capitalize(), titulo) + logger.debug("VER: url: " + enlace + " / title: " + titulo + " / servidor: " + servidor + " / idioma: " + idioma) + if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) @@ -407,33 +441,42 @@ def findvideos(item): enlace = devuelve[0][1] itemlist.append( Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Descargar: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Descargar: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") - titulo = "Partes " + titulo = "Descarga " p = 1 - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + logger.debug("DESCARGAR: url: " + enlace + " / title: " + titulo + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) p += 1 mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + parte_titulo = '[COLOR yellow][%s]-[/COLOR] %s' % (servidor.capitalize(), parte_titulo) + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + parte_titulo = '%s [%s]' %(parte_titulo, item.infoLabels['quality']) + else: + parte_titulo = '%s (%s)' %(parte_titulo, item.infoLabels['quality']) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, + title=parte_titulo, fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -469,16 +512,20 @@ def episodios(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern - data = scrapertools.get_match(data, pattern) - #logger.debug("data: " + data) + if scrapertools.find_single_match(data, pattern): + data = scrapertools.get_match(data, pattern) + else: + logger.debug(item) + logger.debug("data: " + data) + return itemlist if "pelisyseries.com" in host: pattern = ']*>
    ]+>(?P.*?)?<\/h3>.*?<\/li>' else: pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) + logger.debug("patron: " + pattern) + logger.debug(matches) season = "1" diff --git a/plugin.video.alfa/channels/torrentrapid.py b/plugin.video.alfa/channels/torrentrapid.py index 2ef3d953..e0a0a09e 100644 --- a/plugin.video.alfa/channels/torrentrapid.py +++ b/plugin.video.alfa/channels/torrentrapid.py @@ -99,7 +99,6 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) logger.debug('item.extra: %s'%item.extra) @@ -167,8 +166,9 @@ def listado(item): title = title_alt context_title = title_alt show = title_alt - #if item.extra != "buscar-list": - # title = title + '[' + calidad + "]" + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + ' [' + calidad + "]" #Este bucle parece obsoleto: #context = "" @@ -257,6 +257,7 @@ def listado_busqueda(item): if calidad == "": calidad = title context = "movie" + url_real = True # no mostramos lo que no sean videos if "juego/" in url: @@ -281,22 +282,46 @@ def listado_busqueda(item): serieid = "" else: serieid = "" - - url = host + calidad_mps + real_title_mps + "/" + serieid + + #detectar si la url creada de tvshow es válida o hay que volver atras + url_tvshow = host + calidad_mps + real_title_mps + "/" + url_id = host + calidad_mps + real_title_mps + "/" + serieid + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_id).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern + if not scrapertools.find_single_match(data_serie, pattern): + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_tvshow).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + if not scrapertools.find_single_match(data_serie, pattern): + context = "movie" + url_real = False + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + else: + url = url_tvshow + else: + url = url_id real_title_mps = real_title_mps.replace("-", " ") - #logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps) + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps + " / context : " + context) real_title = real_title_mps show = real_title - if ".com/serie" in url and "/miniseries" not in url: - + if ".com/serie" in url and "/miniseries" not in url and url_real: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" context = "tvshow" itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) else: + if config.get_setting("unify"): #Si Titulos Inteligentes SI seleccionados: + title = real_title itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) @@ -307,7 +332,7 @@ def listado_busqueda(item): if post: itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", - thumbnail=get_thumb("next.png"))) + text_color='yellow', text_bold=True, thumbnail=get_thumb("next.png"))) return itemlist @@ -318,6 +343,8 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") + #logger.debug("item: ") + #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) @@ -334,27 +361,46 @@ def findvideos(item): # escraped torrent url = scrapertools.find_single_match(data, patron) - if item.infoLabels['year']: #añadir el año para series, filtrado por Unify + if item.infoLabels['year']: #añadir el año al título general year = '[%s]' % str(item.infoLabels['year']) else: year = "" + if item.infoLabels['aired'] and item.contentType == "episode": #añadir el año de episodio para series + year = scrapertools.find_single_match(str(item.infoLabels['aired']), r'\/(\d{4})') + year = '[%s]' % year + + title_gen = title if item.contentType == "episode": #scrapear información duplicada en Series title = re.sub(r'Temp.*?\[', '[', title) title = re.sub(r'\[Cap.*?\]', '', title) - title = '%sx%s - %s %s, %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle, year, title) + title_epi = '%sx%s - %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle) + title_gen = '%s %s, %s' % (title_epi, year, title) + title_torrent = '%s, %s' % (title_epi, item.contentSerieName) + else: + title_torrent = item.contentTitle + + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_torrent = '%s [%s]' %(title_torrent, item.infoLabels['quality']) + else: + title_torrent = '%s (%s)' %(title_torrent, item.infoLabels['quality']) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_gen = '[COLOR gold]**- Título: [/COLOR]%s -**' % (title_gen) + else: + title_gen = '[COLOR gold]Título: [/COLOR]%s' % (title_gen) + if config.get_setting("quit_channel_name", "videolibrary") == 1 and item.contentChannel == "videolibrary": + title_gen = '%s: %s' % (item.channel.capitalize(), title_gen) + itemlist.append(item.clone(title=title_gen, action="", folder=False)) #Título con todos los datos del vídeo - itemlist.append(item.clone(title=title, action="", folder=False)) #Título con todos los datos del vídeo - - if item.contentType != "episode": - title = re.sub(r'\s(\[.*?\])', ' ', title) #scrapea calidad en pelis - + title = title_torrent + title_torrent = '[COLOR yellow][Torrent]- [/COLOR]%s [online]' % (title_torrent) if url != "": #Torrent itemlist.append( - Item(channel=item.channel, action="play", server="torrent", title=title, fulltitle=title, + Item(channel=item.channel, action="play", server="torrent", title=title_torrent, fulltitle=title, url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) - logger.debug("url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) + logger.debug("TORRENT: url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) # escraped ver vídeos, descargar vídeos un link, múltiples liks @@ -362,23 +408,6 @@ def findvideos(item): data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?torrentrapid.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) #logger.debug("matar %s" % data) - # Antiguo sistema de scrapeo de servidores usado por Newpct1. Como no funciona con torrent.locura, se sustituye por este más común - #patron_descargar = '
    ]+>.*?' - #patron_ver = '
    ]+>.*?' - - #match_ver = scrapertools.find_single_match(data, patron_ver) - #match_descargar = scrapertools.find_single_match(data, patron_descargar) - - #patron = '
    0: - itemlist.append(item.clone(title=" Enlaces Ver: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Ver: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Ver: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: if "Ver" in titulo: @@ -399,7 +431,9 @@ def findvideos(item): mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + titulo = '[COLOR yellow][%s]-[/COLOR] %s [online]' % (servidor.capitalize(), titulo) + logger.debug("VER: url: " + enlace + " / title: " + titulo + " / servidor: " + servidor + " / idioma: " + idioma) + if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) @@ -407,33 +441,42 @@ def findvideos(item): enlace = devuelve[0][1] itemlist.append( Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, - fulltitle=titulo, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass if len(enlaces_descargar) > 0: - itemlist.append(item.clone(title=" Enlaces Descargar: ", action="", folder=False)) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Descargar: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Descargar: [/COLOR]", action="", folder=False)) for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: if "Ver" not in titulo: servidor = servidor.replace("uploaded", "uploadedto") partes = enlace.split(" ") - titulo = "Partes " + titulo = "Descarga " p = 1 - logger.debug("url: " + enlace + " / title: " + title + " / servidor: " + servidor + " / idioma: " + idioma) + logger.debug("DESCARGAR: url: " + enlace + " / title: " + titulo + title + " / servidor: " + servidor + " / idioma: " + idioma) for enlace in partes: - parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + " - " + title + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) p += 1 mostrar_server = True if config.get_setting("hidepremium"): mostrar_server = servertools.is_server_enabled(servidor) + parte_titulo = '[COLOR yellow][%s]-[/COLOR] %s' % (servidor.capitalize(), parte_titulo) + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + parte_titulo = '%s [%s]' %(parte_titulo, item.infoLabels['quality']) + else: + parte_titulo = '%s (%s)' %(parte_titulo, item.infoLabels['quality']) if mostrar_server: try: devuelve = servertools.findvideosbyserver(enlace, servidor) if devuelve: enlace = devuelve[0][1] itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, - title=parte_titulo, fulltitle=parte_titulo, url=enlace, thumbnail=logo, + title=parte_titulo, fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) except: pass @@ -469,16 +512,20 @@ def episodios(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com pattern = '
      (.*?)
    ' % "buscar-list" # item.pattern - data = scrapertools.get_match(data, pattern) - #logger.debug("data: " + data) + if scrapertools.find_single_match(data, pattern): + data = scrapertools.get_match(data, pattern) + else: + logger.debug(item) + logger.debug("data: " + data) + return itemlist if "pelisyseries.com" in host: pattern = ']*>
    ]+>(?P.*?)?<\/h3>.*?<\/li>' else: pattern = ']*>]+>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) + logger.debug("patron: " + pattern) + logger.debug(matches) season = "1" From c15a10fae723adb610b94115d011d71367918601 Mon Sep 17 00:00:00 2001 From: Intel1 <25161862+Intel11@users.noreply.github.com> Date: Sun, 8 Apr 2018 11:53:12 -0500 Subject: [PATCH 16/22] Add files via upload --- plugin.video.alfa/core/cloudflare.py | 39 +++++++++++++++++++++++++--- plugin.video.alfa/core/httptools.py | 11 ++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/plugin.video.alfa/core/cloudflare.py b/plugin.video.alfa/core/cloudflare.py index 356eb310..b4948b71 100755 --- a/plugin.video.alfa/core/cloudflare.py +++ b/plugin.video.alfa/core/cloudflare.py @@ -9,7 +9,7 @@ import urllib import urlparse from platformcode import logger - +from decimal import Decimal, ROUND_UP class Cloudflare: def __init__(self, response): @@ -62,12 +62,20 @@ class Cloudflare: def get_url(self): # Metodo #1 (javascript) if self.js_data.get("wait", 0): - jschl_answer = self.decode(self.js_data["value"]) + jschl_answer = self.decode2(self.js_data["value"]) for op, v in self.js_data["op"]: - jschl_answer = eval(str(jschl_answer) + op + str(self.decode(v))) + #jschl_answer = eval(str(jschl_answer) + op + str(self.decode2(v))) + if op == '+': + jschl_answer = jschl_answer + self.decode2(v) + elif op == '-': + jschl_answer = jschl_answer - self.decode2(v) + elif op == '*': + jschl_answer = jschl_answer * self.decode2(v) + elif op == '/': + jschl_answer = jschl_answer / self.decode2(v) - self.js_data["params"]["jschl_answer"] = jschl_answer + len(self.domain) + self.js_data["params"]["jschl_answer"] = round(jschl_answer, 10) + len(self.domain) response = "%s://%s%s?%s" % ( self.protocol, self.domain, self.js_data["auth_url"], urllib.urlencode(self.js_data["params"])) @@ -85,6 +93,29 @@ class Cloudflare: return response + def decode2(self, data): + data = re.sub("\!\+\[\]", "1", data) + data = re.sub("\!\!\[\]", "1", data) + data = re.sub("\[\]", "0", data) + + pos = data.find("/") + numerador = data[:pos] + denominador = data[pos+1:] + + aux = re.compile('\(([0-9\+]+)\)').findall(numerador) + num1 = "" + for n in aux: + num1 += str(eval(n)) + + aux = re.compile('\(([0-9\+]+)\)').findall(denominador) + num2 = "" + for n in aux: + num2 += str(eval(n)) + + #return float(num1) / float(num2) + #return Decimal(Decimal(num1) / Decimal(num2)).quantize(Decimal('.0000000000000001'), rounding=ROUND_UP) + return Decimal(Decimal(num1) / Decimal(num2)).quantize(Decimal('.0000000000000001')) + def decode(self, data): t = time.time() timeout = False diff --git a/plugin.video.alfa/core/httptools.py b/plugin.video.alfa/core/httptools.py index ffda92c4..8e1217c9 100755 --- a/plugin.video.alfa/core/httptools.py +++ b/plugin.video.alfa/core/httptools.py @@ -25,7 +25,7 @@ ficherocookies = os.path.join(config.get_data_path(), "cookies.dat") # Headers por defecto, si no se especifica nada default_headers = dict() -default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3163.100 Safari/537.36" +default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3163.100 Safari/537.36" default_headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" default_headers["Accept-Language"] = "es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3" default_headers["Accept-Charset"] = "UTF-8" @@ -68,7 +68,7 @@ load_cookies() def downloadpage(url, post=None, headers=None, timeout=None, follow_redirects=True, cookies=True, replace_headers=False, - add_referer=False, only_headers=False, bypass_cloudflare=True): + add_referer=False, only_headers=False, bypass_cloudflare=True, count_retries=0): """ Abre una url y retorna los datos obtenidos @@ -234,13 +234,14 @@ def downloadpage(url, post=None, headers=None, timeout=None, follow_redirects=Tr logger.info("No se ha podido descomprimir") # Anti Cloudflare - if bypass_cloudflare: + if bypass_cloudflare and count_retries < 5: cf = Cloudflare(response) if cf.is_cloudflare: + count_retries += 1 logger.info("cloudflare detectado, esperando %s segundos..." % cf.wait_time) auth_url = cf.get_url() - logger.info("Autorizando... url: %s" % auth_url) - if downloadpage(auth_url, headers=request_headers, replace_headers=True).sucess: + logger.info("Autorizando... intento %d url: %s" % (count_retries, auth_url)) + if downloadpage(auth_url, headers=request_headers, replace_headers=True, count_retries=count_retries).sucess: logger.info("Autorización correcta, descargando página") resp = downloadpage(url=response["url"], post=post, headers=headers, timeout=timeout, follow_redirects=follow_redirects, From f959c5e516aec6b1e43116fe2cf2419fe2f0c904 Mon Sep 17 00:00:00 2001 From: Intel1 <25161862+Intel11@users.noreply.github.com> Date: Sun, 8 Apr 2018 11:54:59 -0500 Subject: [PATCH 17/22] Update httptools.py --- plugin.video.alfa/core/httptools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.video.alfa/core/httptools.py b/plugin.video.alfa/core/httptools.py index 8e1217c9..460f4f37 100755 --- a/plugin.video.alfa/core/httptools.py +++ b/plugin.video.alfa/core/httptools.py @@ -25,7 +25,7 @@ ficherocookies = os.path.join(config.get_data_path(), "cookies.dat") # Headers por defecto, si no se especifica nada default_headers = dict() -default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3163.100 Safari/537.36" +default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3163.100 Safari/537.36" default_headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" default_headers["Accept-Language"] = "es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3" default_headers["Accept-Charset"] = "UTF-8" From d573508c8d0710365e094ed2f34027020817b2e7 Mon Sep 17 00:00:00 2001 From: Alfa <30527549+alfa-addon@users.noreply.github.com> Date: Sun, 8 Apr 2018 12:46:38 -0500 Subject: [PATCH 18/22] v2.5.8 --- plugin.video.alfa/addon.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/plugin.video.alfa/addon.xml b/plugin.video.alfa/addon.xml index 5bd05fdc..12ee44df 100755 --- a/plugin.video.alfa/addon.xml +++ b/plugin.video.alfa/addon.xml @@ -1,5 +1,5 @@ - + @@ -19,13 +19,8 @@ [B]Estos son los cambios para esta versión:[/B] [COLOR green][B]Canales agregados y arreglos[/B][/COLOR] - » diskokosmiko » gamovideo - » mispelisyseries » pelisplus - » seriespapaya » descargas2020 - » openload » torrentlocura - » torrentrapid » streamcloud - » danimados » animemovil - » serieslan + » torrentrapid » torrentlocura + » mispelisyseries » descargas2020 ¤ arreglos internos ¤ Gracias a la colaboración de @pipcat y @lopezvg en ésta versión From 4e59a6178c09cc0e6955f40dc7cef1eb89a3f1d4 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Sun, 8 Apr 2018 19:49:18 +0200 Subject: [PATCH 19/22] Nuevos canales Tvsinpagar y Tumejortorrent, clones de Newpct1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Añadir estos dos nuevos canales y actualizar levemente los otros cuatro clones para mantener la compatibilidad a 100% --- plugin.video.alfa/channels/descargas2020.py | 108 ++- plugin.video.alfa/channels/mispelisyseries.py | 112 ++- plugin.video.alfa/channels/torrentlocura.py | 108 ++- plugin.video.alfa/channels/torrentrapid.py | 108 ++- .../channels/tumejortorrent.json | 58 ++ plugin.video.alfa/channels/tumejortorrent.py | 656 ++++++++++++++++++ plugin.video.alfa/channels/tvsinpagar.json | 58 ++ plugin.video.alfa/channels/tvsinpagar.py | 656 ++++++++++++++++++ 8 files changed, 1610 insertions(+), 254 deletions(-) create mode 100644 plugin.video.alfa/channels/tumejortorrent.json create mode 100644 plugin.video.alfa/channels/tumejortorrent.py create mode 100644 plugin.video.alfa/channels/tvsinpagar.json create mode 100644 plugin.video.alfa/channels/tvsinpagar.py diff --git a/plugin.video.alfa/channels/descargas2020.py b/plugin.video.alfa/channels/descargas2020.py index 7947d4c7..7de30a6b 100644 --- a/plugin.video.alfa/channels/descargas2020.py +++ b/plugin.video.alfa/channels/descargas2020.py @@ -42,9 +42,7 @@ def submenu(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com - #patron = '
  • .*?
      (.*?)
    ' - patron = '
  • <.*?href="'+item.url+item.extra + '/">.*?(.*?)' #Filtrado por url, compatibilidad con mispelisy.series.com - #logger.debug("patron: " + patron + " / data: " + data) + patron = '
  • .*?(.*?)' if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com data = ' Documentales' else: @@ -99,11 +97,8 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|()", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) - logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': - logger.debug('item.title: %s'% item.title) patron = '
      (.*?)
    ' fichas = scrapertools.get_match(data, patron) page_extra = item.extra @@ -118,8 +113,6 @@ def listado(item): patron += '([^<].*?)?<' # la calidad #logger.debug("patron: " + patron + " / fichas: " + fichas) matches = re.compile(patron, re.DOTALL).findall(fichas) - #logger.debug('item.next_page: %s'%item.next_page) - #logger.debug(matches) # Paginacion if item.next_page != 'b': @@ -169,26 +162,6 @@ def listado(item): if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: if calidad: title = title + ' [' + calidad + "]" - - #Este bucle parece obsoleto: - #context = "" - #context_title = scrapertools.find_single_match(url, "http://(?:www.)?descargas2020.com/(.*?)/(.*?)/") - #if context_title: - # try: - # context = context_title[0].replace("descargar-", "").replace("descargar", "").replace("pelicula", "movie").replace("series", "tvshow").replace("-hd", "").replace("-vo", "") - # context_title = context_title[1].replace("-", " ") - # if re.search('\d{4}', context_title[-4:]): - # context_title = context_title[:-4] - # elif re.search('\(\d{4}\)', context_title[-6:]): - # context_title = context_title[:-6] - # - # except: - # context_title = show - # - - #logger.debug('contxt title: %s'%context_title) - #logger.debug('year: %s' % year) - #logger.debug('context: %s' % context) if not 'array' in title: itemlist.append(Item(channel=item.channel, action=action, title=title, url=url, thumbnail=thumbnail, @@ -233,8 +206,6 @@ def listado_busqueda(item): data = scrapertools.get_match(data, pattern) pattern = ']*>(?P.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series @@ -343,8 +314,6 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") - #logger.debug("item: ") - #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) @@ -357,7 +326,6 @@ def findvideos(item): caratula = scrapertools.find_single_match(data, '<h1.*?<img.*?src="([^"]+)') patron = 'openTorrent.*?title=".*?class="btn-torrent">.*?function openTorrent.*?href = "(.*?)";' - #logger.debug("patron: " + patron + " / data: " + data) # escraped torrent url = scrapertools.find_single_match(data, patron) @@ -406,13 +374,11 @@ def findvideos(item): data = data.replace("http://tumejorserie.com/descargar/url_encript.php?link=", "(") data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?descargas2020.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) - #logger.debug("matar %s" % data) # Nuevo sistema de scrapeo de servidores creado por Torrentlocula, compatible con otros clones de Newpct1 patron = '<div class=\"box1\"[^<]+<img src=\"([^<]+)?" style[^<]+><\/div[^<]+<div class="box2">([^<]+)?<\/div[^<]+<div class="box3">([^<]+)?' patron += '<\/div[^<]+<div class="box4">([^<]+)?<\/div[^<]+<div class="box5"><a href=(.*?)? rel.*?' patron += '<\/div[^<]+<div class="box6">([^<]+)?<' - #logger.debug("Patron: " + patron) enlaces_ver = re.compile(patron, re.DOTALL).findall(data) enlaces_descargar = enlaces_ver @@ -507,7 +473,6 @@ def episodios(item): list_pages = [item.url] for index, page in enumerate(list_pages): - logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com @@ -524,8 +489,8 @@ def episodios(item): else: pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2[^>]+>(?P<info>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - logger.debug("patron: " + pattern) - logger.debug(matches) + #logger.debug("patron: " + pattern) + #logger.debug(matches) season = "1" @@ -542,55 +507,72 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" - logger.debug("patron: " + pattern) - logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] if match['season'] is None: match['season'] = season if match['episode'] is None: match['episode'] = "0" - if match['quality']: item.quality = match['quality'] + if match['quality']: + item.quality = match['quality'] if match["episode2"]: multi = True - title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - str(match["episode2"]).zfill(2), match["lang"], - match["quality"]) + title = "%s (%sx%s-%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: multi = False - title = "%s (%sx%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - match["lang"], match["quality"]) + title = "%s (%sx%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: # old style - pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d+).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ + if scrapertools.find_single_match(info, '\[\d{3}\]'): + info = re.sub(r'\[(\d{3}\])', r'[Cap.\1', info) + elif scrapertools.find_single_match(info, '\[Cap.\d{2}_\d{2}\]'): + info = re.sub(r'\[Cap.(\d{2})_(\d{2})\]', r'[Cap.1\1_1\2]', info) + elif scrapertools.find_single_match(info, '\[Cap.([A-Za-z]+)\]'): + info = re.sub(r'\[Cap.([A-Za-z]+)\]', '[Cap.100]', info) + if scrapertools.find_single_match(info, '\[Cap.\d{2,3}'): + pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" - logger.debug("patron: " + pattern) - logger.debug(info) + elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): + pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" + else: + logger.debug("patron episodio: " + pattern) + logger.debug(info) + continue + r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - #logger.debug("data %s" % match) - - #if match['season'] is "": match['season'] = season - #if match['episode'] is "": match['episode'] = "0" - #logger.debug(match) str_lang = "" + if match['quality']: + item.quality = match['quality'] + if match["lang"] is not None: str_lang = "[%s]" % match["lang"] - + item.quality = "%s %s" % (item.quality, match['lang']) + if match["season2"] and match["episode2"]: multi = True if match["season"] == match["season2"]: - title = "%s (%sx%s-%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["episode2"], str_lang, match["quality"]) + title = "%s (%sx%s-%s) %s" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s-%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["season2"], match["episode2"], str_lang, - match["quality"]) + title = "%s (%sx%s-%sx%s) %s" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], str_lang, - match["quality"]) + title = "%s (%sx%s) %s" % (item.show, match["season"], match["episode"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) multi = False season = match['season'] @@ -619,7 +601,7 @@ def search(item, texto): item.post = "q=%s" % texto item.pattern = "buscar-list" itemlist = listado_busqueda(item) - + return itemlist # Se captura la excepción, para no interrumpir al buscador global si un canal falla diff --git a/plugin.video.alfa/channels/mispelisyseries.py b/plugin.video.alfa/channels/mispelisyseries.py index fc8f4bda..f58462a8 100644 --- a/plugin.video.alfa/channels/mispelisyseries.py +++ b/plugin.video.alfa/channels/mispelisyseries.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re @@ -10,7 +10,7 @@ from core.item import Item from platformcode import config, logger from core import tmdb -host = 'http://mispelisyseries.com/' +host = 'http://www.mispelisyseries.com/' def mainlist(item): logger.info() @@ -42,9 +42,7 @@ def submenu(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com - #patron = '<li><a href="http://(?:www.)?mispelisyseries.com/' + item.extra + '/">.*?<ul>(.*?)</ul>' - patron = '<li><.*?href="'+item.url+item.extra + '/">.*?<ul.*?>(.*?)</ul>' #Filtrado por url, compatibilidad con mispelisy.series.com - #logger.debug("patron: " + patron + " / data: " + data) + patron = '<li><a href="http://(?:www.)?mispelisyseries.com/' + item.extra + '/">.*?<ul.*?>(.*?)</ul>' if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com data = '<a href="http://mispelisyseries.com/varios/" title="Documentales"><i class="icon-rocket"></i> Documentales</a>' else: @@ -99,11 +97,8 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) - logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': - logger.debug('item.title: %s'% item.title) patron = '<ul class="' + item.extra + '">(.*?)</ul>' fichas = scrapertools.get_match(data, patron) page_extra = item.extra @@ -118,8 +113,6 @@ def listado(item): patron += '<span>([^<].*?)?<' # la calidad #logger.debug("patron: " + patron + " / fichas: " + fichas) matches = re.compile(patron, re.DOTALL).findall(fichas) - #logger.debug('item.next_page: %s'%item.next_page) - #logger.debug(matches) # Paginacion if item.next_page != 'b': @@ -169,26 +162,6 @@ def listado(item): if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: if calidad: title = title + ' [' + calidad + "]" - - #Este bucle parece obsoleto: - #context = "" - #context_title = scrapertools.find_single_match(url, "http://(?:www.)?mispelisyseries.com/(.*?)/(.*?)/") - #if context_title: - # try: - # context = context_title[0].replace("descargar-", "").replace("descargar", "").replace("pelicula", "movie").replace("series", "tvshow").replace("-hd", "").replace("-vo", "") - # context_title = context_title[1].replace("-", " ") - # if re.search('\d{4}', context_title[-4:]): - # context_title = context_title[:-4] - # elif re.search('\(\d{4}\)', context_title[-6:]): - # context_title = context_title[:-6] - # - # except: - # context_title = show - # - - #logger.debug('contxt title: %s'%context_title) - #logger.debug('year: %s' % year) - #logger.debug('context: %s' % context) if not 'array' in title: itemlist.append(Item(channel=item.channel, action=action, title=title, url=url, thumbnail=thumbnail, @@ -233,8 +206,6 @@ def listado_busqueda(item): data = scrapertools.get_match(data, pattern) pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2.*?>(?P<title>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series @@ -343,8 +314,6 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") - #logger.debug("item: ") - #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) @@ -357,7 +326,6 @@ def findvideos(item): caratula = scrapertools.find_single_match(data, '<h1.*?<img.*?src="([^"]+)') patron = 'openTorrent.*?title=".*?class="btn-torrent">.*?function openTorrent.*?href = "(.*?)";' - #logger.debug("patron: " + patron + " / data: " + data) # escraped torrent url = scrapertools.find_single_match(data, patron) @@ -406,13 +374,11 @@ def findvideos(item): data = data.replace("http://tumejorserie.com/descargar/url_encript.php?link=", "(") data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?mispelisyseries.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) - #logger.debug("matar %s" % data) # Nuevo sistema de scrapeo de servidores creado por Torrentlocula, compatible con otros clones de Newpct1 patron = '<div class=\"box1\"[^<]+<img src=\"([^<]+)?" style[^<]+><\/div[^<]+<div class="box2">([^<]+)?<\/div[^<]+<div class="box3">([^<]+)?' patron += '<\/div[^<]+<div class="box4">([^<]+)?<\/div[^<]+<div class="box5"><a href=(.*?)? rel.*?' patron += '<\/div[^<]+<div class="box6">([^<]+)?<' - #logger.debug("Patron: " + patron) enlaces_ver = re.compile(patron, re.DOTALL).findall(data) enlaces_descargar = enlaces_ver @@ -507,7 +473,6 @@ def episodios(item): list_pages = [item.url] for index, page in enumerate(list_pages): - logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com @@ -524,8 +489,8 @@ def episodios(item): else: pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2[^>]+>(?P<info>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - logger.debug("patron: " + pattern) - logger.debug(matches) + #logger.debug("patron: " + pattern) + #logger.debug(matches) season = "1" @@ -542,55 +507,72 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" - logger.debug("patron: " + pattern) - logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] if match['season'] is None: match['season'] = season if match['episode'] is None: match['episode'] = "0" - if match['quality']: item.quality = match['quality'] + if match['quality']: + item.quality = match['quality'] if match["episode2"]: multi = True - title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - str(match["episode2"]).zfill(2), match["lang"], - match["quality"]) + title = "%s (%sx%s-%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: multi = False - title = "%s (%sx%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - match["lang"], match["quality"]) + title = "%s (%sx%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: # old style - pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d+).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ + if scrapertools.find_single_match(info, '\[\d{3}\]'): + info = re.sub(r'\[(\d{3}\])', r'[Cap.\1', info) + elif scrapertools.find_single_match(info, '\[Cap.\d{2}_\d{2}\]'): + info = re.sub(r'\[Cap.(\d{2})_(\d{2})\]', r'[Cap.1\1_1\2]', info) + elif scrapertools.find_single_match(info, '\[Cap.([A-Za-z]+)\]'): + info = re.sub(r'\[Cap.([A-Za-z]+)\]', '[Cap.100]', info) + if scrapertools.find_single_match(info, '\[Cap.\d{2,3}'): + pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" - logger.debug("patron: " + pattern) - logger.debug(info) + elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): + pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" + else: + logger.debug("patron episodio: " + pattern) + logger.debug(info) + continue + r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - #logger.debug("data %s" % match) - - #if match['season'] is "": match['season'] = season - #if match['episode'] is "": match['episode'] = "0" - #logger.debug(match) str_lang = "" + if match['quality']: + item.quality = match['quality'] + if match["lang"] is not None: str_lang = "[%s]" % match["lang"] - + item.quality = "%s %s" % (item.quality, match['lang']) + if match["season2"] and match["episode2"]: multi = True if match["season"] == match["season2"]: - title = "%s (%sx%s-%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["episode2"], str_lang, match["quality"]) + title = "%s (%sx%s-%s) %s" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s-%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["season2"], match["episode2"], str_lang, - match["quality"]) + title = "%s (%sx%s-%sx%s) %s" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], str_lang, - match["quality"]) + title = "%s (%sx%s) %s" % (item.show, match["season"], match["episode"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) multi = False season = match['season'] @@ -619,7 +601,7 @@ def search(item, texto): item.post = "q=%s" % texto item.pattern = "buscar-list" itemlist = listado_busqueda(item) - + return itemlist # Se captura la excepción, para no interrumpir al buscador global si un canal falla diff --git a/plugin.video.alfa/channels/torrentlocura.py b/plugin.video.alfa/channels/torrentlocura.py index e6b2d413..2d3d50c6 100755 --- a/plugin.video.alfa/channels/torrentlocura.py +++ b/plugin.video.alfa/channels/torrentlocura.py @@ -42,9 +42,7 @@ def submenu(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com - #patron = '<li><a href="http://(?:www.)?torrentlocura.com/' + item.extra + '/">.*?<ul>(.*?)</ul>' - patron = '<li><.*?href="'+item.url+item.extra + '/">.*?<ul.*?>(.*?)</ul>' #Filtrado por url, compatibilidad con mispelisy.series.com - #logger.debug("patron: " + patron + " / data: " + data) + patron = '<li><a href="http://(?:www.)?torrentlocura.com/' + item.extra + '/">.*?<ul.*?>(.*?)</ul>' if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com data = '<a href="http://torrentlocura.com/varios/" title="Documentales"><i class="icon-rocket"></i> Documentales</a>' else: @@ -99,11 +97,8 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) - logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': - logger.debug('item.title: %s'% item.title) patron = '<ul class="' + item.extra + '">(.*?)</ul>' fichas = scrapertools.get_match(data, patron) page_extra = item.extra @@ -118,8 +113,6 @@ def listado(item): patron += '<span>([^<].*?)?<' # la calidad #logger.debug("patron: " + patron + " / fichas: " + fichas) matches = re.compile(patron, re.DOTALL).findall(fichas) - #logger.debug('item.next_page: %s'%item.next_page) - #logger.debug(matches) # Paginacion if item.next_page != 'b': @@ -169,26 +162,6 @@ def listado(item): if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: if calidad: title = title + ' [' + calidad + "]" - - #Este bucle parece obsoleto: - #context = "" - #context_title = scrapertools.find_single_match(url, "http://(?:www.)?torrentlocura.com/(.*?)/(.*?)/") - #if context_title: - # try: - # context = context_title[0].replace("descargar-", "").replace("descargar", "").replace("pelicula", "movie").replace("series", "tvshow").replace("-hd", "").replace("-vo", "") - # context_title = context_title[1].replace("-", " ") - # if re.search('\d{4}', context_title[-4:]): - # context_title = context_title[:-4] - # elif re.search('\(\d{4}\)', context_title[-6:]): - # context_title = context_title[:-6] - # - # except: - # context_title = show - # - - #logger.debug('contxt title: %s'%context_title) - #logger.debug('year: %s' % year) - #logger.debug('context: %s' % context) if not 'array' in title: itemlist.append(Item(channel=item.channel, action=action, title=title, url=url, thumbnail=thumbnail, @@ -233,8 +206,6 @@ def listado_busqueda(item): data = scrapertools.get_match(data, pattern) pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2.*?>(?P<title>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series @@ -343,8 +314,6 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") - #logger.debug("item: ") - #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) @@ -357,7 +326,6 @@ def findvideos(item): caratula = scrapertools.find_single_match(data, '<h1.*?<img.*?src="([^"]+)') patron = 'openTorrent.*?title=".*?class="btn-torrent">.*?function openTorrent.*?href = "(.*?)";' - #logger.debug("patron: " + patron + " / data: " + data) # escraped torrent url = scrapertools.find_single_match(data, patron) @@ -406,13 +374,11 @@ def findvideos(item): data = data.replace("http://tumejorserie.com/descargar/url_encript.php?link=", "(") data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?torrentlocura.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) - #logger.debug("matar %s" % data) # Nuevo sistema de scrapeo de servidores creado por Torrentlocula, compatible con otros clones de Newpct1 patron = '<div class=\"box1\"[^<]+<img src=\"([^<]+)?" style[^<]+><\/div[^<]+<div class="box2">([^<]+)?<\/div[^<]+<div class="box3">([^<]+)?' patron += '<\/div[^<]+<div class="box4">([^<]+)?<\/div[^<]+<div class="box5"><a href=(.*?)? rel.*?' patron += '<\/div[^<]+<div class="box6">([^<]+)?<' - #logger.debug("Patron: " + patron) enlaces_ver = re.compile(patron, re.DOTALL).findall(data) enlaces_descargar = enlaces_ver @@ -507,7 +473,6 @@ def episodios(item): list_pages = [item.url] for index, page in enumerate(list_pages): - logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com @@ -524,8 +489,8 @@ def episodios(item): else: pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2[^>]+>(?P<info>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - logger.debug("patron: " + pattern) - logger.debug(matches) + #logger.debug("patron: " + pattern) + #logger.debug(matches) season = "1" @@ -542,55 +507,72 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" - logger.debug("patron: " + pattern) - logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] if match['season'] is None: match['season'] = season if match['episode'] is None: match['episode'] = "0" - if match['quality']: item.quality = match['quality'] + if match['quality']: + item.quality = match['quality'] if match["episode2"]: multi = True - title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - str(match["episode2"]).zfill(2), match["lang"], - match["quality"]) + title = "%s (%sx%s-%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: multi = False - title = "%s (%sx%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - match["lang"], match["quality"]) + title = "%s (%sx%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: # old style - pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d+).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ + if scrapertools.find_single_match(info, '\[\d{3}\]'): + info = re.sub(r'\[(\d{3}\])', r'[Cap.\1', info) + elif scrapertools.find_single_match(info, '\[Cap.\d{2}_\d{2}\]'): + info = re.sub(r'\[Cap.(\d{2})_(\d{2})\]', r'[Cap.1\1_1\2]', info) + elif scrapertools.find_single_match(info, '\[Cap.([A-Za-z]+)\]'): + info = re.sub(r'\[Cap.([A-Za-z]+)\]', '[Cap.100]', info) + if scrapertools.find_single_match(info, '\[Cap.\d{2,3}'): + pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" - logger.debug("patron: " + pattern) - logger.debug(info) + elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): + pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" + else: + logger.debug("patron episodio: " + pattern) + logger.debug(info) + continue + r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - #logger.debug("data %s" % match) - - #if match['season'] is "": match['season'] = season - #if match['episode'] is "": match['episode'] = "0" - #logger.debug(match) str_lang = "" + if match['quality']: + item.quality = match['quality'] + if match["lang"] is not None: str_lang = "[%s]" % match["lang"] - + item.quality = "%s %s" % (item.quality, match['lang']) + if match["season2"] and match["episode2"]: multi = True if match["season"] == match["season2"]: - title = "%s (%sx%s-%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["episode2"], str_lang, match["quality"]) + title = "%s (%sx%s-%s) %s" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s-%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["season2"], match["episode2"], str_lang, - match["quality"]) + title = "%s (%sx%s-%sx%s) %s" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], str_lang, - match["quality"]) + title = "%s (%sx%s) %s" % (item.show, match["season"], match["episode"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) multi = False season = match['season'] @@ -619,7 +601,7 @@ def search(item, texto): item.post = "q=%s" % texto item.pattern = "buscar-list" itemlist = listado_busqueda(item) - + return itemlist # Se captura la excepción, para no interrumpir al buscador global si un canal falla diff --git a/plugin.video.alfa/channels/torrentrapid.py b/plugin.video.alfa/channels/torrentrapid.py index e0a0a09e..85caf927 100644 --- a/plugin.video.alfa/channels/torrentrapid.py +++ b/plugin.video.alfa/channels/torrentrapid.py @@ -42,9 +42,7 @@ def submenu(item): data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com - #patron = '<li><a href="http://(?:www.)?torrentrapid.com/' + item.extra + '/">.*?<ul>(.*?)</ul>' - patron = '<li><.*?href="'+item.url+item.extra + '/">.*?<ul.*?>(.*?)</ul>' #Filtrado por url, compatibilidad con mispelisy.series.com - #logger.debug("patron: " + patron + " / data: " + data) + patron = '<li><a href="http://(?:www.)?torrentrapid.com/' + item.extra + '/">.*?<ul.*?>(.*?)</ul>' if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com data = '<a href="http://torrentrapid.com/varios/" title="Documentales"><i class="icon-rocket"></i> Documentales</a>' else: @@ -99,11 +97,8 @@ def listado(item): data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) #data = httptools.downloadpage(item.url).data data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") - logger.debug('item.modo: %s'%item.modo) - logger.debug('item.extra: %s'%item.extra) if item.modo != 'next' or item.modo =='': - logger.debug('item.title: %s'% item.title) patron = '<ul class="' + item.extra + '">(.*?)</ul>' fichas = scrapertools.get_match(data, patron) page_extra = item.extra @@ -118,8 +113,6 @@ def listado(item): patron += '<span>([^<].*?)?<' # la calidad #logger.debug("patron: " + patron + " / fichas: " + fichas) matches = re.compile(patron, re.DOTALL).findall(fichas) - #logger.debug('item.next_page: %s'%item.next_page) - #logger.debug(matches) # Paginacion if item.next_page != 'b': @@ -169,26 +162,6 @@ def listado(item): if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: if calidad: title = title + ' [' + calidad + "]" - - #Este bucle parece obsoleto: - #context = "" - #context_title = scrapertools.find_single_match(url, "http://(?:www.)?torrentrapid.com/(.*?)/(.*?)/") - #if context_title: - # try: - # context = context_title[0].replace("descargar-", "").replace("descargar", "").replace("pelicula", "movie").replace("series", "tvshow").replace("-hd", "").replace("-vo", "") - # context_title = context_title[1].replace("-", " ") - # if re.search('\d{4}', context_title[-4:]): - # context_title = context_title[:-4] - # elif re.search('\(\d{4}\)', context_title[-6:]): - # context_title = context_title[:-6] - # - # except: - # context_title = show - # - - #logger.debug('contxt title: %s'%context_title) - #logger.debug('year: %s' % year) - #logger.debug('context: %s' % context) if not 'array' in title: itemlist.append(Item(channel=item.channel, action=action, title=title, url=url, thumbnail=thumbnail, @@ -233,8 +206,6 @@ def listado_busqueda(item): data = scrapertools.get_match(data, pattern) pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2.*?>(?P<title>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - #logger.debug("patron: " + pattern) - #logger.debug(matches) for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series @@ -343,8 +314,6 @@ def findvideos(item): # item.url = item.url.replace(".com/",".com/ver-online/") # item.url = item.url.replace(".com/",".com/descarga-directa/") item.url = item.url.replace(".com/", ".com/descarga-torrent/") - #logger.debug("item: ") - #logger.debug(item) # Descarga la página data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) @@ -357,7 +326,6 @@ def findvideos(item): caratula = scrapertools.find_single_match(data, '<h1.*?<img.*?src="([^"]+)') patron = 'openTorrent.*?title=".*?class="btn-torrent">.*?function openTorrent.*?href = "(.*?)";' - #logger.debug("patron: " + patron + " / data: " + data) # escraped torrent url = scrapertools.find_single_match(data, patron) @@ -406,13 +374,11 @@ def findvideos(item): data = data.replace("http://tumejorserie.com/descargar/url_encript.php?link=", "(") data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?torrentrapid.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) - #logger.debug("matar %s" % data) # Nuevo sistema de scrapeo de servidores creado por Torrentlocula, compatible con otros clones de Newpct1 patron = '<div class=\"box1\"[^<]+<img src=\"([^<]+)?" style[^<]+><\/div[^<]+<div class="box2">([^<]+)?<\/div[^<]+<div class="box3">([^<]+)?' patron += '<\/div[^<]+<div class="box4">([^<]+)?<\/div[^<]+<div class="box5"><a href=(.*?)? rel.*?' patron += '<\/div[^<]+<div class="box6">([^<]+)?<' - #logger.debug("Patron: " + patron) enlaces_ver = re.compile(patron, re.DOTALL).findall(data) enlaces_descargar = enlaces_ver @@ -507,7 +473,6 @@ def episodios(item): list_pages = [item.url] for index, page in enumerate(list_pages): - logger.debug("Loading page %s/%s url=%s" % (index, len(list_pages), page)) data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com @@ -524,8 +489,8 @@ def episodios(item): else: pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2[^>]+>(?P<info>.*?)?<\/h2>' matches = re.compile(pattern, re.DOTALL).findall(data) - logger.debug("patron: " + pattern) - logger.debug(matches) + #logger.debug("patron: " + pattern) + #logger.debug(matches) season = "1" @@ -542,55 +507,72 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" - logger.debug("patron: " + pattern) - logger.debug(info) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] if match['season'] is None: match['season'] = season if match['episode'] is None: match['episode'] = "0" - if match['quality']: item.quality = match['quality'] + if match['quality']: + item.quality = match['quality'] if match["episode2"]: multi = True - title = "%s (%sx%s-%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - str(match["episode2"]).zfill(2), match["lang"], - match["quality"]) + title = "%s (%sx%s-%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: multi = False - title = "%s (%sx%s) [%s][%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), - match["lang"], match["quality"]) + title = "%s (%sx%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: # old style - pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d+).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ + if scrapertools.find_single_match(info, '\[\d{3}\]'): + info = re.sub(r'\[(\d{3}\])', r'[Cap.\1', info) + elif scrapertools.find_single_match(info, '\[Cap.\d{2}_\d{2}\]'): + info = re.sub(r'\[Cap.(\d{2})_(\d{2})\]', r'[Cap.1\1_1\2]', info) + elif scrapertools.find_single_match(info, '\[Cap.([A-Za-z]+)\]'): + info = re.sub(r'\[Cap.([A-Za-z]+)\]', '[Cap.100]', info) + if scrapertools.find_single_match(info, '\[Cap.\d{2,3}'): + pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" - logger.debug("patron: " + pattern) - logger.debug(info) + elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): + pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" + else: + logger.debug("patron episodio: " + pattern) + logger.debug(info) + continue + r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] - #logger.debug("data %s" % match) - - #if match['season'] is "": match['season'] = season - #if match['episode'] is "": match['episode'] = "0" - #logger.debug(match) str_lang = "" + if match['quality']: + item.quality = match['quality'] + if match["lang"] is not None: str_lang = "[%s]" % match["lang"] - + item.quality = "%s %s" % (item.quality, match['lang']) + if match["season2"] and match["episode2"]: multi = True if match["season"] == match["season2"]: - title = "%s (%sx%s-%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["episode2"], str_lang, match["quality"]) + title = "%s (%sx%s-%s) %s" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s-%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], - match["season2"], match["episode2"], str_lang, - match["quality"]) + title = "%s (%sx%s-%sx%s) %s" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) else: - title = "%s (%sx%s) %s[%s]" % (item.show, match["season"], match["episode"], str_lang, - match["quality"]) + title = "%s (%sx%s) %s" % (item.show, match["season"], match["episode"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) multi = False season = match['season'] @@ -619,7 +601,7 @@ def search(item, texto): item.post = "q=%s" % texto item.pattern = "buscar-list" itemlist = listado_busqueda(item) - + return itemlist # Se captura la excepción, para no interrumpir al buscador global si un canal falla diff --git a/plugin.video.alfa/channels/tumejortorrent.json b/plugin.video.alfa/channels/tumejortorrent.json new file mode 100644 index 00000000..9f11c74c --- /dev/null +++ b/plugin.video.alfa/channels/tumejortorrent.json @@ -0,0 +1,58 @@ +{ + "id": "tumejortorrent", + "name": "Tumejortorrent", + "active": true, + "adult": false, + "language": ["cast", "lat"], + "thumbnail": "tumejortorrent.png", + "banner": "tumejortorrent.png", + "categories": [ + "movie", + "tvshow", + "anime", + "torrent", + "documentary" + ], + "settings": [ + { + "id": "include_in_global_search", + "type": "bool", + "label": "Incluir en busqueda global", + "default": false, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_peliculas", + "type": "bool", + "label": "Incluir en Novedades - Peliculas", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_series", + "type": "bool", + "label": "Incluir en Novedades - Episodios de series", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_torrent", + "type": "bool", + "label": "Incluir en Novedades - Torrent", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_4k", + "type": "bool", + "label": "Incluir en Novedades - 4K", + "default": true, + "enabled": true, + "visible": true + } + ] +} \ No newline at end of file diff --git a/plugin.video.alfa/channels/tumejortorrent.py b/plugin.video.alfa/channels/tumejortorrent.py new file mode 100644 index 00000000..3ea77b12 --- /dev/null +++ b/plugin.video.alfa/channels/tumejortorrent.py @@ -0,0 +1,656 @@ +# -*- coding: utf-8 -*- + +import re + +from channelselector import get_thumb +from core import httptools +from core import scrapertools +from core import servertools +from core.item import Item +from platformcode import config, logger +from core import tmdb + +host = 'http://tumejortorrent.com/' + +def mainlist(item): + logger.info() + + itemlist = [] + + thumb_pelis=get_thumb("channels_movie.png") + thumb_series=get_thumb("channels_tvshow.png") + thumb_search = get_thumb("search.png") + + itemlist.append(Item(channel=item.channel, action="submenu", title="Películas", url=host, + extra="peliculas", thumbnail=thumb_pelis )) + + itemlist.append(Item(channel=item.channel, action="submenu", title="Series", url=host, extra="series", + thumbnail=thumb_series)) + + itemlist.append(Item(channel=item.channel, action="submenu", title="Documentales", url=host, extra="varios", + thumbnail=thumb_series)) + itemlist.append( + Item(channel=item.channel, action="search", title="Buscar", url=host + "buscar", thumbnail=thumb_search)) + + return itemlist + +def submenu(item): + logger.info() + itemlist = [] + + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com + + patron = '<li><a href="http://(?:www.)?tumejortorrent.com/' + item.extra + '/">.*?<ul.*?>(.*?)</ul>' + if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com + data = '<a href="http://tumejortorrent.com/varios/" title="Documentales"><i class="icon-rocket"></i> Documentales</a>' + else: + data = scrapertools.get_match(data, patron) + + patron = '<.*?href="([^"]+)".*?>([^>]+)</a>' + matches = re.compile(patron, re.DOTALL).findall(data) + + for scrapedurl, scrapedtitle in matches: + title = scrapedtitle.strip() + url = scrapedurl + + itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra="pelilist")) + itemlist.append( + Item(channel=item.channel, action="alfabeto", title=title + " [A-Z]", url=url, extra="pelilist")) + + if item.extra == "peliculas": + itemlist.append(Item(channel=item.channel, action="listado", title="Películas 4K", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) + itemlist.append( + Item(channel=item.channel, action="alfabeto", title="Películas 4K" + " [A-Z]", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) + + return itemlist + + +def alfabeto(item): + logger.info() + itemlist = [] + + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + patron = '<ul class="alfabeto">(.*?)</ul>' + data = scrapertools.get_match(data, patron) + + patron = '<a href="([^"]+)"[^>]+>([^>]+)</a>' + matches = re.compile(patron, re.DOTALL).findall(data) + + for scrapedurl, scrapedtitle in matches: + title = scrapedtitle.upper() + url = scrapedurl + + itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra=item.extra)) + + return itemlist + + +def listado(item): + logger.info() + itemlist = [] + url_next_page ='' + + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + #data = httptools.downloadpage(item.url).data + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + if item.modo != 'next' or item.modo =='': + patron = '<ul class="' + item.extra + '">(.*?)</ul>' + fichas = scrapertools.get_match(data, patron) + page_extra = item.extra + else: + fichas = data + page_extra = item.extra + + patron = '<a href="([^"]+).*?' # la url + patron += 'title="([^"]+).*?' # el titulo + patron += '<img.*?src="([^"]+)"[^>]+>.*?' # el thumbnail + patron += '<h2.*?>(.*?)?<\/h2>' # titulo alternativo + patron += '<span>([^<].*?)?<' # la calidad + #logger.debug("patron: " + patron + " / fichas: " + fichas) + matches = re.compile(patron, re.DOTALL).findall(fichas) + + # Paginacion + if item.next_page != 'b': + if len(matches) > 30: + url_next_page = item.url + matches = matches[:30] + next_page = 'b' + modo = 'continue' + else: + matches = matches[30:] + next_page = 'a' + patron_next_page = '<a href="([^"]+)">Next<\/a>' + matches_next_page = re.compile(patron_next_page, re.DOTALL).findall(data) + modo = 'continue' + if len(matches_next_page) > 0: + url_next_page = matches_next_page[0] + modo = 'next' + + for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: + url = scrapedurl + title = scrapedtitle + thumbnail = scrapedthumbnail + action = "findvideos" + extra = "" + context = "movie" + year = scrapertools.find_single_match(scrapedthumbnail, r'-(\d{4})') + + if ".com/serie" in url and "/miniseries" not in url: + action = "episodios" + extra = "serie" + context = "tvshow" + + title = scrapertools.find_single_match(title, '([^-]+)') + title = title.replace("Ver online", "", 1).replace("Descarga Serie HD", "", 1).replace("Ver en linea ", "", + 1).strip() + + else: + title = title.replace("Descargar torrent ", "", 1).replace("Descarga Gratis ", "", 1).replace("Descargar Estreno ", "", 1).replace("Pelicula en latino ", "", 1).replace("Descargar Pelicula ", "", 1).replace("Descargar", "", 1).replace("Descarga", "", 1).replace("Bajar", "", 1).strip() + if title.endswith("gratis"): title = title[:-7] + if title.endswith("torrent"): title = title[:-8] + if title.endswith("en HD"): title = title[:-6] + + if title == "": + title = title_alt + context_title = title_alt + show = title_alt + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + ' [' + calidad + "]" + + if not 'array' in title: + itemlist.append(Item(channel=item.channel, action=action, title=title, url=url, thumbnail=thumbnail, + extra = extra, show = context_title, contentTitle=context_title, contentType=context, quality=calidad, + context=["buscar_trailer"], infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / contxt title: " + context_title + " / context: " + context + " / calidad: " + calidad+ " / year: " + year) + + tmdb.set_infoLabels(itemlist, True) + + if url_next_page: + itemlist.append(Item(channel=item.channel, action="listado", title=">> Página siguiente", + url=url_next_page, next_page=next_page, folder=True, + text_color='yellow', text_bold=True, modo = modo, plot = extra, + extra = page_extra)) + return itemlist + +def listado_busqueda(item): + logger.info() + itemlist = [] + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url, post=item.post).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + list_chars = [["ñ", "ñ"]] + + for el in list_chars: + data = re.sub(r"%s" % el[0], el[1], data) + + try: + get, post = scrapertools.find_single_match(data, '<ul class="pagination">.*?<a class="current" href.*?' + '<a\s*href="([^"]+)"(?:\s*onClick=".*?\'([^"]+)\'.*?")') + except: + post = False + + if post: + if "pg" in item.post: + item.post = re.sub(r"pg=(\d+)", "pg=%s" % post, item.post) + else: + item.post += "&pg=%s" % post + + pattern = '<ul class="%s">(.*?)</ul>' % item.pattern + data = scrapertools.get_match(data, pattern) + pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2.*?>(?P<title>.*?)?<\/h2>' + matches = re.compile(pattern, re.DOTALL).findall(data) + + for url, thumb, title in matches: + real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series + if real_title == "": + real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies + real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') + real_title = scrapertools.htmlclean(real_title) + calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series + if calidad == "": + calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies + year = scrapertools.find_single_match(thumb, r'-(\d{4})') + + # fix encoding for title + title = scrapertools.htmlclean(title) + title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") + title = re.sub(r'(Calidad.*?\])', '', title) + + if real_title == "": + real_title = title + if calidad == "": + calidad = title + context = "movie" + url_real = True + + # no mostramos lo que no sean videos + if "juego/" in url: + continue + + # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie + if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: + calidad_mps = "series/" + if "seriehd" in url: + calidad_mps = "series-hd/" + if "serievo" in url: + calidad_mps = "series-vo/" + if "serie-vo" in url: + calidad_mps = "series-vo/" + + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + + if "/0_" not in thumb: + serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') + if len(serieid) > 5: + serieid = "" + else: + serieid = "" + + #detectar si la url creada de tvshow es válida o hay que volver atras + url_tvshow = host + calidad_mps + real_title_mps + "/" + url_id = host + calidad_mps + real_title_mps + "/" + serieid + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_id).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + pattern = '<ul class="%s">(.*?)</ul>' % "buscar-list" # item.pattern + if not scrapertools.find_single_match(data_serie, pattern): + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_tvshow).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + if not scrapertools.find_single_match(data_serie, pattern): + context = "movie" + url_real = False + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + else: + url = url_tvshow + else: + url = url_id + + real_title_mps = real_title_mps.replace("-", " ") + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps + " / context : " + context) + real_title = real_title_mps + + show = real_title + + if ".com/serie" in url and "/miniseries" not in url and url_real: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + context = "tvshow" + + itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) + else: + if config.get_setting("unify"): #Si Titulos Inteligentes SI seleccionados: + title = real_title + + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / show: " + show + " / calidad: " + calidad) + + tmdb.set_infoLabels(itemlist, True) + + if post: + itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", + text_color='yellow', text_bold=True, thumbnail=get_thumb("next.png"))) + + return itemlist + +def findvideos(item): + logger.info() + itemlist = [] + ## Cualquiera de las tres opciones son válidas + # item.url = item.url.replace(".com/",".com/ver-online/") + # item.url = item.url.replace(".com/",".com/descarga-directa/") + item.url = item.url.replace(".com/", ".com/descarga-torrent/") + + # Descarga la página + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("$!", "#!").replace("'", "\"").replace("ñ", "ñ").replace("//pictures", "/pictures") + + title = scrapertools.find_single_match(data, "<h1.*?<strong>([^<]+)<\/strong>.*?<\/h1>") #corregido para adaptarlo a mispelisy.series.com + title += scrapertools.find_single_match(data, "<h1.*?<strong>[^<]+<\/strong>([^<]+)<\/h1>") #corregido para adaptarlo a mispelisy.series.com + #caratula = scrapertools.find_single_match(data, '<div class="entry-left">.*?src="([^"]+)"') + caratula = scrapertools.find_single_match(data, '<h1.*?<img.*?src="([^"]+)') + + patron = 'openTorrent.*?title=".*?class="btn-torrent">.*?function openTorrent.*?href = "(.*?)";' + # escraped torrent + url = scrapertools.find_single_match(data, patron) + + if item.infoLabels['year']: #añadir el año al título general + year = '[%s]' % str(item.infoLabels['year']) + else: + year = "" + + if item.infoLabels['aired'] and item.contentType == "episode": #añadir el año de episodio para series + year = scrapertools.find_single_match(str(item.infoLabels['aired']), r'\/(\d{4})') + year = '[%s]' % year + + title_gen = title + if item.contentType == "episode": #scrapear información duplicada en Series + title = re.sub(r'Temp.*?\[', '[', title) + title = re.sub(r'\[Cap.*?\]', '', title) + title_epi = '%sx%s - %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle) + title_gen = '%s %s, %s' % (title_epi, year, title) + title_torrent = '%s, %s' % (title_epi, item.contentSerieName) + else: + title_torrent = item.contentTitle + + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_torrent = '%s [%s]' %(title_torrent, item.infoLabels['quality']) + else: + title_torrent = '%s (%s)' %(title_torrent, item.infoLabels['quality']) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_gen = '[COLOR gold]**- Título: [/COLOR]%s -**' % (title_gen) + else: + title_gen = '[COLOR gold]Título: [/COLOR]%s' % (title_gen) + if config.get_setting("quit_channel_name", "videolibrary") == 1 and item.contentChannel == "videolibrary": + title_gen = '%s: %s' % (item.channel.capitalize(), title_gen) + itemlist.append(item.clone(title=title_gen, action="", folder=False)) #Título con todos los datos del vídeo + + title = title_torrent + title_torrent = '[COLOR yellow][Torrent]- [/COLOR]%s [online]' % (title_torrent) + if url != "": #Torrent + itemlist.append( + Item(channel=item.channel, action="play", server="torrent", title=title_torrent, fulltitle=title, + url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + + logger.debug("TORRENT: url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) + + # escraped ver vídeos, descargar vídeos un link, múltiples liks + + data = data.replace("http://tumejorserie.com/descargar/url_encript.php?link=", "(") + data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?tumejortorrent.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) + + # Nuevo sistema de scrapeo de servidores creado por Torrentlocula, compatible con otros clones de Newpct1 + patron = '<div class=\"box1\"[^<]+<img src=\"([^<]+)?" style[^<]+><\/div[^<]+<div class="box2">([^<]+)?<\/div[^<]+<div class="box3">([^<]+)?' + patron += '<\/div[^<]+<div class="box4">([^<]+)?<\/div[^<]+<div class="box5"><a href=(.*?)? rel.*?' + patron += '<\/div[^<]+<div class="box6">([^<]+)?<' + + enlaces_ver = re.compile(patron, re.DOTALL).findall(data) + enlaces_descargar = enlaces_ver + #logger.debug(enlaces_ver) + + if len(enlaces_ver) > 0: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Ver: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Ver: [/COLOR]", action="", folder=False)) + + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: + if "Ver" in titulo: + servidor = servidor.replace("streamin", "streaminto") + titulo = title + mostrar_server = True + if config.get_setting("hidepremium"): + mostrar_server = servertools.is_server_enabled(servidor) + titulo = '[COLOR yellow][%s]-[/COLOR] %s [online]' % (servidor.capitalize(), titulo) + logger.debug("VER: url: " + enlace + " / title: " + titulo + " / servidor: " + servidor + " / idioma: " + idioma) + + if mostrar_server: + try: + devuelve = servertools.findvideosbyserver(enlace, servidor) + if devuelve: + enlace = devuelve[0][1] + itemlist.append( + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, + fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + except: + pass + + if len(enlaces_descargar) > 0: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Descargar: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Descargar: [/COLOR]", action="", folder=False)) + + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: + if "Ver" not in titulo: + servidor = servidor.replace("uploaded", "uploadedto") + partes = enlace.split(" ") + titulo = "Descarga " + p = 1 + logger.debug("DESCARGAR: url: " + enlace + " / title: " + titulo + title + " / servidor: " + servidor + " / idioma: " + idioma) + for enlace in partes: + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + p += 1 + mostrar_server = True + if config.get_setting("hidepremium"): + mostrar_server = servertools.is_server_enabled(servidor) + parte_titulo = '[COLOR yellow][%s]-[/COLOR] %s' % (servidor.capitalize(), parte_titulo) + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + parte_titulo = '%s [%s]' %(parte_titulo, item.infoLabels['quality']) + else: + parte_titulo = '%s (%s)' %(parte_titulo, item.infoLabels['quality']) + if mostrar_server: + try: + devuelve = servertools.findvideosbyserver(enlace, servidor) + if devuelve: + enlace = devuelve[0][1] + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, + title=parte_titulo, fulltitle=title, url=enlace, thumbnail=logo, + plot=item.plot, infoLabels=item.infoLabels, folder=False)) + except: + pass + + return itemlist + + +def episodios(item): + logger.info() + itemlist = [] + infoLabels = item.infoLabels + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + calidad = item.quality + + pattern = '<ul class="%s">(.*?)</ul>' % "pagination" # item.pattern + pagination = scrapertools.find_single_match(data, pattern) + if pagination: + pattern = '<li><a href="([^"]+)">Last<\/a>' + full_url = scrapertools.find_single_match(pagination, pattern) + url, last_page = scrapertools.find_single_match(full_url, r'(.*?\/pg\/)(\d+)') + list_pages = [item.url] + for x in range(2, int(last_page) + 1): + response = httptools.downloadpage('%s%s'% (url,x)) + if response.sucess: + list_pages.append("%s%s" % (url, x)) + else: + list_pages = [item.url] + + for index, page in enumerate(list_pages): + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com + pattern = '<ul class="%s">(.*?)</ul>' % "buscar-list" # item.pattern + if scrapertools.find_single_match(data, pattern): + data = scrapertools.get_match(data, pattern) + else: + logger.debug(item) + logger.debug("data: " + data) + return itemlist + + if "pelisyseries.com" in host: + pattern = '<li[^>]*><div class.*?src="(?P<thumb>[^"]+)?".*?<a class.*?href="(?P<url>[^"]+).*?<h3[^>]+>(?P<info>.*?)?<\/h3>.*?<\/li>' + else: + pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2[^>]+>(?P<info>.*?)?<\/h2>' + matches = re.compile(pattern, re.DOTALL).findall(data) + #logger.debug("patron: " + pattern) + #logger.debug(matches) + + season = "1" + + for url, thumb, info in matches: + + if "pelisyseries.com" in host: + interm = url + url = thumb + thumb = interm + + if "<span" in info: # new style + pattern = ".*?[^>]+>.*?Temporada\s*(?P<season>\d+)?.*?Capitulo(?:s)?\s*(?P<episode>\d+)?" \ + "(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>" \ + "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + if "Especial" in info: # Capitulos Especiales + pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + r = re.compile(pattern) + match = [m.groupdict() for m in r.finditer(info)][0] + + if match['season'] is None: match['season'] = season + if match['episode'] is None: match['episode'] = "0" + if match['quality']: + item.quality = match['quality'] + + if match["episode2"]: + multi = True + title = "%s (%sx%s-%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + else: + multi = False + title = "%s (%sx%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + + else: # old style + if scrapertools.find_single_match(info, '\[\d{3}\]'): + info = re.sub(r'\[(\d{3}\])', r'[Cap.\1', info) + elif scrapertools.find_single_match(info, '\[Cap.\d{2}_\d{2}\]'): + info = re.sub(r'\[Cap.(\d{2})_(\d{2})\]', r'[Cap.1\1_1\2]', info) + elif scrapertools.find_single_match(info, '\[Cap.([A-Za-z]+)\]'): + info = re.sub(r'\[Cap.([A-Za-z]+)\]', '[Cap.100]', info) + if scrapertools.find_single_match(info, '\[Cap.\d{2,3}'): + pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ + "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" + elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): + pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" + else: + logger.debug("patron episodio: " + pattern) + logger.debug(info) + continue + + r = re.compile(pattern) + match = [m.groupdict() for m in r.finditer(info)][0] + + str_lang = "" + if match['quality']: + item.quality = match['quality'] + + if match["lang"] is not None: + str_lang = "[%s]" % match["lang"] + item.quality = "%s %s" % (item.quality, match['lang']) + + if match["season2"] and match["episode2"]: + multi = True + if match["season"] == match["season2"]: + + title = "%s (%sx%s-%s) %s" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + else: + title = "%s (%sx%s-%sx%s) %s" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + else: + title = "%s (%sx%s) %s" % (item.show, match["season"], match["episode"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + multi = False + + season = match['season'] + episode = match['episode'] + logger.debug("title: " + title + " / url: " + url + " / calidad: " + item.quality + " / multi: " + str(multi) + " / Season: " + str(season) + " / EpisodeNumber: " + str(episode)) + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, + quality=item.quality, multi=multi, contentSeason=season, + contentEpisodeNumber=episode, infoLabels = infoLabels)) + # order list + #tmdb.set_infoLabels(itemlist, True) + tmdb.set_infoLabels_itemlist(itemlist, seekTmdb = True) + if len(itemlist) > 1: + itemlist = sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber))) + + if config.get_videolibrary_support() and len(itemlist) > 0: + itemlist.append( + item.clone(title="Añadir esta serie a la videoteca", action="add_serie_to_library", extra="episodios", quality=calidad)) + + return itemlist + +def search(item, texto): + logger.info("search:" + texto) + # texto = texto.replace(" ", "+") + + try: + item.post = "q=%s" % texto + item.pattern = "buscar-list" + itemlist = listado_busqueda(item) + + return itemlist + + # Se captura la excepción, para no interrumpir al buscador global si un canal falla + except: + import sys + for line in sys.exc_info(): + logger.error("%s" % line) + return [] + +def newest(categoria): + logger.info() + itemlist = [] + item = Item() + try: + item.extra = 'pelilist' + if categoria == 'torrent': + item.url = host+'peliculas/' + + itemlist = listado(item) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + item.url = host+'series/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'peliculas 4k': + item.url = host+'peliculas-hd/4kultrahd/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'anime': + item.url = host+'anime/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'documentales': + item.url = host+'documentales/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + # Se captura la excepción, para no interrumpir al canal novedades si un canal falla + except: + import sys + for line in sys.exc_info(): + logger.error("{0}".format(line)) + return [] + + return itemlist diff --git a/plugin.video.alfa/channels/tvsinpagar.json b/plugin.video.alfa/channels/tvsinpagar.json new file mode 100644 index 00000000..f82e4d5e --- /dev/null +++ b/plugin.video.alfa/channels/tvsinpagar.json @@ -0,0 +1,58 @@ +{ + "id": "tvsinpagar", + "name": "Tvsinpagar", + "active": true, + "adult": false, + "language": ["cast", "lat"], + "thumbnail": "tvsinpagar.png", + "banner": "tvsinpagar.png", + "categories": [ + "movie", + "tvshow", + "anime", + "torrent", + "documentary" + ], + "settings": [ + { + "id": "include_in_global_search", + "type": "bool", + "label": "Incluir en busqueda global", + "default": false, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_peliculas", + "type": "bool", + "label": "Incluir en Novedades - Peliculas", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_series", + "type": "bool", + "label": "Incluir en Novedades - Episodios de series", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_torrent", + "type": "bool", + "label": "Incluir en Novedades - Torrent", + "default": true, + "enabled": true, + "visible": true + }, + { + "id": "include_in_newest_4k", + "type": "bool", + "label": "Incluir en Novedades - 4K", + "default": true, + "enabled": true, + "visible": true + } + ] +} \ No newline at end of file diff --git a/plugin.video.alfa/channels/tvsinpagar.py b/plugin.video.alfa/channels/tvsinpagar.py new file mode 100644 index 00000000..56898ea1 --- /dev/null +++ b/plugin.video.alfa/channels/tvsinpagar.py @@ -0,0 +1,656 @@ +# -*- coding: utf-8 -*- + +import re + +from channelselector import get_thumb +from core import httptools +from core import scrapertools +from core import servertools +from core.item import Item +from platformcode import config, logger +from core import tmdb + +host = 'http://www.tvsinpagar.com/' + +def mainlist(item): + logger.info() + + itemlist = [] + + thumb_pelis=get_thumb("channels_movie.png") + thumb_series=get_thumb("channels_tvshow.png") + thumb_search = get_thumb("search.png") + + itemlist.append(Item(channel=item.channel, action="submenu", title="Películas", url=host, + extra="peliculas", thumbnail=thumb_pelis )) + + itemlist.append(Item(channel=item.channel, action="submenu", title="Series", url=host, extra="series", + thumbnail=thumb_series)) + + itemlist.append(Item(channel=item.channel, action="submenu", title="Documentales", url=host, extra="varios", + thumbnail=thumb_series)) + itemlist.append( + Item(channel=item.channel, action="search", title="Buscar", url=host + "buscar", thumbnail=thumb_search)) + + return itemlist + +def submenu(item): + logger.info() + itemlist = [] + + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("'", "\"").replace("/series\"", "/series/\"") #Compatibilidad con mispelisy.series.com + + patron = '<li><a href="http://(?:www.)?tvsinpagar.com/' + item.extra + '/">.*?<ul.*?>(.*?)</ul>' + if "pelisyseries.com" in host and item.extra == "varios": #compatibilidad con mispelisy.series.com + data = '<a href="http://tvsinpagar.com/varios/" title="Documentales"><i class="icon-rocket"></i> Documentales</a>' + else: + data = scrapertools.get_match(data, patron) + + patron = '<.*?href="([^"]+)".*?>([^>]+)</a>' + matches = re.compile(patron, re.DOTALL).findall(data) + + for scrapedurl, scrapedtitle in matches: + title = scrapedtitle.strip() + url = scrapedurl + + itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra="pelilist")) + itemlist.append( + Item(channel=item.channel, action="alfabeto", title=title + " [A-Z]", url=url, extra="pelilist")) + + if item.extra == "peliculas": + itemlist.append(Item(channel=item.channel, action="listado", title="Películas 4K", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) + itemlist.append( + Item(channel=item.channel, action="alfabeto", title="Películas 4K" + " [A-Z]", url=host + "peliculas-hd/4kultrahd/", extra="pelilist")) + + return itemlist + + +def alfabeto(item): + logger.info() + itemlist = [] + + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + patron = '<ul class="alfabeto">(.*?)</ul>' + data = scrapertools.get_match(data, patron) + + patron = '<a href="([^"]+)"[^>]+>([^>]+)</a>' + matches = re.compile(patron, re.DOTALL).findall(data) + + for scrapedurl, scrapedtitle in matches: + title = scrapedtitle.upper() + url = scrapedurl + + itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, extra=item.extra)) + + return itemlist + + +def listado(item): + logger.info() + itemlist = [] + url_next_page ='' + + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + #data = httptools.downloadpage(item.url).data + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + if item.modo != 'next' or item.modo =='': + patron = '<ul class="' + item.extra + '">(.*?)</ul>' + fichas = scrapertools.get_match(data, patron) + page_extra = item.extra + else: + fichas = data + page_extra = item.extra + + patron = '<a href="([^"]+).*?' # la url + patron += 'title="([^"]+).*?' # el titulo + patron += '<img.*?src="([^"]+)"[^>]+>.*?' # el thumbnail + patron += '<h2.*?>(.*?)?<\/h2>' # titulo alternativo + patron += '<span>([^<].*?)?<' # la calidad + #logger.debug("patron: " + patron + " / fichas: " + fichas) + matches = re.compile(patron, re.DOTALL).findall(fichas) + + # Paginacion + if item.next_page != 'b': + if len(matches) > 30: + url_next_page = item.url + matches = matches[:30] + next_page = 'b' + modo = 'continue' + else: + matches = matches[30:] + next_page = 'a' + patron_next_page = '<a href="([^"]+)">Next<\/a>' + matches_next_page = re.compile(patron_next_page, re.DOTALL).findall(data) + modo = 'continue' + if len(matches_next_page) > 0: + url_next_page = matches_next_page[0] + modo = 'next' + + for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: + url = scrapedurl + title = scrapedtitle + thumbnail = scrapedthumbnail + action = "findvideos" + extra = "" + context = "movie" + year = scrapertools.find_single_match(scrapedthumbnail, r'-(\d{4})') + + if ".com/serie" in url and "/miniseries" not in url: + action = "episodios" + extra = "serie" + context = "tvshow" + + title = scrapertools.find_single_match(title, '([^-]+)') + title = title.replace("Ver online", "", 1).replace("Descarga Serie HD", "", 1).replace("Ver en linea ", "", + 1).strip() + + else: + title = title.replace("Descargar torrent ", "", 1).replace("Descarga Gratis ", "", 1).replace("Descargar Estreno ", "", 1).replace("Pelicula en latino ", "", 1).replace("Descargar Pelicula ", "", 1).replace("Descargar", "", 1).replace("Descarga", "", 1).replace("Bajar", "", 1).strip() + if title.endswith("gratis"): title = title[:-7] + if title.endswith("torrent"): title = title[:-8] + if title.endswith("en HD"): title = title[:-6] + + if title == "": + title = title_alt + context_title = title_alt + show = title_alt + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + ' [' + calidad + "]" + + if not 'array' in title: + itemlist.append(Item(channel=item.channel, action=action, title=title, url=url, thumbnail=thumbnail, + extra = extra, show = context_title, contentTitle=context_title, contentType=context, quality=calidad, + context=["buscar_trailer"], infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / contxt title: " + context_title + " / context: " + context + " / calidad: " + calidad+ " / year: " + year) + + tmdb.set_infoLabels(itemlist, True) + + if url_next_page: + itemlist.append(Item(channel=item.channel, action="listado", title=">> Página siguiente", + url=url_next_page, next_page=next_page, folder=True, + text_color='yellow', text_bold=True, modo = modo, plot = extra, + extra = page_extra)) + return itemlist + +def listado_busqueda(item): + logger.info() + itemlist = [] + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url, post=item.post).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + + list_chars = [["ñ", "ñ"]] + + for el in list_chars: + data = re.sub(r"%s" % el[0], el[1], data) + + try: + get, post = scrapertools.find_single_match(data, '<ul class="pagination">.*?<a class="current" href.*?' + '<a\s*href="([^"]+)"(?:\s*onClick=".*?\'([^"]+)\'.*?")') + except: + post = False + + if post: + if "pg" in item.post: + item.post = re.sub(r"pg=(\d+)", "pg=%s" % post, item.post) + else: + item.post += "&pg=%s" % post + + pattern = '<ul class="%s">(.*?)</ul>' % item.pattern + data = scrapertools.get_match(data, pattern) + pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2.*?>(?P<title>.*?)?<\/h2>' + matches = re.compile(pattern, re.DOTALL).findall(data) + + for url, thumb, title in matches: + real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series + if real_title == "": + real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies + real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') + real_title = scrapertools.htmlclean(real_title) + calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series + if calidad == "": + calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies + year = scrapertools.find_single_match(thumb, r'-(\d{4})') + + # fix encoding for title + title = scrapertools.htmlclean(title) + title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") + title = re.sub(r'(Calidad.*?\])', '', title) + + if real_title == "": + real_title = title + if calidad == "": + calidad = title + context = "movie" + url_real = True + + # no mostramos lo que no sean videos + if "juego/" in url: + continue + + # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie + if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: + calidad_mps = "series/" + if "seriehd" in url: + calidad_mps = "series-hd/" + if "serievo" in url: + calidad_mps = "series-vo/" + if "serie-vo" in url: + calidad_mps = "series-vo/" + + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + + if "/0_" not in thumb: + serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') + if len(serieid) > 5: + serieid = "" + else: + serieid = "" + + #detectar si la url creada de tvshow es válida o hay que volver atras + url_tvshow = host + calidad_mps + real_title_mps + "/" + url_id = host + calidad_mps + real_title_mps + "/" + serieid + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_id).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + pattern = '<ul class="%s">(.*?)</ul>' % "buscar-list" # item.pattern + if not scrapertools.find_single_match(data_serie, pattern): + data_serie = data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(url_tvshow).data) + data_serie = unicode(data_serie, "iso-8859-1", errors="replace").encode("utf-8") + data_serie = data_serie.replace("chapters", "buscar-list") + if not scrapertools.find_single_match(data_serie, pattern): + context = "movie" + url_real = False + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + else: + url = url_tvshow + else: + url = url_id + + real_title_mps = real_title_mps.replace("-", " ") + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / real_title_mps: " + real_title_mps + " / calidad_mps : " + calidad_mps + " / context : " + context) + real_title = real_title_mps + + show = real_title + + if ".com/serie" in url and "/miniseries" not in url and url_real: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + if calidad: + title = title + '[' + calidad + "]" + context = "tvshow" + + itemlist.append(Item(channel=item.channel, action="episodios", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, extra="serie", context=["buscar_trailer"], contentType=context, contentTitle=real_title, contentSerieName=real_title, infoLabels= {'year':year})) + else: + if config.get_setting("unify"): #Si Titulos Inteligentes SI seleccionados: + title = real_title + + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, quality=calidad, + show=show, context=["buscar_trailer"], contentType=context, contentTitle=real_title, infoLabels= {'year':year})) + + logger.debug("url: " + url + " / title: " + title + " / real_title: " + real_title + " / show: " + show + " / calidad: " + calidad) + + tmdb.set_infoLabels(itemlist, True) + + if post: + itemlist.append(item.clone(channel=item.channel, action="listado_busqueda", title=">> Página siguiente", + text_color='yellow', text_bold=True, thumbnail=get_thumb("next.png"))) + + return itemlist + +def findvideos(item): + logger.info() + itemlist = [] + ## Cualquiera de las tres opciones son válidas + # item.url = item.url.replace(".com/",".com/ver-online/") + # item.url = item.url.replace(".com/",".com/descarga-directa/") + item.url = item.url.replace(".com/", ".com/descarga-torrent/") + + # Descarga la página + data = re.sub(r"\n|\r|\t|\s{2}|(<!--.*?-->)", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("$!", "#!").replace("'", "\"").replace("ñ", "ñ").replace("//pictures", "/pictures") + + title = scrapertools.find_single_match(data, "<h1.*?<strong>([^<]+)<\/strong>.*?<\/h1>") #corregido para adaptarlo a mispelisy.series.com + title += scrapertools.find_single_match(data, "<h1.*?<strong>[^<]+<\/strong>([^<]+)<\/h1>") #corregido para adaptarlo a mispelisy.series.com + #caratula = scrapertools.find_single_match(data, '<div class="entry-left">.*?src="([^"]+)"') + caratula = scrapertools.find_single_match(data, '<h1.*?<img.*?src="([^"]+)') + + patron = 'openTorrent.*?title=".*?class="btn-torrent">.*?function openTorrent.*?href = "(.*?)";' + # escraped torrent + url = scrapertools.find_single_match(data, patron) + + if item.infoLabels['year']: #añadir el año al título general + year = '[%s]' % str(item.infoLabels['year']) + else: + year = "" + + if item.infoLabels['aired'] and item.contentType == "episode": #añadir el año de episodio para series + year = scrapertools.find_single_match(str(item.infoLabels['aired']), r'\/(\d{4})') + year = '[%s]' % year + + title_gen = title + if item.contentType == "episode": #scrapear información duplicada en Series + title = re.sub(r'Temp.*?\[', '[', title) + title = re.sub(r'\[Cap.*?\]', '', title) + title_epi = '%sx%s - %s' % (str(item.contentSeason), str(item.contentEpisodeNumber), item.contentTitle) + title_gen = '%s %s, %s' % (title_epi, year, title) + title_torrent = '%s, %s' % (title_epi, item.contentSerieName) + else: + title_torrent = item.contentTitle + + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_torrent = '%s [%s]' %(title_torrent, item.infoLabels['quality']) + else: + title_torrent = '%s (%s)' %(title_torrent, item.infoLabels['quality']) + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + title_gen = '[COLOR gold]**- Título: [/COLOR]%s -**' % (title_gen) + else: + title_gen = '[COLOR gold]Título: [/COLOR]%s' % (title_gen) + if config.get_setting("quit_channel_name", "videolibrary") == 1 and item.contentChannel == "videolibrary": + title_gen = '%s: %s' % (item.channel.capitalize(), title_gen) + itemlist.append(item.clone(title=title_gen, action="", folder=False)) #Título con todos los datos del vídeo + + title = title_torrent + title_torrent = '[COLOR yellow][Torrent]- [/COLOR]%s [online]' % (title_torrent) + if url != "": #Torrent + itemlist.append( + Item(channel=item.channel, action="play", server="torrent", title=title_torrent, fulltitle=title, + url=url, thumbnail=caratula, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + + logger.debug("TORRENT: url: " + url + " / title: " + title + " / calidad: " + item.quality + " / context: " + str(item.context)) + + # escraped ver vídeos, descargar vídeos un link, múltiples liks + + data = data.replace("http://tumejorserie.com/descargar/url_encript.php?link=", "(") + data = re.sub(r'javascript:;" onClick="popup\("http:\/\/(?:www.)?tvsinpagar.com\/\w{1,9}\/library\/include\/ajax\/get_modallinks.php\?links=', "", data) + + # Nuevo sistema de scrapeo de servidores creado por Torrentlocula, compatible con otros clones de Newpct1 + patron = '<div class=\"box1\"[^<]+<img src=\"([^<]+)?" style[^<]+><\/div[^<]+<div class="box2">([^<]+)?<\/div[^<]+<div class="box3">([^<]+)?' + patron += '<\/div[^<]+<div class="box4">([^<]+)?<\/div[^<]+<div class="box5"><a href=(.*?)? rel.*?' + patron += '<\/div[^<]+<div class="box6">([^<]+)?<' + + enlaces_ver = re.compile(patron, re.DOTALL).findall(data) + enlaces_descargar = enlaces_ver + #logger.debug(enlaces_ver) + + if len(enlaces_ver) > 0: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Ver: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Ver: [/COLOR]", action="", folder=False)) + + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_ver: + if "Ver" in titulo: + servidor = servidor.replace("streamin", "streaminto") + titulo = title + mostrar_server = True + if config.get_setting("hidepremium"): + mostrar_server = servertools.is_server_enabled(servidor) + titulo = '[COLOR yellow][%s]-[/COLOR] %s [online]' % (servidor.capitalize(), titulo) + logger.debug("VER: url: " + enlace + " / title: " + titulo + " / servidor: " + servidor + " / idioma: " + idioma) + + if mostrar_server: + try: + devuelve = servertools.findvideosbyserver(enlace, servidor) + if devuelve: + enlace = devuelve[0][1] + itemlist.append( + Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, title=titulo, + fulltitle=title, url=enlace, thumbnail=logo, plot=item.plot, infoLabels=item.infoLabels, folder=False)) + except: + pass + + if len(enlaces_descargar) > 0: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + itemlist.append(item.clone(title="[COLOR gold]**- Enlaces Descargar: -**[/COLOR]", action="", folder=False)) + else: + itemlist.append(item.clone(title="[COLOR gold] Enlaces Descargar: [/COLOR]", action="", folder=False)) + + for logo, servidor, idioma, calidad, enlace, titulo in enlaces_descargar: + if "Ver" not in titulo: + servidor = servidor.replace("uploaded", "uploadedto") + partes = enlace.split(" ") + titulo = "Descarga " + p = 1 + logger.debug("DESCARGAR: url: " + enlace + " / title: " + titulo + title + " / servidor: " + servidor + " / idioma: " + idioma) + for enlace in partes: + parte_titulo = titulo + " (%s/%s)" % (p, len(partes)) + p += 1 + mostrar_server = True + if config.get_setting("hidepremium"): + mostrar_server = servertools.is_server_enabled(servidor) + parte_titulo = '[COLOR yellow][%s]-[/COLOR] %s' % (servidor.capitalize(), parte_titulo) + if item.infoLabels['quality']: + if not config.get_setting("unify"): #Si Titulos Inteligentes NO seleccionados: + parte_titulo = '%s [%s]' %(parte_titulo, item.infoLabels['quality']) + else: + parte_titulo = '%s (%s)' %(parte_titulo, item.infoLabels['quality']) + if mostrar_server: + try: + devuelve = servertools.findvideosbyserver(enlace, servidor) + if devuelve: + enlace = devuelve[0][1] + itemlist.append(Item(fanart=item.fanart, channel=item.channel, action="play", server=servidor, + title=parte_titulo, fulltitle=title, url=enlace, thumbnail=logo, + plot=item.plot, infoLabels=item.infoLabels, folder=False)) + except: + pass + + return itemlist + + +def episodios(item): + logger.info() + itemlist = [] + infoLabels = item.infoLabels + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(item.url).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + calidad = item.quality + + pattern = '<ul class="%s">(.*?)</ul>' % "pagination" # item.pattern + pagination = scrapertools.find_single_match(data, pattern) + if pagination: + pattern = '<li><a href="([^"]+)">Last<\/a>' + full_url = scrapertools.find_single_match(pagination, pattern) + url, last_page = scrapertools.find_single_match(full_url, r'(.*?\/pg\/)(\d+)') + list_pages = [item.url] + for x in range(2, int(last_page) + 1): + response = httptools.downloadpage('%s%s'% (url,x)) + if response.sucess: + list_pages.append("%s%s" % (url, x)) + else: + list_pages = [item.url] + + for index, page in enumerate(list_pages): + data = re.sub(r"\n|\r|\t|\s{2,}", "", httptools.downloadpage(page).data) + data = unicode(data, "iso-8859-1", errors="replace").encode("utf-8") + data = data.replace("chapters", "buscar-list") #Compatibilidad con mispelisy.series.com + pattern = '<ul class="%s">(.*?)</ul>' % "buscar-list" # item.pattern + if scrapertools.find_single_match(data, pattern): + data = scrapertools.get_match(data, pattern) + else: + logger.debug(item) + logger.debug("data: " + data) + return itemlist + + if "pelisyseries.com" in host: + pattern = '<li[^>]*><div class.*?src="(?P<thumb>[^"]+)?".*?<a class.*?href="(?P<url>[^"]+).*?<h3[^>]+>(?P<info>.*?)?<\/h3>.*?<\/li>' + else: + pattern = '<li[^>]*><a href="(?P<url>[^"]+).*?<img.*?src="(?P<thumb>[^"]+)?".*?<h2[^>]+>(?P<info>.*?)?<\/h2>' + matches = re.compile(pattern, re.DOTALL).findall(data) + #logger.debug("patron: " + pattern) + #logger.debug(matches) + + season = "1" + + for url, thumb, info in matches: + + if "pelisyseries.com" in host: + interm = url + url = thumb + thumb = interm + + if "<span" in info: # new style + pattern = ".*?[^>]+>.*?Temporada\s*(?P<season>\d+)?.*?Capitulo(?:s)?\s*(?P<episode>\d+)?" \ + "(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>" \ + "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + if "Especial" in info: # Capitulos Especiales + pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + r = re.compile(pattern) + match = [m.groupdict() for m in r.finditer(info)][0] + + if match['season'] is None: match['season'] = season + if match['episode'] is None: match['episode'] = "0" + if match['quality']: + item.quality = match['quality'] + + if match["episode2"]: + multi = True + title = "%s (%sx%s-%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + str(match["episode2"]).zfill(2), match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + else: + multi = False + title = "%s (%sx%s) [%s]" % (item.show, match["season"], str(match["episode"]).zfill(2), + match["lang"]) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + + else: # old style + if scrapertools.find_single_match(info, '\[\d{3}\]'): + info = re.sub(r'\[(\d{3}\])', r'[Cap.\1', info) + elif scrapertools.find_single_match(info, '\[Cap.\d{2}_\d{2}\]'): + info = re.sub(r'\[Cap.(\d{2})_(\d{2})\]', r'[Cap.1\1_1\2]', info) + elif scrapertools.find_single_match(info, '\[Cap.([A-Za-z]+)\]'): + info = re.sub(r'\[Cap.([A-Za-z]+)\]', '[Cap.100]', info) + if scrapertools.find_single_match(info, '\[Cap.\d{2,3}'): + pattern = "\[(?P<quality>.*?)\].*?\[Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)" \ + "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" + elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): + pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" + else: + logger.debug("patron episodio: " + pattern) + logger.debug(info) + continue + + r = re.compile(pattern) + match = [m.groupdict() for m in r.finditer(info)][0] + + str_lang = "" + if match['quality']: + item.quality = match['quality'] + + if match["lang"] is not None: + str_lang = "[%s]" % match["lang"] + item.quality = "%s %s" % (item.quality, match['lang']) + + if match["season2"] and match["episode2"]: + multi = True + if match["season"] == match["season2"]: + + title = "%s (%sx%s-%s) %s" % (item.show, match["season"], match["episode"], + match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + else: + title = "%s (%sx%s-%sx%s) %s" % (item.show, match["season"], match["episode"], + match["season2"], match["episode2"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + else: + title = "%s (%sx%s) %s" % (item.show, match["season"], match["episode"], str_lang) + if not config.get_setting("unify") and match["quality"]: #Si Titulos Inteligentes NO seleccionados: + title = "%s[%s]" % (title, match["quality"]) + multi = False + + season = match['season'] + episode = match['episode'] + logger.debug("title: " + title + " / url: " + url + " / calidad: " + item.quality + " / multi: " + str(multi) + " / Season: " + str(season) + " / EpisodeNumber: " + str(episode)) + itemlist.append(Item(channel=item.channel, action="findvideos", title=title, url=url, thumbnail=thumb, + quality=item.quality, multi=multi, contentSeason=season, + contentEpisodeNumber=episode, infoLabels = infoLabels)) + # order list + #tmdb.set_infoLabels(itemlist, True) + tmdb.set_infoLabels_itemlist(itemlist, seekTmdb = True) + if len(itemlist) > 1: + itemlist = sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber))) + + if config.get_videolibrary_support() and len(itemlist) > 0: + itemlist.append( + item.clone(title="Añadir esta serie a la videoteca", action="add_serie_to_library", extra="episodios", quality=calidad)) + + return itemlist + +def search(item, texto): + logger.info("search:" + texto) + # texto = texto.replace(" ", "+") + + try: + item.post = "q=%s" % texto + item.pattern = "buscar-list" + itemlist = listado_busqueda(item) + + return itemlist + + # Se captura la excepción, para no interrumpir al buscador global si un canal falla + except: + import sys + for line in sys.exc_info(): + logger.error("%s" % line) + return [] + +def newest(categoria): + logger.info() + itemlist = [] + item = Item() + try: + item.extra = 'pelilist' + if categoria == 'torrent': + item.url = host+'peliculas/' + + itemlist = listado(item) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + item.url = host+'series/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'peliculas 4k': + item.url = host+'peliculas-hd/4kultrahd/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'anime': + item.url = host+'anime/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + if categoria == 'documentales': + item.url = host+'documentales/' + itemlist.extend(listado(item)) + if itemlist[-1].title == ">> Página siguiente": + itemlist.pop() + + # Se captura la excepción, para no interrumpir al canal novedades si un canal falla + except: + import sys + for line in sys.exc_info(): + logger.error("{0}".format(line)) + return [] + + return itemlist From 87f67acf3344087b243142cd39f6bf942456e482 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Sun, 8 Apr 2018 20:40:15 +0200 Subject: [PATCH 20/22] Thumbs y banners de Tumejortorrent y Tvsinpagar --- .../media/channels/banner/tumejortorrent.png | Bin 0 -> 42454 bytes .../media/channels/banner/tvsinpagar.png | Bin 0 -> 32411 bytes .../media/channels/thumb/tumejortorrent.png | Bin 0 -> 14905 bytes .../media/channels/thumb/tvsinpagar.png | Bin 0 -> 28669 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugin.video.alfa/resources/media/channels/banner/tumejortorrent.png create mode 100644 plugin.video.alfa/resources/media/channels/banner/tvsinpagar.png create mode 100644 plugin.video.alfa/resources/media/channels/thumb/tumejortorrent.png create mode 100644 plugin.video.alfa/resources/media/channels/thumb/tvsinpagar.png diff --git a/plugin.video.alfa/resources/media/channels/banner/tumejortorrent.png b/plugin.video.alfa/resources/media/channels/banner/tumejortorrent.png new file mode 100644 index 0000000000000000000000000000000000000000..1910766d22cfcb9d181a02adf869d16664cd41a2 GIT binary patch literal 42454 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 literal 0 HcmV?d00001 diff --git a/plugin.video.alfa/resources/media/channels/banner/tvsinpagar.png b/plugin.video.alfa/resources/media/channels/banner/tvsinpagar.png new file mode 100644 index 0000000000000000000000000000000000000000..ea43d48763d27001a79329685a3985f356fe19b1 GIT binary patch literal 32411 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 F{{ftSL&N|8 literal 0 HcmV?d00001 diff --git a/plugin.video.alfa/resources/media/channels/thumb/tumejortorrent.png b/plugin.video.alfa/resources/media/channels/thumb/tumejortorrent.png new file mode 100644 index 0000000000000000000000000000000000000000..2e86f13ebf406ee34376842d8599ab4517760f45 GIT binary patch literal 14905 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 literal 0 HcmV?d00001 diff --git a/plugin.video.alfa/resources/media/channels/thumb/tvsinpagar.png b/plugin.video.alfa/resources/media/channels/thumb/tvsinpagar.png new file mode 100644 index 0000000000000000000000000000000000000000..103e4d8be9dbda47461b69c4c3bae1026abdcf94 GIT binary patch literal 28669 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ν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 literal 0 HcmV?d00001 From fc5fb473dd7e9d0590dc1492b9415cdeafc5327c Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Mon, 9 Apr 2018 15:51:09 +0200 Subject: [PATCH 21/22] Clones de Newpct1: mejora estabilidad en Episodios MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evita el error de los canales cuando llega una Serie con títulos de capítulos con formatos distintos de los previstos. En este caso crea un formáto de título básico para que prosiga el programa de la forma prevista. --- plugin.video.alfa/channels/descargas2020.py | 38 +++++++++++------- plugin.video.alfa/channels/mispelisyseries.py | 40 ++++++++++++------- plugin.video.alfa/channels/torrentlocura.py | 38 +++++++++++------- plugin.video.alfa/channels/torrentrapid.py | 38 +++++++++++------- plugin.video.alfa/channels/tumejortorrent.py | 40 ++++++++++++------- plugin.video.alfa/channels/tvsinpagar.py | 40 ++++++++++++------- 6 files changed, 147 insertions(+), 87 deletions(-) diff --git a/plugin.video.alfa/channels/descargas2020.py b/plugin.video.alfa/channels/descargas2020.py index 7de30a6b..4008919a 100644 --- a/plugin.video.alfa/channels/descargas2020.py +++ b/plugin.video.alfa/channels/descargas2020.py @@ -133,7 +133,8 @@ def listado(item): for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: url = scrapedurl - title = scrapedtitle + title = scrapedtitle.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") + title_alt = title_alt.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") thumbnail = scrapedthumbnail action = "findvideos" extra = "" @@ -209,10 +210,11 @@ def listado_busqueda(item): for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series - if real_title == "": + if not real_title: real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') real_title = scrapertools.htmlclean(real_title) + real_title = real_title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series if calidad == "": calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies @@ -220,9 +222,8 @@ def listado_busqueda(item): # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") title = re.sub(r'(Calidad.*?\])', '', title) - + title = title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") if real_title == "": real_title = title if calidad == "": @@ -236,18 +237,22 @@ def listado_busqueda(item): # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: - calidad_mps = "series/" if "seriehd" in url: calidad_mps = "series-hd/" - if "serievo" in url: + elif "serievo" in url: calidad_mps = "series-vo/" - if "serie-vo" in url: + elif "serie-vo" in url: calidad_mps = "series-vo/" + else: + calidad_mps = "series/" - real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) - real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + if "no_image" in thumb: + real_title_mps = title + else: + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) - if "/0_" not in thumb: + if "/0_" not in thumb and not "no_image" in thumb: serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') if len(serieid) > 5: serieid = "" @@ -507,6 +512,11 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioNEW: " + pattern) + logger.debug(info) + info = '><strong>%sTemporada %s Capitulo 0</strong> - <span >Español Castellano</span> Calidad <span >[%s]</span>' % (item.contentTitle, season, item.infoLabels['quality']) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] @@ -540,11 +550,11 @@ def episodios(item): "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" - else: - logger.debug("patron episodio: " + pattern) + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioOLD: " + pattern) logger.debug(info) - continue - + info = '%s [%s][Cap.%s00][Español]' % (item.contentTitle, item.infoLabels['quality'], season) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] diff --git a/plugin.video.alfa/channels/mispelisyseries.py b/plugin.video.alfa/channels/mispelisyseries.py index f58462a8..7c6ae65b 100644 --- a/plugin.video.alfa/channels/mispelisyseries.py +++ b/plugin.video.alfa/channels/mispelisyseries.py @@ -10,7 +10,7 @@ from core.item import Item from platformcode import config, logger from core import tmdb -host = 'http://www.mispelisyseries.com/' +host = 'http://mispelisyseries.com/' def mainlist(item): logger.info() @@ -133,7 +133,8 @@ def listado(item): for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: url = scrapedurl - title = scrapedtitle + title = scrapedtitle.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") + title_alt = title_alt.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") thumbnail = scrapedthumbnail action = "findvideos" extra = "" @@ -209,10 +210,11 @@ def listado_busqueda(item): for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series - if real_title == "": + if not real_title: real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') real_title = scrapertools.htmlclean(real_title) + real_title = real_title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series if calidad == "": calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies @@ -220,9 +222,8 @@ def listado_busqueda(item): # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") title = re.sub(r'(Calidad.*?\])', '', title) - + title = title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") if real_title == "": real_title = title if calidad == "": @@ -236,18 +237,22 @@ def listado_busqueda(item): # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: - calidad_mps = "series/" if "seriehd" in url: calidad_mps = "series-hd/" - if "serievo" in url: + elif "serievo" in url: calidad_mps = "series-vo/" - if "serie-vo" in url: + elif "serie-vo" in url: calidad_mps = "series-vo/" + else: + calidad_mps = "series/" - real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) - real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + if "no_image" in thumb: + real_title_mps = title + else: + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) - if "/0_" not in thumb: + if "/0_" not in thumb and not "no_image" in thumb: serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') if len(serieid) > 5: serieid = "" @@ -507,6 +512,11 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioNEW: " + pattern) + logger.debug(info) + info = '><strong>%sTemporada %s Capitulo 0</strong> - <span >Español Castellano</span> Calidad <span >[%s]</span>' % (item.contentTitle, season, item.infoLabels['quality']) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] @@ -540,11 +550,11 @@ def episodios(item): "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" - else: - logger.debug("patron episodio: " + pattern) + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioOLD: " + pattern) logger.debug(info) - continue - + info = '%s [%s][Cap.%s00][Español]' % (item.contentTitle, item.infoLabels['quality'], season) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] diff --git a/plugin.video.alfa/channels/torrentlocura.py b/plugin.video.alfa/channels/torrentlocura.py index 2d3d50c6..78ebcfa8 100755 --- a/plugin.video.alfa/channels/torrentlocura.py +++ b/plugin.video.alfa/channels/torrentlocura.py @@ -133,7 +133,8 @@ def listado(item): for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: url = scrapedurl - title = scrapedtitle + title = scrapedtitle.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") + title_alt = title_alt.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") thumbnail = scrapedthumbnail action = "findvideos" extra = "" @@ -209,10 +210,11 @@ def listado_busqueda(item): for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series - if real_title == "": + if not real_title: real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') real_title = scrapertools.htmlclean(real_title) + real_title = real_title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series if calidad == "": calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies @@ -220,9 +222,8 @@ def listado_busqueda(item): # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") title = re.sub(r'(Calidad.*?\])', '', title) - + title = title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") if real_title == "": real_title = title if calidad == "": @@ -236,18 +237,22 @@ def listado_busqueda(item): # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: - calidad_mps = "series/" if "seriehd" in url: calidad_mps = "series-hd/" - if "serievo" in url: + elif "serievo" in url: calidad_mps = "series-vo/" - if "serie-vo" in url: + elif "serie-vo" in url: calidad_mps = "series-vo/" + else: + calidad_mps = "series/" - real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) - real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + if "no_image" in thumb: + real_title_mps = title + else: + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) - if "/0_" not in thumb: + if "/0_" not in thumb and not "no_image" in thumb: serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') if len(serieid) > 5: serieid = "" @@ -507,6 +512,11 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioNEW: " + pattern) + logger.debug(info) + info = '><strong>%sTemporada %s Capitulo 0</strong> - <span >Español Castellano</span> Calidad <span >[%s]</span>' % (item.contentTitle, season, item.infoLabels['quality']) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] @@ -540,11 +550,11 @@ def episodios(item): "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" - else: - logger.debug("patron episodio: " + pattern) + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioOLD: " + pattern) logger.debug(info) - continue - + info = '%s [%s][Cap.%s00][Español]' % (item.contentTitle, item.infoLabels['quality'], season) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] diff --git a/plugin.video.alfa/channels/torrentrapid.py b/plugin.video.alfa/channels/torrentrapid.py index 85caf927..17273437 100644 --- a/plugin.video.alfa/channels/torrentrapid.py +++ b/plugin.video.alfa/channels/torrentrapid.py @@ -133,7 +133,8 @@ def listado(item): for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: url = scrapedurl - title = scrapedtitle + title = scrapedtitle.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") + title_alt = title_alt.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") thumbnail = scrapedthumbnail action = "findvideos" extra = "" @@ -209,10 +210,11 @@ def listado_busqueda(item): for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series - if real_title == "": + if not real_title: real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') real_title = scrapertools.htmlclean(real_title) + real_title = real_title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series if calidad == "": calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies @@ -220,9 +222,8 @@ def listado_busqueda(item): # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") title = re.sub(r'(Calidad.*?\])', '', title) - + title = title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") if real_title == "": real_title = title if calidad == "": @@ -236,18 +237,22 @@ def listado_busqueda(item): # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: - calidad_mps = "series/" if "seriehd" in url: calidad_mps = "series-hd/" - if "serievo" in url: + elif "serievo" in url: calidad_mps = "series-vo/" - if "serie-vo" in url: + elif "serie-vo" in url: calidad_mps = "series-vo/" + else: + calidad_mps = "series/" - real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) - real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + if "no_image" in thumb: + real_title_mps = title + else: + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) - if "/0_" not in thumb: + if "/0_" not in thumb and not "no_image" in thumb: serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') if len(serieid) > 5: serieid = "" @@ -507,6 +512,11 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioNEW: " + pattern) + logger.debug(info) + info = '><strong>%sTemporada %s Capitulo 0</strong> - <span >Español Castellano</span> Calidad <span >[%s]</span>' % (item.contentTitle, season, item.infoLabels['quality']) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] @@ -540,11 +550,11 @@ def episodios(item): "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" - else: - logger.debug("patron episodio: " + pattern) + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioOLD: " + pattern) logger.debug(info) - continue - + info = '%s [%s][Cap.%s00][Español]' % (item.contentTitle, item.infoLabels['quality'], season) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] diff --git a/plugin.video.alfa/channels/tumejortorrent.py b/plugin.video.alfa/channels/tumejortorrent.py index 3ea77b12..10cf34e5 100644 --- a/plugin.video.alfa/channels/tumejortorrent.py +++ b/plugin.video.alfa/channels/tumejortorrent.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re @@ -133,7 +133,8 @@ def listado(item): for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: url = scrapedurl - title = scrapedtitle + title = scrapedtitle.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") + title_alt = title_alt.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") thumbnail = scrapedthumbnail action = "findvideos" extra = "" @@ -209,10 +210,11 @@ def listado_busqueda(item): for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series - if real_title == "": + if not real_title: real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') real_title = scrapertools.htmlclean(real_title) + real_title = real_title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series if calidad == "": calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies @@ -220,9 +222,8 @@ def listado_busqueda(item): # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") title = re.sub(r'(Calidad.*?\])', '', title) - + title = title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") if real_title == "": real_title = title if calidad == "": @@ -236,18 +237,22 @@ def listado_busqueda(item): # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: - calidad_mps = "series/" if "seriehd" in url: calidad_mps = "series-hd/" - if "serievo" in url: + elif "serievo" in url: calidad_mps = "series-vo/" - if "serie-vo" in url: + elif "serie-vo" in url: calidad_mps = "series-vo/" + else: + calidad_mps = "series/" - real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) - real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + if "no_image" in thumb: + real_title_mps = title + else: + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) - if "/0_" not in thumb: + if "/0_" not in thumb and not "no_image" in thumb: serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') if len(serieid) > 5: serieid = "" @@ -507,6 +512,11 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioNEW: " + pattern) + logger.debug(info) + info = '><strong>%sTemporada %s Capitulo 0</strong> - <span >Español Castellano</span> Calidad <span >[%s]</span>' % (item.contentTitle, season, item.infoLabels['quality']) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] @@ -540,11 +550,11 @@ def episodios(item): "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" - else: - logger.debug("patron episodio: " + pattern) + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioOLD: " + pattern) logger.debug(info) - continue - + info = '%s [%s][Cap.%s00][Español]' % (item.contentTitle, item.infoLabels['quality'], season) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] diff --git a/plugin.video.alfa/channels/tvsinpagar.py b/plugin.video.alfa/channels/tvsinpagar.py index 56898ea1..ab352ac1 100644 --- a/plugin.video.alfa/channels/tvsinpagar.py +++ b/plugin.video.alfa/channels/tvsinpagar.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import re @@ -133,7 +133,8 @@ def listado(item): for scrapedurl, scrapedtitle, scrapedthumbnail, title_alt, calidad in matches: url = scrapedurl - title = scrapedtitle + title = scrapedtitle.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") + title_alt = title_alt.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "") thumbnail = scrapedthumbnail action = "findvideos" extra = "" @@ -209,10 +210,11 @@ def listado_busqueda(item): for url, thumb, title in matches: real_title = scrapertools.find_single_match(title, r'<strong.*?>(.*?)Temporada.*?<\/strong>') #series - if real_title == "": + if not real_title: real_title = scrapertools.find_single_match(title, r'(.*?)\[.*?]') #movies real_title = scrapertools.remove_htmltags(real_title).decode('iso-8859-1').encode('utf-8') real_title = scrapertools.htmlclean(real_title) + real_title = real_title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") calidad = scrapertools.find_single_match(title, r'.*?\s*Calidad.*?<span[^>]+>[\[]\s*(?P<quality>.*?)\s*[\]]<\/span>') #series if calidad == "": calidad = scrapertools.find_single_match(title, r'..*?(\[.*?.*\])') #movies @@ -220,9 +222,8 @@ def listado_busqueda(item): # fix encoding for title title = scrapertools.htmlclean(title) - title = title.replace("�", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng") title = re.sub(r'(Calidad.*?\])', '', title) - + title = title.replace("�", "ñ").replace("ñ", "ñ").replace("Temp", " Temp").replace("Esp", " Esp").replace("Ing", " Ing").replace("Eng", " Eng").replace("Calidad", "").replace("de la Serie", "") if real_title == "": real_title = title if calidad == "": @@ -236,18 +237,22 @@ def listado_busqueda(item): # Codigo para rescatar lo que se puede en pelisy.series.com de Series para la Videoteca. la URL apunta al capítulo y no a la Serie. Nombre de Serie frecuentemente en blanco. Se obtiene de Thumb, así como el id de la serie if ("/serie" in url or "-serie" in url) and "pelisyseries.com" in host: - calidad_mps = "series/" if "seriehd" in url: calidad_mps = "series-hd/" - if "serievo" in url: + elif "serievo" in url: calidad_mps = "series-vo/" - if "serie-vo" in url: + elif "serie-vo" in url: calidad_mps = "series-vo/" + else: + calidad_mps = "series/" - real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) - real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) + if "no_image" in thumb: + real_title_mps = title + else: + real_title_mps = re.sub(r'.*?\/\d+_', '', thumb) + real_title_mps = re.sub(r'\.\w+.*?', '', real_title_mps) - if "/0_" not in thumb: + if "/0_" not in thumb and not "no_image" in thumb: serieid = scrapertools.find_single_match(thumb, r'.*?\/\w\/(?P<serieid>\d+).*?.*') if len(serieid) > 5: serieid = "" @@ -507,6 +512,11 @@ def episodios(item): "[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" if "Especial" in info: # Capitulos Especiales pattern = ".*?[^>]+>.*?Temporada.*?\[.*?(?P<season>\d+).*?\].*?Capitulo.*?\[\s*(?P<episode>\d+).*?\]?(?:.*?(?P<episode2>\d+)?)<.+?<span[^>]+>(?P<lang>.*?)?<\/span>\s*Calidad\s*<span[^>]+>[\[]\s*(?P<quality>.*?)?\s*[\]]<\/span>" + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioNEW: " + pattern) + logger.debug(info) + info = '><strong>%sTemporada %s Capitulo 0</strong> - <span >Español Castellano</span> Calidad <span >[%s]</span>' % (item.contentTitle, season, item.infoLabels['quality']) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] @@ -540,11 +550,11 @@ def episodios(item): "(?P<episode2>\d{2}))?.*?\].*?(?:\[(?P<lang>.*?)\])?" elif scrapertools.find_single_match(info, 'Cap.\d{2,3}'): pattern = ".*?Temp.*?\s(?P<quality>.*?)\s.*?Cap.(?P<season>\d).*?(?P<episode>\d{2})(?:_(?P<season2>\d+)(?P<episode2>\d{2}))?.*?\s(?P<lang>.*)?" - else: - logger.debug("patron episodio: " + pattern) + + if not scrapertools.find_single_match(info, pattern): #en caso de error de formato, creo uno básico + logger.debug("patron episodioOLD: " + pattern) logger.debug(info) - continue - + info = '%s [%s][Cap.%s00][Español]' % (item.contentTitle, item.infoLabels['quality'], season) r = re.compile(pattern) match = [m.groupdict() for m in r.finditer(info)][0] From 1a3560977392b6c77f8d685f04fc6b60dfebdfd2 Mon Sep 17 00:00:00 2001 From: Kingbox <37674310+lopezvg@users.noreply.github.com> Date: Mon, 9 Apr 2018 15:53:15 +0200 Subject: [PATCH 22/22] Mejortorrent: arreglos de url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Después del cambio de dominio, ha cambiado el comportamiento de la creación de urls de .torrents, tanto en Series como en Películas --- plugin.video.alfa/channels/mejortorrent.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugin.video.alfa/channels/mejortorrent.py b/plugin.video.alfa/channels/mejortorrent.py index f085f160..c31662f6 100755 --- a/plugin.video.alfa/channels/mejortorrent.py +++ b/plugin.video.alfa/channels/mejortorrent.py @@ -254,7 +254,7 @@ def episodios(item): url = host + scrapertools.find_single_match(data,patron) # "episodios%5B1%5D=11744&total_capis=5&tabla=series&titulo=Sea+Patrol+-+2%AA+Temporada" post = urllib.urlencode({name: value, "total_capis": total_capis, "tabla": tabla, "titulo": titulo}) - logger.debug("post=" + post) + #logger.debug("post=" + post) if item.extra == "series": epi = scrapedtitle.split("x") @@ -311,7 +311,6 @@ def show_movie_info(item): pass data = httptools.downloadpage(item.url).data - logger.debug("data=" + data) patron = "<a href='(secciones.php\?sec\=descargas[^']+)'" matches = re.compile(patron, re.DOTALL).findall(data) @@ -319,9 +318,11 @@ def show_movie_info(item): for scrapedurl in matches: url = urlparse.urljoin(item.url, scrapedurl) logger.debug("title=[" + item.title + "], url=[" + url + "], thumbnail=[" + item.thumbnail + "]") - torrent_data = httptools.downloadpage(url).data - link = scrapertools.get_match(torrent_data, "<a href='(\/uploads\/torrents\/peliculas\/.*?\.torrent)'>") + if scrapertools.find_single_match(torrent_data, "<a href='(\/uploads\/torrents\/peliculas\/.*?\.torrent)'>"): + link = scrapertools.get_match(torrent_data, "<a href='(\/uploads\/torrents\/peliculas\/.*?\.torrent)'>") + else: + link = scrapertools.get_match(torrent_data, "<a href='(http:\/\/www.mejortorrent.com\/uploads\/torrents\/.*?peliculas\/.*?\.torrent)'>") link = urlparse.urljoin(url, link) logger.debug("link=" + link) itemlist.append(Item(channel=item.channel, action="play", server="torrent", title=item.title, url=link, @@ -363,7 +364,7 @@ def play(item): else: #data = httptools.downloadpage(item.url, post=item.extra).data data = httptools.downloadpage(item.url).data - logger.debug("data=" + data) + #logger.debug("data=" + data) params = dict(urlparse.parse_qsl(item.extra)) patron = host + "/secciones.php?sec=descargas&ap=contar&tabla=" + params["tabla"] + "&id=" + item.id @@ -373,7 +374,9 @@ def play(item): data = httptools.downloadpage(patron).data patron = "Pincha <a href='(.*?)'>" - link = host + scrapertools.find_single_match(data, patron) + link = scrapertools.find_single_match(data, patron) + if not host in link: + link = host + link logger.info("link=" + link) itemlist.append(Item(channel=item.channel, action="play", server="torrent", title=item.title, url=link, thumbnail=item.thumbnail, plot=item.plot, folder=False))