- tanti miglioramenti sotto il cofano, supporto iniziale al futuro kodi 19
- Nuova modalità di visualizzazione per episodio successivo
- fixato wstream tramite l'aggiunta della finestra per risolvere il reCaptcha
- aggiunta sezione segnala un problema in Aiuto
- altri fix e migliorie varie a canali e server
This commit is contained in:
marco
2020-02-22 13:36:58 +01:00
parent 82b61df289
commit ca6d5eb56d
121 changed files with 13147 additions and 5448 deletions
+6 -7
View File
@@ -1,4 +1,4 @@
<addon id="plugin.video.kod" name="Kodi on Demand" provider-name="KOD Team" version="0.7.2"> <addon id="plugin.video.kod" name="Kodi on Demand" provider-name="KOD Team" version="0.8">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.libtorrent" optional="true"/> <import addon="script.module.libtorrent" optional="true"/>
@@ -19,12 +19,11 @@
<screenshot>resources/media/themes/ss/2.png</screenshot> <screenshot>resources/media/themes/ss/2.png</screenshot>
<screenshot>resources/media/themes/ss/3.png</screenshot> <screenshot>resources/media/themes/ss/3.png</screenshot>
</assets> </assets>
<news>- - aggiunto raiplay <news>- tanti miglioramenti &quot;sotto il cofano&quot;, supporto iniziale al futuro kodi 19
- agigunto d.s.d.a (ex documentaristreamingda) - Nuova modalità di visualizzazione per episodio successivo
- svariati fix ai canali (eurostreaming, streamtime, piratestreaming, altadefinizioneclick) - fixato wstream tramite l'aggiunta della finestra per risolvere il reCaptcha
- la videoteca ora può essere messa nelle unità di rete - aggiunta sezione &quot;segnala un problema&quot; in Aiuto
- aggiunto server upstream - altri fix e migliorie varie a canali e server</news>
- altri piccoli fix vari</news>
<description lang="it">Naviga velocemente sul web e guarda i contenuti presenti</description> <description lang="it">Naviga velocemente sul web e guarda i contenuti presenti</description>
<disclaimer>[COLOR red]The owners and submitters to this addon do not host or distribute any of the content displayed by these addons nor do they have any affiliation with the content providers.[/COLOR] <disclaimer>[COLOR red]The owners and submitters to this addon do not host or distribute any of the content displayed by these addons nor do they have any affiliation with the content providers.[/COLOR]
[COLOR yellow]Kodi © is a registered trademark of the XBMC Foundation. We are not connected to or in any other way affiliated with Kodi, Team Kodi, or the XBMC Foundation. Furthermore, any software, addons, or products offered by us will receive no support in official Kodi channels, including the Kodi forums and various social networks.[/COLOR]</disclaimer> [COLOR yellow]Kodi © is a registered trademark of the XBMC Foundation. We are not connected to or in any other way affiliated with Kodi, Team Kodi, or the XBMC Foundation. Furthermore, any software, addons, or products offered by us will receive no support in official Kodi channels, including the Kodi forums and various social networks.[/COLOR]</disclaimer>
+9 -10
View File
@@ -1,17 +1,17 @@
{ {
"altadefinizione01": "https://www.altadefinizione01.tel", "altadefinizione01": "https://altadefinizione01.media",
"altadefinizione01_link": "https://altadefinizione01.cam", "altadefinizione01_link": "https://altadefinizione01.kim",
"altadefinizioneclick": "https://altadefinizione.style", "altadefinizioneclick": "https://altadefinizione.style",
"animeforce": "https://ww1.animeforce.org", "animeforce": "https://ww1.animeforce.org",
"animeleggendari": "https://animepertutti.com", "animeleggendari": "https://animepertutti.com",
"animesaturn": "https://animesaturn.com", "animesaturn": "https://www.animesaturn.com",
"animestream": "https://www.animeworld.it", "animestream": "https://www.animeworld.it",
"animesubita": "http://www.animesubita.org", "animesubita": "http://www.animesubita.org",
"animetubeita": "http://www.animetubeita.com", "animetubeita": "http://www.animetubeita.com",
"animeunity": "https://www.animeunity.it", "animeunity": "https://www.animeunity.it",
"animeworld": "https://www.animeworld.cc", "animeworld": "https://www.animeworld.tv",
"casacinema": "https://www.casacinema.biz", "casacinema": "https://www.casacinema.bid",
"casacinemaInfo": "https://casacinema.kim", "casacinemaInfo": "https://casacinema.blue",
"cb01anime": "https://www.cineblog01.ink", "cb01anime": "https://www.cineblog01.ink",
"cinetecadibologna": "http://cinestore.cinetecadibologna.it", "cinetecadibologna": "http://cinestore.cinetecadibologna.it",
"dreamsub": "https://dreamsub.stream", "dreamsub": "https://dreamsub.stream",
@@ -19,8 +19,8 @@
"fastsubita": "https://fastsubita.com", "fastsubita": "https://fastsubita.com",
"filmgratis": "https://www.filmaltadefinizione.org", "filmgratis": "https://www.filmaltadefinizione.org",
"filmigratis": "https://filmigratis.org", "filmigratis": "https://filmigratis.org",
"filmpertutti": "https://www.filmpertutti.casa", "filmpertutti": "https://www.filmpertutti.date",
"filmsenzalimiticc": "https://www.filmsenzalimiti.monster", "filmsenzalimiticc": "https://www.filmsenzalimiti.london",
"filmstreaming01": "https://filmstreaming01.com", "filmstreaming01": "https://filmstreaming01.com",
"guardarefilm": "https://www.guardarefilm.red", "guardarefilm": "https://www.guardarefilm.red",
"guardaserie_stream": "https://guardaserie.store", "guardaserie_stream": "https://guardaserie.store",
@@ -33,11 +33,10 @@
"polpotv": "https://polpo.tv", "polpotv": "https://polpo.tv",
"pufimovies": "https://pufimovies.com", "pufimovies": "https://pufimovies.com",
"raiplay": "https://www.raiplay.it", "raiplay": "https://www.raiplay.it",
"seriehd": "https://www.seriehd.watch", "seriehd": "https://seriehd.click",
"serietvonline": "https://serietvonline.icu", "serietvonline": "https://serietvonline.icu",
"serietvsubita": "http://serietvsubita.xyz", "serietvsubita": "http://serietvsubita.xyz",
"serietvu": "https://www.serietvu.link", "serietvu": "https://www.serietvu.link",
"streamingaltadefinizione": "https://www.popcornstream.best",
"streamtime": "https://t.me/s/StreamTime", "streamtime": "https://t.me/s/StreamTime",
"tantifilm": "https://www.tantifilm.eu", "tantifilm": "https://www.tantifilm.eu",
"toonitalia": "https://toonitalia.org", "toonitalia": "https://toonitalia.org",
+32 -43
View File
@@ -44,8 +44,6 @@
# per l'uso dei decoratori, per i log, e funzioni per siti particolari # per l'uso dei decoratori, per i log, e funzioni per siti particolari
from core import support from core import support
# se non si fa uso di findhost()
from platformcode import config
# in caso di necessità # in caso di necessità
#from core import scrapertools, httptools, servertools, tmdb #from core import scrapertools, httptools, servertools, tmdb
@@ -54,30 +52,22 @@ from core.item import Item # per newest
##### fine import ##### fine import
# impostazioni variabili o def findhost() # se il sito ha un link per ottenere l'url corretto in caso di oscuramenti
# la funzione deve ritornare l'indirizzo corretto, verrà chiamata solo se necessario (link primario irraggiungibile)
# se necessaria la variabile __channel__
# da cancellare se non utilizzata
__channel__ = "id nel json"
# da cancellare se si utilizza findhost()
host = config.get_channel_url('id nel json' OR __channel__) # <-- ATTENZIONE
headers = [['Referer', host]]
# Inizio findhost() - da cancellare se usato l'altro metodo
#impostati dinamicamente da findhost()
host = ""
headers = ""
def findhost(): def findhost():
global host, headers def findhost():
# da adattare alla bisogna... permUrl = httptools.downloadpage('https://www.cb01.uno/', follow_redirects=False).headers
permUrl = httptools.downloadpage('INSERIRE-URL-QUI', follow_redirects=False).headers if 'google' in permUrl['location']:
host = 'https://www.'+permUrl['location'].replace('https://www.google.it/search?q=site:', '') host = permUrl['location'].replace('https://www.google.it/search?q=site:', '')
# cancellare host non utilizzato else:
host = scrapertools.find_single_match(permUrl, r'<div class="elementor-button-wrapper"> <a href="([^"]+)"') host = permUrl['location']
headers = [['Referer', host]] return host
# così le imposta una volta per tutte
### fine findhost # se si usa findhost
host = config.get_channel_url(findhost)
# se non si usa (metti l'url in channels.json)
host = config.get_channel_url()
headers = [['Referer', host]]
# server di esempio... # server di esempio...
list_servers = ['supervideo', 'streamcherry','rapidvideo', 'streamango', 'openload'] list_servers = ['supervideo', 'streamcherry','rapidvideo', 'streamango', 'openload']
@@ -141,25 +131,24 @@ def mainlist(item):
nome = [( '' ['', '', '', '']) nome = [( '' ['', '', '', ''])
return locals() return locals()
# Legenda known_keys per i groups nei patron
# known_keys = ['url', 'title', 'title2', 'season', 'episode', 'thumb', 'quality',
# 'year', 'plot', 'duration', 'genere', 'rating', 'type', 'lang']
# url = link relativo o assoluto alla pagina titolo film/serie
# title = titolo Film/Serie/Anime/Altro
# title2 = titolo dell'episodio Serie/Anime/Altro
# season = stagione in formato numerico
# episode = numero episodio, in formato numerico.
# thumb = linkrealtivo o assoluto alla locandina Film/Serie/Anime/Altro
# quality = qualità indicata del video
# year = anno in formato numerico (4 cifre)
# duration = durata del Film/Serie/Anime/Altro
# genere = genere del Film/Serie/Anime/Altro. Es: avventura, commedia
# rating = punteggio/voto in formato numerico
# type = tipo del video. Es. movie per film o tvshow per le serie. Di solito sono discrimanti usati dal sito
# lang = lingua del video. Es: ITA, Sub-ITA, Sub, SUB ITA.
# AVVERTENZE: Se il titolo è trovato nella ricerca TMDB/TVDB/Altro allora le locandine e altre info non saranno quelle recuperate nel sito.!!!!
# Legenda known_keys per i groups nei patron
# known_keys = ['url', 'title', 'title2', 'season', 'episode', 'thumb', 'quality',
# 'year', 'plot', 'duration', 'genere', 'rating', 'type', 'lang']
# url = link relativo o assoluto alla pagina titolo film/serie
# title = titolo Film/Serie/Anime/Altro
# title2 = titolo dell'episodio Serie/Anime/Altro
# season = stagione in formato numerico
# episode = numero episodio, in formato numerico.
# thumb = linkrealtivo o assoluto alla locandina Film/Serie/Anime/Altro
# quality = qualità indicata del video
# year = anno in formato numerico (4 cifre)
# duration = durata del Film/Serie/Anime/Altro
# genere = genere del Film/Serie/Anime/Altro. Es: avventura, commedia
# rating = punteggio/voto in formato numerico
# type = tipo del video. Es. movie per film o tvshow per le serie. Di solito sono discrimanti usati dal sito
# lang = lingua del video. Es: ITA, Sub-ITA, Sub, SUB ITA.
# AVVERTENZE: Se il titolo è trovato nella ricerca TMDB/TVDB/Altro allora le locandine e altre info non saranno quelle recuperate nel sito.!!!!
@support.scrape @support.scrape
def peliculas(item): def peliculas(item):
support.log(item) support.log(item)
+47 -75
View File
@@ -9,6 +9,7 @@ from core import scrapertools, httptools, servertools, tmdb, support
from core.item import Item from core.item import Item
from lib import unshortenit from lib import unshortenit
from platformcode import logger, config from platformcode import logger, config
from lib.concurrent import futures
def findhost(): def findhost():
@@ -175,31 +176,23 @@ def findvideos(item):
if item.contentType == "episode": if item.contentType == "episode":
return findvid_serie(item) return findvid_serie(item)
def load_links(itemlist, re_txt, color, desc_txt, quality=""): def load_links(urls, re_txt, desc_txt, quality=""):
streaming = scrapertools.find_single_match(data, re_txt).replace('"', '') if re_txt:
support.log('STREAMING',streaming) streaming = scrapertools.find_single_match(data, re_txt).replace('"', '')
support.log('STREAMING=', streaming) support.log('STREAMING',streaming)
# patron = '<td><a.*?href=(.*?) (?:target|rel)[^>]+>([^<]+)<' matches = support.match(streaming, patron = r'<td><a.*?href=([^ ]+) [^>]+>[^<]+<').matches
patron = '<td><a.*?href=([^ ]+) [^>]+>([^<]+)<' with futures.ThreadPoolExecutor() as executor:
matches = re.compile(patron, re.DOTALL).findall(streaming) u = [executor.submit(final_links, match) for match in matches]
for scrapedurl, scrapedtitle in matches: for res in futures.as_completed(u):
logger.debug("##### findvideos %s ## %s ## %s ##" % (desc_txt, scrapedurl, scrapedtitle)) if res.result():
itemlist.append( urls.append(res.result())
Item(channel=item.channel, # for url in matches:
action="play", # # logger.debug("##### findvideos %s ## %s ## %s ##" % (desc_txt, url, server))
title=scrapedtitle, # urls.append(final_links(url))
url=scrapedurl,
server=scrapedtitle,
fulltitle=item.fulltitle,
thumbnail=item.thumbnail,
show=item.show,
quality=quality,
contentType=item.contentType,
folder=False))
support.log() support.log()
itemlist = [] itemlist = urls = []
# Carica la pagina # Carica la pagina
data = httptools.downloadpage(item.url).data data = httptools.downloadpage(item.url).data
@@ -213,20 +206,20 @@ def findvideos(item):
QualityStr = scrapertools.decodeHtmlentities(match.group(1)) QualityStr = scrapertools.decodeHtmlentities(match.group(1))
# Estrae i contenuti - Streaming # Estrae i contenuti - Streaming
load_links(itemlist, '<strong>Streamin?g:</strong>(.*?)cbtable', "orange", "Streaming", "SD") load_links(urls, '<strong>Streamin?g:</strong>(.*?)cbtable', "Streaming", "SD")
# Estrae i contenuti - Streaming HD # Estrae i contenuti - Streaming HD
load_links(itemlist, '<strong>Streamin?g HD[^<]+</strong>(.*?)cbtable', "yellow", "Streaming HD", "HD") load_links(urls, '<strong>Streamin?g HD[^<]+</strong>(.*?)cbtable', "Streaming HD", "HD")
# Estrae i contenuti - Streaming 3D # Estrae i contenuti - Streaming 3D
load_links(itemlist, '<strong>Streamin?g 3D[^<]+</strong>(.*?)cbtable', "pink", "Streaming 3D") load_links(urls, '<strong>Streamin?g 3D[^<]+</strong>(.*?)cbtable', "Streaming 3D")
itemlist=support.server(item, itemlist=itemlist) itemlist=support.server(item, urls)
if itemlist and QualityStr: if itemlist and QualityStr:
itemlist.insert(0, itemlist.insert(0,
Item(channel=item.channel, Item(channel=item.channel,
action="", action="",
title="[COLOR orange]%s[/COLOR]" % QualityStr, title=support.typo(QualityStr,'[] color kod bold'),
folder=False)) folder=False))
return itemlist return itemlist
@@ -239,68 +232,47 @@ def findvideos(item):
def findvid_serie(item): def findvid_serie(item):
def load_vid_series(html, item, itemlist, blktxt): def load_vid_series(html, item, urls, blktxt=''):
logger.info('HTML' + html) # logger.info('HTML' + html)
patron = r'<a href="([^"]+)"[^=]+="_blank"[^>]+>(?!<!--)(.*?)</a>' # patron = r'<a href="([^"]+)"[^=]+="_blank"[^>]+>(?!<!--)(.*?)</a>'
# Estrae i contenuti # Estrae i contenuti
matches = re.compile(patron, re.DOTALL).finditer(html) # matches = re.compile(patron, re.DOTALL).finditer(html)
for match in matches: matches = support.match(html, patron = r'<a href="([^"]+)"[^=]+="_blank"[^>]+>(?!<!--).*?</a>').matches
scrapedurl = match.group(1) with futures.ThreadPoolExecutor() as executor:
scrapedtitle = match.group(2) u = [executor.submit(final_links, match) for match in matches]
# title = item.title + " [COLOR blue][" + scrapedtitle + "][/COLOR]" for res in futures.as_completed(u):
itemlist.append( if res.result():
Item(channel=item.channel, urls.append(res.result())
action="play", # for url, server in matches:
title=scrapedtitle, # urls.append(final_links(url))
url=scrapedurl,
server=scrapedtitle,
fulltitle=item.fulltitle,
show=item.show,
contentType=item.contentType,
folder=False))
support.log() support.log()
itemlist = [] itemlist = []
lnkblk = [] lnkblk = []
lnkblkp = [] lnkblkp = []
urls = []
data = item.url data = item.url
# First blocks of links # Blocks with split
if data[0:data.find('<a')].find(':') > 0: blk=re.split(r"(?:>\s*)?([A-Za-z\s0-9]*):\s*<",data,re.S)
lnkblk.append(data[data.find(' - ') + 3:data[0:data.find('<a')].find(':') + 1]) blktxt=""
lnkblkp.append(data.find(' - ') + 3) for b in blk:
else: if b[0:3]=="a h" or b[0:4]=="<a h":
lnkblk.append(' ') load_vid_series("<%s>"%b, item, urls, blktxt)
lnkblkp.append(data.find('<a')) blktxt=""
elif len(b.strip())>1:
blktxt=b.strip()
# Find new blocks of links return support.server(item, urls)
patron = r'<a\s[^>]+>[^<]+</a>([^<]+)'
matches = re.compile(patron, re.DOTALL).finditer(data)
for match in matches:
sep = match.group(1)
if sep != ' - ':
lnkblk.append(sep)
i = 0
if len(lnkblk) > 1:
for lb in lnkblk[1:]:
lnkblkp.append(data.find(lb, lnkblkp[i] + len(lnkblk[i])))
i = i + 1
for i in range(0, len(lnkblk)):
if i == len(lnkblk) - 1:
load_vid_series(data[lnkblkp[i]:], item, itemlist, lnkblk[i])
else:
load_vid_series(data[lnkblkp[i]:lnkblkp[i + 1]], item, itemlist, lnkblk[i])
return support.server(item, itemlist=itemlist)
def play(item): def final_links(url):
support.log() support.log()
itemlist = [] itemlist = []
item= Item()
item.url = url
### Handling new cb01 wrapper ### Handling new cb01 wrapper
if host[9:] + "/film/" in item.url: if host[9:] + "/film/" in item.url:
iurl = httptools.downloadpage(item.url, only_headers=True, follow_redirects=False).headers.get("location", "") iurl = httptools.downloadpage(item.url, only_headers=True, follow_redirects=False).headers.get("location", "")
@@ -329,4 +301,4 @@ def play(item):
else: else:
data = support.swzz_get_url(item) data = support.swzz_get_url(item)
return servertools.find_video_items(data=data) return data
+4 -1
View File
@@ -14,7 +14,10 @@ list_quality = ['default']
def findhost(): def findhost():
permUrl = httptools.downloadpage('https://www.cinemalibero.online/', follow_redirects=False).headers permUrl = httptools.downloadpage('https://www.cinemalibero.online/', follow_redirects=False).headers
import urlparse try:
import urlparse
except:
import urllib.parse as urlparse
p = list(urlparse.urlparse(permUrl['location'].replace('https://www.google.com/search?q=site:', ''))) p = list(urlparse.urlparse(permUrl['location'].replace('https://www.google.com/search?q=site:', '')))
if not p[0]: if not p[0]:
p[0] = 'https' p[0] = 'https'
-7
View File
@@ -3,14 +3,7 @@
# Ringraziamo Icarus crew # Ringraziamo Icarus crew
# Canale per cinetecadibologna # Canale per cinetecadibologna
# ------------------------------------------------------------ # ------------------------------------------------------------
import re
import urlparse
from core import httptools, scrapertools
from core.item import Item from core.item import Item
from platformcode import logger, config
from core import support from core import support
+31 -20
View File
@@ -110,6 +110,7 @@ def episodios(item):
def findvideos(item): def findvideos(item):
itemlist = [] itemlist = []
support.log() support.log()
# support.dbg()
matches = support.match(item, patron=r'href="([^"]+)"', patronBlock=r'<div style="white-space: (.*?)<div id="main-content"') matches = support.match(item, patron=r'href="([^"]+)"', patronBlock=r'<div style="white-space: (.*?)<div id="main-content"')
@@ -118,27 +119,37 @@ def findvideos(item):
item.contentType = 'tvshow' item.contentType = 'tvshow'
return episodios(item) return episodios(item)
# matches.matches.sort() if 'vvvvid' in matches.data:
support.log('VIDEO')
for url in matches.matches:
lang = url.split('/')[-2]
if 'ita' in lang.lower():
language = 'ITA'
if 'sub' in lang.lower():
language = 'Sub-' + language
quality = url.split('/')[-1]
itemlist.append( itemlist.append(
support.Item(channel=item.channel, support.Item(channel=item.channel,
action="play", action="play",
contentType=item.contentType, contentType=item.contentType,
title=language, title='vvvid',
url=url, url=support.match(matches.data, patron=r'(http://www.vvvvid[^"]+)').match,
contentLanguage = language, server='vvvvid',
quality = quality, ))
order = quality.replace('p','').zfill(4), else:
server='directo', # matches.matches.sort()
)) support.log('VIDEO')
for url in matches.matches:
lang = url.split('/')[-2]
if 'ita' in lang.lower():
language = 'ITA'
if 'sub' in lang.lower():
language = 'Sub-' + language
quality = url.split('/')[-1]
itemlist.append(
support.Item(channel=item.channel,
action="play",
contentType=item.contentType,
title=language,
url=url,
contentLanguage = language,
quality = quality,
order = quality.replace('p','').zfill(4),
server='directo',
))
itemlist.sort(key=lambda x: (x.title, x.order), reverse=False) itemlist.sort(key=lambda x: (x.title, x.order), reverse=False)
return support.server(item, itemlist=itemlist) return support.server(item, itemlist=itemlist)
+1 -3
View File
@@ -3,10 +3,8 @@
# Ringraziamo Icarus crew # Ringraziamo Icarus crew
# Canale per documentaristreamingda # Canale per documentaristreamingda
# ------------------------------------------------------------ # ------------------------------------------------------------
import re
import urlparse
from core import httptools, scrapertools, servertools, support from core import support
from core.item import Item from core.item import Item
from platformcode import logger, config from platformcode import logger, config
+4 -1
View File
@@ -4,7 +4,10 @@
# ------------------------------------------------------------ # ------------------------------------------------------------
import re import re
import urlparse try:
import urlparse
except:
import urllib.parse as urlparse
from core import scrapertools, servertools, httptools from core import scrapertools, servertools, httptools
from core import tmdb from core import tmdb
+4 -5
View File
@@ -69,7 +69,6 @@ def getmainlist(view="thumb_"):
itemlist.append(Item(title=config.get_localized_string(30100), channel="setting", action="mainlist", itemlist.append(Item(title=config.get_localized_string(30100), channel="setting", action="mainlist",
thumbnail=get_thumb(thumb_setting, view), thumbnail=get_thumb(thumb_setting, view),
category=config.get_localized_string(30100), viewmode="list")) category=config.get_localized_string(30100), viewmode="list"))
itemlist.append(Item(title=config.get_localized_string(30104) + " (v" + config.get_addon_version(with_fix=True) + ")", channel="help", action="mainlist", itemlist.append(Item(title=config.get_localized_string(30104) + " (v" + config.get_addon_version(with_fix=True) + ")", channel="help", action="mainlist",
thumbnail=get_thumb("help.png", view), thumbnail=get_thumb("help.png", view),
category=config.get_localized_string(30104), viewmode="list")) category=config.get_localized_string(30104), viewmode="list"))
@@ -109,7 +108,7 @@ def getchanneltypes(view="thumb_"):
itemlist.append(Item(title=config.get_localized_string(70685), channel="community", action="mainlist", view=view, itemlist.append(Item(title=config.get_localized_string(70685), channel="community", action="mainlist", view=view,
category=title, channel_type="all", thumbnail=get_thumb("channels_community.png", view), category=config.get_localized_string(70685), channel_type="all", thumbnail=get_thumb("channels_community.png", view),
viewmode="thumbnails")) viewmode="thumbnails"))
return itemlist return itemlist
@@ -150,9 +149,9 @@ def filterchannels(category, view="thumb_"):
if channel_parameters["channel"] == 'community': if channel_parameters["channel"] == 'community':
continue continue
# si el canal no es compatible, no se muestra # # si el canal no es compatible, no se muestra
if not channel_parameters["compatible"]: # if not channel_parameters["compatible"]:
continue # continue
# Si no es un canal lo saltamos # Si no es un canal lo saltamos
if not channel_parameters["channel"]: if not channel_parameters["channel"]:
+68 -97
View File
@@ -3,10 +3,9 @@
# channeltools - Herramientas para trabajar con canales # channeltools - Herramientas para trabajar con canales
# ------------------------------------------------------------ # ------------------------------------------------------------
import os from __future__ import absolute_import
import jsontools
from core import jsontools
from platformcode import config, logger from platformcode import config, logger
DEFAULT_UPDATE_URL = "/channels/" DEFAULT_UPDATE_URL = "/channels/"
@@ -14,6 +13,7 @@ dict_channels_parameters = dict()
remote_path = 'https://raw.githubusercontent.com/kodiondemand/media/master/' remote_path = 'https://raw.githubusercontent.com/kodiondemand/media/master/'
def is_adult(channel_name): def is_adult(channel_name):
logger.info("channel_name=" + channel_name) logger.info("channel_name=" + channel_name)
channel_parameters = get_channel_parameters(channel_name) channel_parameters = get_channel_parameters(channel_name)
@@ -27,6 +27,7 @@ def is_enabled(channel_name):
def get_channel_parameters(channel_name): def get_channel_parameters(channel_name):
from core import filetools
global dict_channels_parameters global dict_channels_parameters
if channel_name not in dict_channels_parameters: if channel_name not in dict_channels_parameters:
@@ -35,20 +36,22 @@ def get_channel_parameters(channel_name):
# logger.debug(channel_parameters) # logger.debug(channel_parameters)
if channel_parameters: if channel_parameters:
# cambios de nombres y valores por defecto # cambios de nombres y valores por defecto
channel_parameters["title"] = channel_parameters.pop("name") + (' [DEPRECATED]' if channel_parameters.has_key('deprecated') and channel_parameters['deprecated'] else '') channel_parameters["title"] = channel_parameters.pop("name") + (' [DEPRECATED]' if 'deprecated' in channel_parameters and channel_parameters['deprecated'] else '')
channel_parameters["channel"] = channel_parameters.pop("id") channel_parameters["channel"] = channel_parameters.pop("id")
# si no existe el key se declaran valor por defecto para que no de fallos en las funciones que lo llaman # si no existe el key se declaran valor por defecto para que no de fallos en las funciones que lo llaman
channel_parameters["adult"] = channel_parameters.get("adult", False) channel_parameters["adult"] = channel_parameters.get("adult", False)
logger.info(channel_parameters["adult"]) logger.info(channel_parameters["adult"])
if channel_parameters["adult"]: if channel_parameters["adult"]:
channel_parameters["update_url"] = channel_parameters.get("update_url", DEFAULT_UPDATE_URL+'porn/') channel_parameters["update_url"] = channel_parameters.get("update_url",
DEFAULT_UPDATE_URL + 'porn/')
else: else:
channel_parameters["update_url"] = channel_parameters.get("update_url", DEFAULT_UPDATE_URL) channel_parameters["update_url"] = channel_parameters.get("update_url", DEFAULT_UPDATE_URL)
channel_parameters["language"] = channel_parameters.get("language", ["all"]) channel_parameters["language"] = channel_parameters.get("language", ["all"])
## channel_parameters["adult"] = channel_parameters.get("adult", False) ## channel_parameters["adult"] = channel_parameters.get("adult", False)
channel_parameters["active"] = channel_parameters.get("active", False) channel_parameters["active"] = channel_parameters.get("active", False)
channel_parameters["include_in_global_search"] = channel_parameters.get("include_in_global_search", False) channel_parameters["include_in_global_search"] = channel_parameters.get("include_in_global_search",
False)
channel_parameters["categories"] = channel_parameters.get("categories", list()) channel_parameters["categories"] = channel_parameters.get("categories", list())
channel_parameters["thumbnail"] = channel_parameters.get("thumbnail", "") channel_parameters["thumbnail"] = channel_parameters.get("thumbnail", "")
@@ -57,57 +60,27 @@ def get_channel_parameters(channel_name):
# Imagenes: se admiten url y archivos locales dentro de "resources/images" # Imagenes: se admiten url y archivos locales dentro de "resources/images"
if channel_parameters.get("thumbnail") and "://" not in channel_parameters["thumbnail"]: if channel_parameters.get("thumbnail") and "://" not in channel_parameters["thumbnail"]:
channel_parameters["thumbnail"] = os.path.join(remote_path, 'resources', "thumb", channel_parameters["thumbnail"]) channel_parameters["thumbnail"] = filetools.join(remote_path, "resources", "thumb", channel_parameters["thumbnail"])
if channel_parameters.get("banner") and "://" not in channel_parameters["banner"]: if channel_parameters.get("banner") and "://" not in channel_parameters["banner"]:
channel_parameters["banner"] = os.path.join(remote_path, 'resources', "banner", channel_parameters["banner"]) channel_parameters["banner"] = filetools.join(remote_path, "resources", "banner", channel_parameters["banner"])
if channel_parameters.get("fanart") and "://" not in channel_parameters["fanart"]: if channel_parameters.get("fanart") and "://" not in channel_parameters["fanart"]:
channel_parameters["fanart"] = os.path.join(remote_path, 'resources', "fanart", channel_parameters["fanart"]) channel_parameters["fanart"] = filetools.join(remote_path, "resources", channel_parameters["fanart"])
# Obtenemos si el canal tiene opciones de configuración # Obtenemos si el canal tiene opciones de configuración
channel_parameters["has_settings"] = False channel_parameters["has_settings"] = False
if 'settings' in channel_parameters: if 'settings' in channel_parameters:
# if not isinstance(channel_parameters['settings'], list):
# channel_parameters['settings'] = [channel_parameters['settings']]
# if "include_in_global_search" in channel_parameters['settings']:
# channel_parameters["include_in_global_search"] = channel_parameters['settings']
# ["include_in_global_search"].get('default', False)
#
# found = False
# for el in channel_parameters['settings']:
# for key in el.items():
# if 'include_in' not in key:
# channel_parameters["has_settings"] = True
# found = True
# break
# if found:
# break
channel_parameters['settings'] = get_default_settings(channel_name) channel_parameters['settings'] = get_default_settings(channel_name)
for s in channel_parameters['settings']: for s in channel_parameters['settings']:
if 'id' in s: if 'id' in s:
if s['id'] == "include_in_global_search": if s['id'] == "include_in_global_search":
channel_parameters["include_in_global_search"] = True channel_parameters["include_in_global_search"] = True
elif s['id'] == "filter_languages": elif s['id'] == "filter_languages":
channel_parameters["filter_languages"] = s.get('lvalues',[]) channel_parameters["filter_languages"] = s.get('lvalues', [])
elif s['id'].startswith("include_in_"): elif s['id'].startswith("include_in_"):
channel_parameters["has_settings"] = True channel_parameters["has_settings"] = True
del channel_parameters['settings'] del channel_parameters['settings']
# Compatibilidad
if 'compatible' in channel_parameters:
# compatible python
python_compatible = True
if 'python' in channel_parameters["compatible"]:
import sys
python_condition = channel_parameters["compatible"]['python']
if sys.version_info < tuple(map(int, (python_condition.split(".")))):
python_compatible = False
channel_parameters["compatible"] = python_compatible
else:
channel_parameters["compatible"] = True
dict_channels_parameters[channel_name] = channel_parameters dict_channels_parameters[channel_name] = channel_parameters
else: else:
@@ -115,13 +88,12 @@ def get_channel_parameters(channel_name):
# lanzamos la excepcion y asi tenemos los valores básicos # lanzamos la excepcion y asi tenemos los valores básicos
raise Exception raise Exception
except Exception, ex: except Exception as ex:
logger.error(channel_name + ".json error \n%s" % ex) logger.error(channel_name + ".json error \n%s" % ex)
channel_parameters = dict() channel_parameters = dict()
channel_parameters["channel"] = "" channel_parameters["channel"] = ""
channel_parameters["adult"] = False channel_parameters["adult"] = False
channel_parameters['active'] = False channel_parameters['active'] = False
channel_parameters["compatible"] = True
channel_parameters["language"] = "" channel_parameters["language"] = ""
channel_parameters["update_url"] = DEFAULT_UPDATE_URL channel_parameters["update_url"] = DEFAULT_UPDATE_URL
return channel_parameters return channel_parameters
@@ -131,25 +103,26 @@ def get_channel_parameters(channel_name):
def get_channel_json(channel_name): def get_channel_json(channel_name):
# logger.info("channel_name=" + channel_name) # logger.info("channel_name=" + channel_name)
import filetools from core import filetools
channel_json = None channel_json = None
try: try:
channel_path = filetools.join(config.get_runtime_path(), "channels", channel_name + ".json") channel_path = filetools.join(config.get_runtime_path(), "channels", channel_name + ".json")
if not os.path.isfile(channel_path): if not filetools.isfile(channel_path):
channel_path = filetools.join(config.get_runtime_path(), 'channels', "porn", channel_name + ".json") channel_path = filetools.join(config.get_runtime_path(), 'channels', "porn", channel_name + ".json")
if not os.path.isfile(channel_path): if not filetools.isfile(channel_path):
channel_path = filetools.join(config.get_runtime_path(), "specials", channel_name + ".json") channel_path = filetools.join(config.get_runtime_path(), "specials", channel_name + ".json")
if not os.path.isfile(channel_path): if not filetools.isfile(channel_path):
channel_path = filetools.join(config.get_runtime_path(), "servers", channel_name + ".json") channel_path = filetools.join(config.get_runtime_path(), "servers", channel_name + ".json")
if not os.path.isfile(channel_path): if not filetools.isfile(channel_path):
channel_path = filetools.join(config.get_runtime_path(), "servers", "debriders", channel_name + ".json") channel_path = filetools.join(config.get_runtime_path(), "servers", "debriders",
channel_name + ".json")
if filetools.isfile(channel_path): if filetools.isfile(channel_path):
# logger.info("channel_data=" + channel_path) # logger.info("channel_data=" + channel_path)
channel_json = jsontools.load(filetools.read(channel_path)) channel_json = jsontools.load(filetools.read(channel_path))
# logger.info("channel_json= %s" % channel_json) # logger.info("channel_json= %s" % channel_json)
except Exception, ex: except Exception as ex:
template = "An exception of type %s occured. Arguments:\n%r" template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args) message = template % (type(ex).__name__, ex.args)
logger.error(" %s" % message) logger.error(" %s" % message)
@@ -174,6 +147,7 @@ def get_channel_controls_settings(channel_name):
return list_controls, dict_settings return list_controls, dict_settings
def get_lang(channel_name): def get_lang(channel_name):
channel = __import__('channels.%s' % channel_name, fromlist=["channels.%s" % channel_name]) channel = __import__('channels.%s' % channel_name, fromlist=["channels.%s" % channel_name])
list_language = [config.get_localized_string(70522)] list_language = [config.get_localized_string(70522)]
@@ -199,16 +173,17 @@ def get_lang(channel_name):
list_language.append(lang) list_language.append(lang)
return list_language return list_language
def get_default_settings(channel_name): def get_default_settings(channel_name):
import filetools from core import filetools
default_path = filetools.join(config.get_runtime_path(), 'default_channel_settings' + '.json') default_path = filetools.join(config.get_runtime_path(), 'default_channel_settings' + '.json')
default_file = jsontools.load(filetools.read(default_path)) default_file = jsontools.load(filetools.read(default_path))
channel_path = filetools.join(config.get_runtime_path(),'channels',channel_name + '.json') channel_path = filetools.join(config.get_runtime_path(), 'channels', channel_name + '.json')
adult_path = filetools.join(config.get_runtime_path(),'channels', 'porn', channel_name + '.json') adult_path = filetools.join(config.get_runtime_path(), 'channels', 'porn', channel_name + '.json')
# from core.support import dbg; dbg() # from core.support import dbg; dbg()
if os.path.exists(channel_path) or os.path.exists(adult_path): if filetools.exists(channel_path) or filetools.exists(adult_path):
default_controls = default_file['settings'] default_controls = default_file['settings']
default_controls_renumber = default_file['renumber'] default_controls_renumber = default_file['renumber']
channel_json = get_channel_json(channel_name) channel_json = get_channel_json(channel_name)
@@ -217,33 +192,43 @@ def get_default_settings(channel_name):
channel_language = channel_json['language'] channel_language = channel_json['language']
channel_controls = channel_json['settings'] channel_controls = channel_json['settings']
categories = channel_json['categories'] categories = channel_json['categories']
not_active = channel_json['not_active'] if channel_json.has_key('not_active') else [] not_active = channel_json['not_active'] if 'not_active' in channel_json else []
default_off = channel_json['default_off'] if channel_json.has_key('default_off') else [] default_off = channel_json['default_off'] if 'default_off' in channel_json else []
# Apply default configurations if they do not exist # Apply default configurations if they do not exist
for control in default_controls: for control in default_controls:
if control['id'] not in str(channel_controls): if control['id'] not in str(channel_controls):
if 'include_in_newest' in control['id'] and 'include_in_newest' not in not_active and control['id'] not in not_active: if 'include_in_newest' in control['id'] and 'include_in_newest' not in not_active and control[
'id'] not in not_active:
label = control['id'].split('_') label = control['id'].split('_')
label = label[-1] label = label[-1]
if label == 'peliculas': if label == 'peliculas':
if 'movie' in categories: if 'movie' in categories:
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(30122) control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(
control['default'] = False if ('include_in_newest' in default_off) or ('include_in_newest_peliculas' in default_off) else True 30122)
control['default'] = False if ('include_in_newest' in default_off) or (
'include_in_newest_peliculas' in default_off) else True
channel_controls.append(control) channel_controls.append(control)
else: pass else:
pass
elif label == 'series': elif label == 'series':
if 'tvshow' in categories: if 'tvshow' in categories:
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(30123) control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(
control['default'] = False if ('include_in_newest' in default_off) or ('include_in_newest_series' in default_off) else True 30123)
control['default'] = False if ('include_in_newest' in default_off) or (
'include_in_newest_series' in default_off) else True
channel_controls.append(control) channel_controls.append(control)
else: pass else:
pass
elif label == 'anime': elif label == 'anime':
if 'anime' in categories: if 'anime' in categories:
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(30124) control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(
control['default'] = False if ('include_in_newest' in default_off) or ('include_in_newest_anime' in default_off) else True 30124)
control['default'] = False if ('include_in_newest' in default_off) or (
'include_in_newest_anime' in default_off) else True
channel_controls.append(control) channel_controls.append(control)
else: pass else:
pass
else: else:
control['label'] = config.get_localized_string(70727) + ' - ' + label.capitalize() control['label'] = config.get_localized_string(70727) + ' - ' + label.capitalize()
@@ -259,13 +244,15 @@ def get_default_settings(channel_name):
for control in default_controls_renumber: for control in default_controls_renumber:
if control['id'] not in str(channel_controls): if control['id'] not in str(channel_controls):
channel_controls.append(control) channel_controls.append(control)
else: pass else:
pass
else: else:
return get_channel_json(channel_name).get('settings', list()) return get_channel_json(channel_name).get('settings', list())
return channel_controls return channel_controls
def get_channel_setting(name, channel, default=None): def get_channel_setting(name, channel, default=None):
from core import filetools
""" """
Retorna el valor de configuracion del parametro solicitado. Retorna el valor de configuracion del parametro solicitado.
@@ -288,13 +275,15 @@ def get_channel_setting(name, channel, default=None):
@rtype: any @rtype: any
""" """
file_settings = os.path.join(config.get_data_path(), "settings_channels", channel + "_data.json") file_settings = filetools.join(config.get_data_path(), "settings_channels", channel + "_data.json")
dict_settings = {} dict_settings = {}
dict_file = {} dict_file = {}
if os.path.exists(file_settings): if channel not in ['trakt']: def_settings = get_default_settings(channel)
if filetools.exists(file_settings):
# Obtenemos configuracion guardada de ../settings/channel_data.json # Obtenemos configuracion guardada de ../settings/channel_data.json
try: try:
dict_file = jsontools.load(open(file_settings, "rb").read()) dict_file = jsontools.load(filetools.read(file_settings))
if isinstance(dict_file, dict) and 'settings' in dict_file: if isinstance(dict_file, dict) and 'settings' in dict_file:
dict_settings = dict_file['settings'] dict_settings = dict_file['settings']
except EnvironmentError: except EnvironmentError:
@@ -313,9 +302,7 @@ def get_channel_setting(name, channel, default=None):
dict_file['settings'] = dict_settings dict_file['settings'] = dict_settings
# Creamos el archivo ../settings/channel_data.json # Creamos el archivo ../settings/channel_data.json
json_data = jsontools.dump(dict_file) json_data = jsontools.dump(dict_file)
try: if not filetools.write(file_settings, json_data, silent=True):
open(file_settings, "wb").write(json_data)
except EnvironmentError:
logger.error("ERROR al salvar el archivo: %s" % file_settings) logger.error("ERROR al salvar el archivo: %s" % file_settings)
# Devolvemos el valor del parametro local 'name' si existe, si no se devuelve default # Devolvemos el valor del parametro local 'name' si existe, si no se devuelve default
@@ -323,7 +310,7 @@ def get_channel_setting(name, channel, default=None):
def set_channel_setting(name, value, channel): def set_channel_setting(name, value, channel):
import filetools from core import filetools
""" """
Fija el valor de configuracion del parametro indicado. Fija el valor de configuracion del parametro indicado.
@@ -346,36 +333,22 @@ def set_channel_setting(name, value, channel):
""" """
# Creamos la carpeta si no existe # Creamos la carpeta si no existe
if not os.path.exists(os.path.join(config.get_data_path(), "settings_channels")): if not filetools.exists(filetools.join(config.get_data_path(), "settings_channels")):
os.mkdir(os.path.join(config.get_data_path(), "settings_channels")) filetools.mkdir(filetools.join(config.get_data_path(), "settings_channels"))
file_settings = os.path.join(config.get_data_path(), "settings_channels", channel + "_data.json") file_settings = filetools.join(config.get_data_path(), "settings_channels", channel + "_data.json")
dict_settings = {} dict_settings = {}
if channel not in ['trakt']: def_settings = get_default_settings(channel)
dict_file = None dict_file = None
if os.path.exists(file_settings): if filetools.exists(file_settings):
# Obtenemos configuracion guardada de ../settings/channel_data.json # Obtenemos configuracion guardada de ../settings/channel_data.json
try: try:
dict_file = jsontools.load(open(file_settings, "r").read()) dict_file = jsontools.load(filetools.read(file_settings))
dict_settings = dict_file.get('settings', {}) dict_settings = dict_file.get('settings', {})
except EnvironmentError: except EnvironmentError:
logger.error("ERROR al leer el archivo: %s" % file_settings) logger.error("ERROR al leer el archivo: %s" % file_settings)
if os.path.isfile(filetools.join(config.get_runtime_path(), "channels", channel + ".json")):
# delete unused Settings
def_keys = []
del_keys = []
for key in def_settings:
def_keys.append(key['id'])
for key in dict_settings:
if key not in def_keys:
del_keys.append(key)
for key in del_keys:
del dict_settings[key]
dict_settings[name] = value dict_settings[name] = value
# comprobamos si existe dict_file y es un diccionario, sino lo creamos # comprobamos si existe dict_file y es un diccionario, sino lo creamos
@@ -385,10 +358,8 @@ def set_channel_setting(name, value, channel):
dict_file['settings'] = dict_settings dict_file['settings'] = dict_settings
# Creamos el archivo ../settings/channel_data.json # Creamos el archivo ../settings/channel_data.json
try: json_data = jsontools.dump(dict_file)
json_data = jsontools.dump(dict_file) if not filetools.write(file_settings, json_data, silent=True):
open(file_settings, "w").write(json_data)
except EnvironmentError:
logger.error("ERROR al salvar el archivo: %s" % file_settings) logger.error("ERROR al salvar el archivo: %s" % file_settings)
return None return None
+3 -3
View File
@@ -20,8 +20,8 @@ metodos:
from __future__ import division from __future__ import division
from future import standard_library from future import standard_library
standard_library.install_aliases() standard_library.install_aliases()
from future.builtins import range from builtins import range
from future.builtins import object from builtins import object
from past.utils import old_div from past.utils import old_div
#from builtins import str #from builtins import str
import sys import sys
@@ -258,7 +258,7 @@ class Downloader(object):
self.__get_download_info__() self.__get_download_info__()
try: try:
logger.info("Initialized Download: Parts: %s | Path: %s | Archive: %s | Size: %s" % \ logger.info("Descarga inicializada: Partes: %s | Ruta: %s | Archivo: %s | Tamaño: %s" % \
(str(len(self._download_info["parts"])), self._pathencode('utf-8'), \ (str(len(self._download_info["parts"])), self._pathencode('utf-8'), \
self._filenameencode('utf-8'), str(self._download_info["size"]))) self._filenameencode('utf-8'), str(self._download_info["size"])))
except: except:
+14 -8
View File
@@ -110,6 +110,8 @@ def limpia_nombre_excepto_1(s):
stripped = ''.join(c for c in s if c in validchars) stripped = ''.join(c for c in s if c in validchars)
# Convierte a iso # Convierte a iso
s = stripped.encode("iso-8859-1") s = stripped.encode("iso-8859-1")
if PY3:
s = s.decode('utf-8')
return s return s
@@ -129,7 +131,7 @@ def getfilefromtitle(url, title):
logger.info("platform=" + plataforma) logger.info("platform=" + plataforma)
# nombrefichero = xbmc.makeLegalFilename(title + url[-4:]) # nombrefichero = xbmc.makeLegalFilename(title + url[-4:])
from . import scrapertools from core import scrapertools
nombrefichero = title + scrapertools.get_filename_from_url(url)[-4:] nombrefichero = title + scrapertools.get_filename_from_url(url)[-4:]
logger.info("filename=%s" % nombrefichero) logger.info("filename=%s" % nombrefichero)
@@ -169,7 +171,10 @@ def downloadbest(video_urls, title, continuar=False):
for elemento in invertida: for elemento in invertida:
# videotitle = elemento[0] # videotitle = elemento[0]
url = elemento[1] url = elemento[1]
logger.info("Downloading option " + title + " " + url.encode('ascii', 'ignore')) if not PY3:
logger.info("Downloading option " + title + " " + url.encode('ascii', 'ignore'))
else:
logger.info("Downloading option " + title + " " + url.encode('ascii', 'ignore').decode('utf-8'))
# Calcula el fichero donde debe grabar # Calcula el fichero donde debe grabar
try: try:
@@ -621,7 +626,7 @@ def downloadfileGzipped(url, pathfichero):
break break
except: except:
reintentos += 1 reintentos += 1
logger.info("ERROR in block download, retry %dd" % reintentos) logger.info("ERROR in block download, retry %d" % reintentos)
for line in sys.exc_info(): for line in sys.exc_info():
logger.error("%s" % line) logger.error("%s" % line)
@@ -660,7 +665,7 @@ def GetTitleFromFile(title):
# Imprime en el log lo que va a descartar # Imprime en el log lo que va a descartar
logger.info("title=" + title) logger.info("title=" + title)
plataforma = config.get_system_platform() plataforma = config.get_system_platform()
logger.info("platform=" + plataforma) logger.info("plataform=" + plataforma)
# nombrefichero = xbmc.makeLegalFilename(title + url[-4:]) # nombrefichero = xbmc.makeLegalFilename(title + url[-4:])
nombrefichero = title nombrefichero = title
@@ -678,7 +683,7 @@ def downloadIfNotModifiedSince(url, timestamp):
# Convierte la fecha a GMT # Convierte la fecha a GMT
fecha_formateada = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(timestamp)) fecha_formateada = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(timestamp))
logger.info("DateFormat=%s" % fecha_formateada) logger.info("fechaFormateada=%s" % fecha_formateada)
# Comprueba si ha cambiado # Comprueba si ha cambiado
inicio = time.clock() inicio = time.clock()
@@ -700,7 +705,7 @@ def downloadIfNotModifiedSince(url, timestamp):
except urllib.error.URLError as e: except urllib.error.URLError as e:
# Si devuelve 304 es que no ha cambiado # Si devuelve 304 es que no ha cambiado
if hasattr(e, 'code'): if hasattr(e, 'code'):
logger.info("HTTP response code: %d" % e.code) logger.info("HTTP response code : %d" % e.code)
if e.code == 304: if e.code == 304:
logger.info("It has not changed") logger.info("It has not changed")
updated = False updated = False
@@ -814,6 +819,7 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
for mirror_item in mirrors_itemlist: for mirror_item in mirrors_itemlist:
logger.info("mirror=" + mirror_item.title) logger.info("mirror=" + mirror_item.title)
if "(Italiano)" in mirror_item.title: if "(Italiano)" in mirror_item.title:
idioma = "(Italiano)" idioma = "(Italiano)"
codigo_idioma = "it" codigo_idioma = "it"
@@ -885,8 +891,8 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
def episodio_ya_descargado(show_title, episode_title): def episodio_ya_descargado(show_title, episode_title):
from . import scrapertools from core import scrapertools
ficheros = os.listdir(".") ficheros = filetools.listdir(".")
for fichero in ficheros: for fichero in ficheros:
# logger.info("fichero="+fichero) # logger.info("fichero="+fichero)
+35 -36
View File
@@ -87,7 +87,7 @@ def encode(path, _samba=False):
if scrapertools.find_single_match(path, '(^\w+:\/\/)') or _samba: if scrapertools.find_single_match(path, '(^\w+:\/\/)') or _samba:
path = path.encode("utf-8", "ignore") path = path.encode("utf-8", "ignore")
else: else:
if fs_encoding: if fs_encoding and not PY3:
path = path.encode(fs_encoding, "ignore") path = path.encode(fs_encoding, "ignore")
return path return path
@@ -133,13 +133,13 @@ def read(path, linea_inicio=0, total_lineas=None, whence=0, silent=False, vfs=Tr
try: try:
linea_inicio = int(linea_inicio) linea_inicio = int(linea_inicio)
except: except:
logger.error('Read: ERROR de linea_inicio: %s' % str(linea_inicio)) logger.error('Read: Start_line ERROR: %s' % str(linea_inicio))
linea_inicio = 0 linea_inicio = 0
if total_lineas != None and not isinstance(total_lineas, int): if total_lineas != None and not isinstance(total_lineas, int):
try: try:
total_lineas = int(total_lineas) total_lineas = int(total_lineas)
except: except:
logger.error('Read: ERROR de total_lineas: %s' % str(total_lineas)) logger.error('Read: ERROR of total_lineas: %s' % str(total_lineas))
total_lineas = None total_lineas = None
if xbmc_vfs and vfs: if xbmc_vfs and vfs:
if not exists(path): return False if not exists(path): return False
@@ -151,7 +151,7 @@ def read(path, linea_inicio=0, total_lineas=None, whence=0, silent=False, vfs=Tr
except: except:
return False return False
f.seek(linea_inicio, whence) f.seek(linea_inicio, whence)
logger.debug('POSICIÓN de comienzo de lectura, tell(): %s' % f.seek(0, 1)) logger.debug('POSITION of beginning of reading,, tell(): %s' % f.seek(0, 1))
if total_lineas == None: if total_lineas == None:
total_lineas = 0 total_lineas = 0
data = f.read(total_lineas) data = f.read(total_lineas)
@@ -169,15 +169,15 @@ def read(path, linea_inicio=0, total_lineas=None, whence=0, silent=False, vfs=Tr
f.close() f.close()
except: except:
if not silent: if not silent:
logger.error("ERROR al leer el archivo: %s" % path) logger.error("ERROR reading file: %s" % path)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
else: else:
if not PY3: if not PY3:
return "".join(data) return unicode("".join(data))
else: else:
return b"".join(data) return unicode(b"".join(data))
def write(path, data, mode="wb", silent=False, vfs=True): def write(path, data, mode="wb", silent=False, vfs=True):
@@ -226,20 +226,20 @@ def file_open(path, mode="r", silent=False, vfs=True):
if xbmc_vfs and vfs: if xbmc_vfs and vfs:
if 'r' in mode and '+' in mode: if 'r' in mode and '+' in mode:
mode = mode.replace('r', 'w').replace('+', '') mode = mode.replace('r', 'w').replace('+', '')
logger.debug('Open MODE cambiado a: %s' % mode) logger.debug('Open MODE changed to: %s' % mode)
if 'a' in mode: if 'a' in mode:
mode = mode.replace('a', 'w').replace('+', '') mode = mode.replace('a', 'w').replace('+', '')
logger.debug('Open MODE cambiado a: %s' % mode) logger.debug('Open MODE changed to: %s' % mode)
return xbmcvfs.File(path, mode) return xbmcvfs.File(path, mode)
elif path.lower().startswith("smb://"): elif path.lower().startswith("smb://"):
return samba.smb_open(path, mode) return samba.smb_open(path, mode)
else: else:
return open(path, mode) return open(path, mode)
except: except:
logger.error("ERROR al abrir el archivo: %s, %s" % (path, mode)) logger.error("ERROR when opening file: %s, %s" % (path, mode))
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
platformtools.dialog_notification("Error al abrir", path) platformtools.dialog_notification("Error Opening", path)
return False return False
@@ -258,7 +258,7 @@ def file_stat(path, silent=False, vfs=True):
return xbmcvfs.Stat(path) return xbmcvfs.Stat(path)
raise raise
except: except:
logger.error("File_Stat no soportado: %s" % path) logger.error("File_Stat not supported: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -283,9 +283,9 @@ def rename(path, new_name, silent=False, strict=False, vfs=True):
dest = encode(join(dirname(path_end), new_name)) dest = encode(join(dirname(path_end), new_name))
result = xbmcvfs.rename(path, dest) result = xbmcvfs.rename(path, dest)
if not result and not strict: if not result and not strict:
logger.error("ERROR al RENOMBRAR el archivo: %s. Copiando y borrando" % path) logger.error("ERROR RENAME file: %s. Copying and deleting" % path)
if not silent: if not silent:
dialogo = platformtools.dialog_progress("Copiando archivo", "") dialogo = platformtools.dialog_progress("Copying file", "")
result = xbmcvfs.copy(path, dest) result = xbmcvfs.copy(path, dest)
if not result: if not result:
return False return False
@@ -298,10 +298,10 @@ def rename(path, new_name, silent=False, strict=False, vfs=True):
new_name = encode(new_name, False) new_name = encode(new_name, False)
os.rename(path, os.path.join(os.path.dirname(path), new_name)) os.rename(path, os.path.join(os.path.dirname(path), new_name))
except: except:
logger.error("ERROR al renombrar el archivo: %s" % path) logger.error("ERROR when renaming the file: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
platformtools.dialog_notification("Error al renombrar", path) platformtools.dialog_notification("Error renaming", path)
return False return False
else: else:
return True return True
@@ -324,9 +324,9 @@ def move(path, dest, silent=False, strict=False, vfs=True):
dest = encode(dest) dest = encode(dest)
result = xbmcvfs.rename(path, dest) result = xbmcvfs.rename(path, dest)
if not result and not strict: if not result and not strict:
logger.error("ERROR al MOVER el archivo: %s. Copiando y borrando" % path) logger.error("ERROR when MOVING the file: %s. Copying and deleting" % path)
if not silent: if not silent:
dialogo = platformtools.dialog_progress("Copiando archivo", "") dialogo = platformtools.dialog_progress("Copying file", "")
result = xbmcvfs.copy(path, dest) result = xbmcvfs.copy(path, dest)
if not result: if not result:
return False return False
@@ -349,7 +349,7 @@ def move(path, dest, silent=False, strict=False, vfs=True):
dialogo = platformtools.dialog_progress("Copiando archivo", "") dialogo = platformtools.dialog_progress("Copiando archivo", "")
return copy(path, dest) == True and remove(path) == True return copy(path, dest) == True and remove(path) == True
except: except:
logger.error("ERROR al mover el archivo: %s a %s" % (path, dest)) logger.error("ERROR when moving file: %s to %s" % (path, dest))
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -398,7 +398,7 @@ def copy(path, dest, silent=False, vfs=True):
if not silent: if not silent:
dialogo.close() dialogo.close()
except: except:
logger.error("ERROR al copiar el archivo: %s" % path) logger.error("ERROR when copying the file: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -426,7 +426,7 @@ def exists(path, silent=False, vfs=True):
else: else:
return os.path.exists(path) return os.path.exists(path)
except: except:
logger.error("ERROR al comprobar la ruta: %s" % path) logger.error("ERROR when checking the path: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -458,7 +458,7 @@ def isfile(path, silent=False, vfs=True):
else: else:
return os.path.isfile(path) return os.path.isfile(path)
except: except:
logger.error("ERROR al comprobar el archivo: %s" % path) logger.error("ERROR when checking file: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -490,7 +490,7 @@ def isdir(path, silent=False, vfs=True):
else: else:
return os.path.isdir(path) return os.path.isdir(path)
except: except:
logger.error("ERROR al comprobar el directorio: %s" % path) logger.error("ERROR when checking the directory: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -517,7 +517,7 @@ def getsize(path, silent=False, vfs=True):
else: else:
return os.path.getsize(path) return os.path.getsize(path)
except: except:
logger.error("ERROR al obtener el tamaño: %s" % path) logger.error("ERROR when getting the size: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return long(0) return long(0)
@@ -540,10 +540,10 @@ def remove(path, silent=False, vfs=True):
else: else:
os.remove(path) os.remove(path)
except: except:
logger.error("ERROR al eliminar el archivo: %s" % path) logger.error("ERROR deleting file: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
platformtools.dialog_notification("Error al eliminar el archivo", path) platformtools.dialog_notification("ERROR deleting file", path)
return False return False
else: else:
return True return True
@@ -580,10 +580,10 @@ def rmdirtree(path, silent=False, vfs=True):
import shutil import shutil
shutil.rmtree(path, ignore_errors=True) shutil.rmtree(path, ignore_errors=True)
except: except:
logger.error("ERROR al eliminar el directorio: %s" % path) logger.error("ERROR deleting directory: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
platformtools.dialog_notification("Error al eliminar el directorio", path) platformtools.dialog_notification("ERROR deleting directory", path)
return False return False
else: else:
return not exists(path) return not exists(path)
@@ -608,10 +608,10 @@ def rmdir(path, silent=False, vfs=True):
else: else:
os.rmdir(path) os.rmdir(path)
except: except:
logger.error("ERROR al eliminar el directorio: %s" % path) logger.error("ERROR deleting directory: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
platformtools.dialog_notification("Error al eliminar el directorio", path) platformtools.dialog_notification("ERROR deleting directory", path)
return False return False
else: else:
return True return True
@@ -641,10 +641,10 @@ def mkdir(path, silent=False, vfs=True):
else: else:
os.mkdir(path) os.mkdir(path)
except: except:
logger.error("ERROR al crear el directorio: %s" % path) logger.error("ERROR when creating directory: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
platformtools.dialog_notification("Error al crear el directorio", path) platformtools.dialog_notification("ERROR when creating directory", path)
return False return False
else: else:
return True return True
@@ -724,7 +724,7 @@ def listdir(path, silent=False, vfs=True):
else: else:
return decode(os.listdir(path)) return decode(os.listdir(path))
except: except:
logger.error("ERROR al leer el directorio: %s" % path) logger.error("ERROR when reading the directory: %s" % path)
if not silent: if not silent:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
@@ -740,14 +740,13 @@ def join(*paths):
list_path = [] list_path = []
if paths[0].startswith("/"): if paths[0].startswith("/"):
list_path.append("") list_path.append("")
for path in paths: for path in paths:
if path: if path:
if xbmc_vfs: if xbmc_vfs and type(path) != str:
path = encode(path) path = encode(path)
list_path += path.replace("\\", "/").strip("/").split("/") list_path += path.replace("\\", "/").strip("/").split("/")
if scrapertools.find_single_match(paths[0], '(^\w+:\/\/)'): if scrapertools.find_single_match(paths[0], r'(^\w+:\/\/)'):
return str("/".join(list_path)) return str("/".join(list_path))
else: else:
return str(os.sep.join(list_path)) return str(os.sep.join(list_path))
+8 -6
View File
@@ -31,7 +31,7 @@ cookies_file = os.path.join(config.get_data_path(), "cookies.dat")
default_headers = dict() default_headers = dict()
default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" default_headers["User-Agent"] = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 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"] = "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-Language"] = "it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3"
default_headers["Accept-Charset"] = "UTF-8" default_headers["Accept-Charset"] = "UTF-8"
default_headers["Accept-Encoding"] = "gzip" default_headers["Accept-Encoding"] = "gzip"
@@ -255,13 +255,11 @@ def downloadpage(url, **opt):
domain = urlparse.urlparse(url).netloc domain = urlparse.urlparse(url).netloc
CF = False CF = False
if domain in ['www.guardaserie.media', 'casacinema.space', 'wstream.video', 'akvideo.stream', 'backin.net', if domain in ['www.guardaserie.media', 'casacinema.space', 'wstream.video', 'akvideo.stream', 'backin.net',
'dreamsub.stream', 'altadefinizione-nuovo.link', 'ilgeniodellostreaming.si', 'www.piratestreaming.gratis']: 'dreamsub.stream', 'altadefinizione-nuovo.link', 'ilgeniodellostreaming.si', 'www.piratestreaming.gratis',
'altadefinizione.style']:
from lib import cloudscraper from lib import cloudscraper
session = cloudscraper.create_scraper() session = cloudscraper.create_scraper()
CF = True CF = True
elif opt.get('session', False):
session = opt['session'] # same session to speed up search
logger.info('same session')
else: else:
from lib import requests from lib import requests
session = requests.session() session = requests.session()
@@ -360,6 +358,7 @@ def downloadpage(url, **opt):
timeout=opt['timeout']) timeout=opt['timeout'])
except Exception as e: except Exception as e:
from lib import requests from lib import requests
req = requests.Response()
if not opt.get('ignore_response_code', False) and not proxy_data.get('stat', ''): if not opt.get('ignore_response_code', False) and not proxy_data.get('stat', ''):
response['data'] = '' response['data'] = ''
response['sucess'] = False response['sucess'] = False
@@ -371,7 +370,6 @@ def downloadpage(url, **opt):
show_infobox(info_dict) show_infobox(info_dict)
return type('HTTPResponse', (), response) return type('HTTPResponse', (), response)
else: else:
req = requests.Response()
req.status_code = str(e) req.status_code = str(e)
else: else:
@@ -384,6 +382,10 @@ def downloadpage(url, **opt):
response['data'] = req.content response['data'] = req.content
response['url'] = req.url response['url'] = req.url
if type(response['data']) != str:
response['data'] = response['data'].decode('UTF-8')
if not response['data']: if not response['data']:
response['data'] = '' response['data'] = ''
try: try:
+54 -26
View File
@@ -3,12 +3,23 @@
# Item is the object we use for representing data # Item is the object we use for representing data
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
#from builtins import str
from future.builtins import object
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
#from future import standard_library
#standard_library.install_aliases()
import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo
from html.parser import HTMLParser
else:
import urllib # Usamos el nativo de PY2 que es más rápido
from HTMLParser import HTMLParser
import base64 import base64
import copy import copy
import os
import urllib
from HTMLParser import HTMLParser
from core import jsontools as json from core import jsontools as json
@@ -58,12 +69,12 @@ class InfoLabels(dict):
elif key == 'code': elif key == 'code':
code = [] code = []
# Añadir imdb_id al listado de codigos # Añadir imdb_id al listado de codigos
if 'imdb_id' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('imdb_id'): if 'imdb_id' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('imdb_id'):
code.append(super(InfoLabels, self).__getitem__('imdb_id')) code.append(super(InfoLabels, self).__getitem__('imdb_id'))
# Completar con el resto de codigos # Completar con el resto de codigos
for scr in ['tmdb_id', 'tvdb_id', 'noscrap_id']: for scr in ['tmdb_id', 'tvdb_id', 'noscrap_id']:
if scr in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__(scr): if scr in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__(scr):
value = "%s%s" % (scr[:-2], super(InfoLabels, self).__getitem__(scr)) value = "%s%s" % (scr[:-2], super(InfoLabels, self).__getitem__(scr))
code.append(value) code.append(value)
@@ -78,21 +89,21 @@ class InfoLabels(dict):
elif key == 'mediatype': elif key == 'mediatype':
# "list", "movie", "tvshow", "season", "episode" # "list", "movie", "tvshow", "season", "episode"
if 'tvshowtitle' in super(InfoLabels, self).keys() \ if 'tvshowtitle' in list(super(InfoLabels, self).keys()) \
and super(InfoLabels, self).__getitem__('tvshowtitle') != "": and super(InfoLabels, self).__getitem__('tvshowtitle') != "":
if 'episode' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('episode') != "": if 'episode' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('episode') != "":
return 'episode' return 'episode'
if 'episodeName' in super(InfoLabels, self).keys() \ if 'episodeName' in list(super(InfoLabels, self).keys()) \
and super(InfoLabels, self).__getitem__('episodeName') != "": and super(InfoLabels, self).__getitem__('episodeName') != "":
return 'episode' return 'episode'
if 'season' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('season') != "": if 'season' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('season') != "":
return 'season' return 'season'
else: else:
return 'tvshow' return 'tvshow'
elif 'title' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('title') != "": elif 'title' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('title') != "":
return 'movie' return 'movie'
else: else:
@@ -104,7 +115,7 @@ class InfoLabels(dict):
def tostring(self, separador=', '): def tostring(self, separador=', '):
ls = [] ls = []
dic = dict(super(InfoLabels, self).items()) dic = dict(list(super(InfoLabels, self).items()))
for i in sorted(dic.items()): for i in sorted(dic.items()):
i_str = str(i)[1:-1] i_str = str(i)[1:-1]
@@ -158,6 +169,7 @@ class Item(object):
Función llamada al modificar cualquier atributo del item, modifica algunos atributos en función de los datos Función llamada al modificar cualquier atributo del item, modifica algunos atributos en función de los datos
modificados. modificados.
""" """
if PY3: name = self.toutf8(name)
value = self.toutf8(value) value = self.toutf8(value)
if name == "__dict__": if name == "__dict__":
for key in value: for key in value:
@@ -313,9 +325,13 @@ class Item(object):
valor = dic[var].tostring(',\r\t\t') valor = dic[var].tostring(',\r\t\t')
else: else:
valor = dic[var].tostring() valor = dic[var].tostring()
elif PY3 and isinstance(dic[var], bytes):
valor = "'%s'" % dic[var].decode('utf-8')
else: else:
valor = str(dic[var]) valor = str(dic[var])
if PY3 and isinstance(var, bytes):
var = var.decode('utf-8')
ls.append(var + "= " + valor) ls.append(var + "= " + valor)
return separator.join(ls) return separator.join(ls)
@@ -327,12 +343,12 @@ class Item(object):
Uso: url = item.tourl() Uso: url = item.tourl()
""" """
dump = json.dump(self.__dict__) dump = json.dump(self.__dict__).encode("utf8")
# if empty dict # if empty dict
if not dump: if not dump:
# set a str to avoid b64encode fails # set a str to avoid b64encode fails
dump = "" dump = "".encode("utf8")
return urllib.quote(base64.b64encode(dump)) return str(urllib.quote(base64.b64encode(dump)))
def fromurl(self, url): def fromurl(self, url):
""" """
@@ -367,6 +383,7 @@ class Item(object):
return self return self
def tojson(self, path=""): def tojson(self, path=""):
from core import filetools
""" """
Crea un JSON a partir del item, para guardar archivos de favoritos, lista de descargas, etc... Crea un JSON a partir del item, para guardar archivos de favoritos, lista de descargas, etc...
Si se especifica un path, te lo guarda en la ruta especificada, si no, devuelve la cadena json Si se especifica un path, te lo guarda en la ruta especificada, si no, devuelve la cadena json
@@ -377,11 +394,13 @@ class Item(object):
@type path: str @type path: str
""" """
if path: if path:
open(path, "wb").write(json.dump(self.__dict__)) #open(path, "wb").write(json.dump(self.__dict__))
res = filetools.write(path, json.dump(self.__dict__))
else: else:
return json.dump(self.__dict__) return json.dump(self.__dict__)
def fromjson(self, json_item=None, path=""): def fromjson(self, json_item=None, path=""):
from core import filetools
""" """
Genera un item a partir de un archivo JSON Genera un item a partir de un archivo JSON
Si se especifica un path, lee directamente el archivo, si no, lee la cadena de texto pasada. Si se especifica un path, lee directamente el archivo, si no, lee la cadena de texto pasada.
@@ -394,8 +413,9 @@ class Item(object):
@type path: str @type path: str
""" """
if path: if path:
if os.path.exists(path): if filetools.exists(path):
json_item = open(path, "rb").read() #json_item = open(path, "rb").read()
json_item = filetools.read(path)
else: else:
json_item = {} json_item = {}
@@ -436,6 +456,8 @@ class Item(object):
unicode_title = unicode(value, "utf8", "ignore") unicode_title = unicode(value, "utf8", "ignore")
return HTMLParser().unescape(unicode_title).encode("utf8") return HTMLParser().unescape(unicode_title).encode("utf8")
except: except:
if PY3 and isinstance(value, bytes):
value = value.decode("utf8")
return value return value
def toutf8(self, *args): def toutf8(self, *args):
@@ -447,13 +469,18 @@ class Item(object):
else: else:
value = self.__dict__ value = self.__dict__
if type(value) == unicode: if isinstance(value, unicode):
return value.encode("utf8") value = value.encode("utf8")
if PY3: value = value.decode("utf8")
return value
elif type(value) == str: elif not PY3 and isinstance(value, str):
return unicode(value, "utf8", "ignore").encode("utf8") return unicode(value, "utf8", "ignore").encode("utf8")
elif type(value) == list: elif PY3 and isinstance(value, bytes):
return value.decode("utf8")
elif isinstance(value, list):
for x, key in enumerate(value): for x, key in enumerate(value):
value[x] = self.toutf8(value[x]) value[x] = self.toutf8(value[x])
return value return value
@@ -461,11 +488,12 @@ class Item(object):
elif isinstance(value, dict): elif isinstance(value, dict):
newdct = {} newdct = {}
for key in value: for key in value:
v = self.toutf8(value[key]) value_unc = self.toutf8(value[key])
if type(key) == unicode: key_unc = self.toutf8(key)
key = key.encode("utf8") #if isinstance(key, unicode):
# key = key.encode("utf8")
newdct[key] = v newdct[key_unc] = value_unc
if len(args) > 0: if len(args) > 0:
if isinstance(value, InfoLabels): if isinstance(value, InfoLabels):
+29 -21
View File
@@ -10,24 +10,28 @@ from platformcode import logger
try: try:
import json import json
except: except:
logger.info("json incluido en el interprete **NO** disponible") logger.info("json included in the interpreter **NOT** available")
try: try:
import simplejson as json import simplejson as json
except: except:
logger.info("simplejson incluido en el interprete **NO** disponible") logger.info("simplejson included in the interpreter **NOT** available")
try: try:
from lib import simplejson as json from lib import simplejson as json
except: except:
logger.info("simplejson en el directorio lib **NO** disponible") logger.info("simplejson in lib directory **NOT** available")
logger.error("No se ha encontrado un parser de JSON valido") logger.error("A valid JSON parser was not found")
json = None json = None
else: else:
logger.info("Usando simplejson en el directorio lib") logger.info("Using simplejson in the lib directory")
else: else:
logger.info("Usando simplejson incluido en el interprete") logger.info("Using simplejson included in the interpreter")
else: # ~ else:
logger.info("Usando json incluido en el interprete") # ~ logger.info("Usando json incluido en el interprete")
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
def load(*args, **kwargs): def load(*args, **kwargs):
@@ -37,7 +41,7 @@ def load(*args, **kwargs):
try: try:
value = json.loads(*args, **kwargs) value = json.loads(*args, **kwargs)
except: except:
logger.error("**NO** se ha podido cargar el JSON") logger.error("**NOT** able to load the JSON")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
value = {} value = {}
@@ -46,12 +50,12 @@ def load(*args, **kwargs):
def dump(*args, **kwargs): def dump(*args, **kwargs):
if not kwargs: if not kwargs:
kwargs = {"indent": 4, "skipkeys": True, "sort_keys": True, "ensure_ascii": False} kwargs = {"indent": 4, "skipkeys": True, "sort_keys": True, "ensure_ascii": True}
try: try:
value = json.dumps(*args, **kwargs) value = json.dumps(*args, **kwargs)
except: except:
logger.error("**NO** se ha podido cargar el JSON") logger.error("JSON could **NOT** be saved")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
value = "" value = ""
return value return value
@@ -59,11 +63,15 @@ def dump(*args, **kwargs):
def to_utf8(dct): def to_utf8(dct):
if isinstance(dct, dict): if isinstance(dct, dict):
return dict((to_utf8(key), to_utf8(value)) for key, value in dct.iteritems()) return dict((to_utf8(key), to_utf8(value)) for key, value in dct.items())
elif isinstance(dct, list): elif isinstance(dct, list):
return [to_utf8(element) for element in dct] return [to_utf8(element) for element in dct]
elif isinstance(dct, unicode): elif isinstance(dct, unicode):
return dct.encode('utf-8') dct = dct.encode("utf8")
if PY3: dct = dct.decode("utf8")
return dct
elif PY3 and isinstance(dct, bytes):
return dct.decode('utf-8')
else: else:
return dct return dct
@@ -124,18 +132,18 @@ def check_to_backup(data, fname, dict_data):
logger.info() logger.info()
if not dict_data: if not dict_data:
logger.error("Error al cargar el json del fichero %s" % fname) logger.error("Error loading json from file %s" % fname)
if data != "": if data != "":
# se crea un nuevo fichero # se crea un nuevo fichero
from core import filetools from core import filetools
title = filetools.write("%s.bk" % fname, data) title = filetools.write("%s.bk" % fname, data)
if title != "": if title != "":
logger.error("Ha habido un error al guardar el fichero: %s.bk" % fname) logger.error("There was an error saving the file: %s.bk" % fname)
else: else:
logger.debug("Se ha guardado una copia con el nombre: %s.bk" % fname) logger.debug("A copy with the name has been saved: %s.bk" % fname)
else: else:
logger.debug("Está vacío el fichero: %s" % fname) logger.debug("The file is empty: %s" % fname)
def update_node(dict_node, name_file, node, path=None): def update_node(dict_node, name_file, node, path=None):
@@ -175,18 +183,18 @@ def update_node(dict_node, name_file, node, path=None):
# es un dict # es un dict
if dict_data: if dict_data:
if node in dict_data: if node in dict_data:
logger.debug(" existe el key %s" % node) logger.debug(" the key exists %s" % node)
dict_data[node] = dict_node dict_data[node] = dict_node
else: else:
logger.debug(" NO existe el key %s" % node) logger.debug(" The key does NOT exist %s" % node)
new_dict = {node: dict_node} new_dict = {node: dict_node}
dict_data.update(new_dict) dict_data.update(new_dict)
else: else:
logger.debug(" NO es un dict") logger.debug(" It is NOT a dict")
dict_data = {node: dict_node} dict_data = {node: dict_node}
json_data = dump(dict_data) json_data = dump(dict_data)
result = filetools.write(fname, json_data) result = filetools.write(fname, json_data)
except: except:
logger.error("No se ha podido actualizar %s" % fname) logger.error("Could not update %s" % fname)
return result, json_data return result, json_data
+11 -6
View File
@@ -1,5 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
#from builtins import str
from core.item import InfoLabels from core.item import InfoLabels
from platformcode import config, logger from platformcode import config, logger
from platformcode import platformtools from platformcode import platformtools
@@ -46,7 +51,7 @@ def find_and_set_infoLabels(item):
try: try:
scraper = __import__('core.%s' % scraper_actual, fromlist=["core.%s" % scraper_actual]) scraper = __import__('core.%s' % scraper_actual, fromlist=["core.%s" % scraper_actual])
except ImportError: except ImportError:
exec "import core." + scraper_actual + " as scraper" exec("import core." + scraper_actual + " as scraper")
except: except:
import traceback import traceback
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
@@ -99,9 +104,9 @@ def find_and_set_infoLabels(item):
return True return True
# raise # raise
elif list_opciones_cuadro[index] in scrapers_disponibles.values(): elif list_opciones_cuadro[index] in list(scrapers_disponibles.values()):
# Obtener el nombre del modulo del scraper # Obtener el nombre del modulo del scraper
for k, v in scrapers_disponibles.items(): for k, v in list(scrapers_disponibles.items()):
if list_opciones_cuadro[index] == v: if list_opciones_cuadro[index] == v:
if scrapers_disponibles[scraper_actual] not in list_opciones_cuadro: if scrapers_disponibles[scraper_actual] not in list_opciones_cuadro:
list_opciones_cuadro.append(scrapers_disponibles[scraper_actual]) list_opciones_cuadro.append(scrapers_disponibles[scraper_actual])
@@ -111,7 +116,7 @@ def find_and_set_infoLabels(item):
scraper = None scraper = None
scraper = __import__('core.%s' % scraper_actual, fromlist=["core.%s" % scraper_actual]) scraper = __import__('core.%s' % scraper_actual, fromlist=["core.%s" % scraper_actual])
except ImportError: except ImportError:
exec "import core." + scraper_actual + " as scraper_module" exec("import core." + scraper_actual + " as scraper_module")
break break
logger.error("Error al importar el modulo scraper %s" % scraper_actual) logger.error("Error al importar el modulo scraper %s" % scraper_actual)
@@ -175,7 +180,7 @@ def cuadro_completar(item):
if not dict_default[c[0]] or dict_default[c[0]] == 'None' or dict_default[c[0]] == 0: if not dict_default[c[0]] or dict_default[c[0]] == 'None' or dict_default[c[0]] == 0:
dict_default[c[0]] = '' dict_default[c[0]] = ''
elif isinstance(dict_default[c[0]], (int, float, long)): elif isinstance(dict_default[c[0]], (int, float)) or (not PY3 and isinstance(dict_default[c[0]], (int, float, long))):
# Si es numerico lo convertimos en str # Si es numerico lo convertimos en str
dict_default[c[0]] = str(dict_default[c[0]]) dict_default[c[0]] = str(dict_default[c[0]])
@@ -204,7 +209,7 @@ def callback_cuadro_completar(item, dict_values):
if dict_values.get("title", None): if dict_values.get("title", None):
# Adaptar dict_values a infoLabels validos # Adaptar dict_values a infoLabels validos
dict_values['mediatype'] = ['movie', 'tvshow'][dict_values['mediatype']] dict_values['mediatype'] = ['movie', 'tvshow'][dict_values['mediatype']]
for k, v in dict_values.items(): for k, v in list(dict_values.items()):
if k in dict_default and dict_default[k] == dict_values[k]: if k in dict_default and dict_default[k] == dict_values[k]:
del dict_values[k] del dict_values[k]
+80 -5
View File
@@ -1,17 +1,36 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
# Scraper tools v2 for reading and processing web elements # Scraper tools for reading and processing web elements
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
#from future import standard_library
#standard_library.install_aliases()
#from builtins import str
#from builtins import chr
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import re import re
import time import time
import urlparse # from core import httptools
from core.entities import html5 from core.entities import html5
from platformcode import logger from platformcode import logger
# def get_header_from_response(url, header_to_get="", post=None, headers=None):
# header_to_get = header_to_get.lower()
# response = httptools.downloadpage(url, post=post, headers=headers, only_headers=True)
# return response.headers.get(header_to_get)
# def read_body_and_headers(url, post=None, headers=None, follow_redirects=False, timeout=None):
# response = httptools.downloadpage(url, post=post, headers=headers, follow_redirects=follow_redirects,
# timeout=timeout)
# return response.data, response.headers
def printMatches(matches): def printMatches(matches):
i = 0 i = 0
for match in matches: for match in matches:
@@ -89,7 +108,10 @@ def unescape(text):
else: else:
# named entity # named entity
try: try:
import htmlentitydefs if PY3:
import html.entities as htmlentitydefs
else:
import htmlentitydefs
text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]).encode("utf-8") text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]).encode("utf-8")
except KeyError: except KeyError:
logger.error("keyerror") logger.error("keyerror")
@@ -103,6 +125,50 @@ def unescape(text):
# Convierte los codigos html "&ntilde;" y lo reemplaza por "ñ" caracter unicode utf-8 # Convierte los codigos html "&ntilde;" y lo reemplaza por "ñ" caracter unicode utf-8
# def decodeHtmlentities(string):
# string = entitiesfix(string)
# entity_re = re.compile("&(#?)(\d{1,5}|\w{1,8});")
# def substitute_entity(match):
# if PY3:
# from html.entities import name2codepoint as n2cp
# else:
# from htmlentitydefs import name2codepoint as n2cp
# ent = match.group(2)
# if match.group(1) == "#":
# return unichr(int(ent)).encode('utf-8')
# else:
# cp = n2cp.get(ent)
# if cp:
# return unichr(cp).encode('utf-8')
# else:
# return match.group()
# return entity_re.subn(substitute_entity, string)[0]
# def entitiesfix(string):
# # Las entidades comienzan siempre con el símbolo & , y terminan con un punto y coma ( ; ).
# string = string.replace("&aacute", "&aacute;")
# string = string.replace("&eacute", "&eacute;")
# string = string.replace("&iacute", "&iacute;")
# string = string.replace("&oacute", "&oacute;")
# string = string.replace("&uacute", "&uacute;")
# string = string.replace("&Aacute", "&Aacute;")
# string = string.replace("&Eacute", "&Eacute;")
# string = string.replace("&Iacute", "&Iacute;")
# string = string.replace("&Oacute", "&Oacute;")
# string = string.replace("&Uacute", "&Uacute;")
# string = string.replace("&uuml", "&uuml;")
# string = string.replace("&Uuml", "&Uuml;")
# string = string.replace("&ntilde", "&ntilde;")
# string = string.replace("&#191", "&#191;")
# string = string.replace("&#161", "&#161;")
# string = string.replace(";;", ";")
# return string
def htmlclean(cadena): def htmlclean(cadena):
cadena = re.compile("<!--.*?-->", re.DOTALL).sub("", cadena) cadena = re.compile("<!--.*?-->", re.DOTALL).sub("", cadena)
@@ -292,8 +358,12 @@ def remove_show_from_title(title, show):
return title return title
# scrapertools.get_filename_from_url(media_url)[-4:]
def get_filename_from_url(url): def get_filename_from_url(url):
if PY3:
import urllib.parse as urlparse # Es muy lento en PY2. En PY3 es nativo
else:
import urlparse # Usamos el nativo de PY2 que es más rápido
parsed_url = urlparse.urlparse(url) parsed_url = urlparse.urlparse(url)
try: try:
filename = parsed_url.path filename = parsed_url.path
@@ -311,6 +381,11 @@ def get_filename_from_url(url):
def get_domain_from_url(url): def get_domain_from_url(url):
if PY3:
import urllib.parse as urlparse # Es muy lento en PY2. En PY3 es nativo
else:
import urlparse # Usamos el nativo de PY2 que es más rápido
parsed_url = urlparse.urlparse(url) parsed_url = urlparse.urlparse(url)
try: try:
filename = parsed_url.netloc filename = parsed_url.netloc
+95 -64
View File
@@ -3,18 +3,32 @@
# Server management # Server management
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
import os from __future__ import division
from __future__ import absolute_import
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
#from future import standard_library
#standard_library.install_aliases()
import urllib.parse as urlparse # Es muy lento en PY2. En PY3 es nativo
else:
import urlparse # Usamos el nativo de PY2 que es más rápido
from future.builtins import range
from past.utils import old_div
import datetime
import re import re
import time
import filetools from core import filetools
import urlparse
from core import httptools from core import httptools
from core import jsontools from core import jsontools
from core.item import Item from core.item import Item
from platformcode import config, logger from platformcode import config, logger
from platformcode import platformtools from platformcode import platformtools
# from servers.decrypters import zcrypt
from lib import unshortenit from lib import unshortenit
dict_servers_parameters = {} dict_servers_parameters = {}
@@ -80,7 +94,7 @@ def get_servers_itemlist(itemlist, fnc=None, sort=False):
@type sort: bool @type sort: bool
""" """
# Recorre los servidores # Recorre los servidores
for serverid in get_servers_list().keys(): for serverid in list(get_servers_list().keys()):
server_parameters = get_server_parameters(serverid) server_parameters = get_server_parameters(serverid)
# Recorre los patrones # Recorre los patrones
@@ -105,18 +119,18 @@ def get_servers_itemlist(itemlist, fnc=None, sort=False):
item.url = url item.url = url
# Eliminamos los servidores desactivados # Eliminamos los servidores desactivados
itemlist = filter(lambda i: not i.server or is_server_enabled(i.server), itemlist) #itemlist = filter(lambda i: not i.server or is_server_enabled(i.server), itemlist)
# Filtrar si es necesario
itemlist = filter_servers(itemlist)
for item in itemlist: for item in itemlist:
# Asignamos "directo" en caso de que el server no se encuentre en pelisalcarta # Asignamos "directo" en caso de que el server no se encuentre en Alfa
if not item.server and item.url: if not item.server and item.url:
item.server = 'directo' item.server = "directo"
if fnc: if fnc:
item.title = fnc(item) item.title = fnc(item)
# Filtrar si es necesario
itemlist = filter_servers(itemlist)
# Ordenar segun favoriteslist si es necesario # Ordenar segun favoriteslist si es necesario
if sort: if sort:
@@ -137,7 +151,8 @@ def findvideos(data, skip=False):
logger.info() logger.info()
devuelve = [] devuelve = []
skip = int(skip) skip = int(skip)
servers_list = get_servers_list().keys() servers_list = list(get_servers_list().keys())
# Ordenar segun favoriteslist si es necesario # Ordenar segun favoriteslist si es necesario
servers_list = sort_servers(servers_list) servers_list = sort_servers(servers_list)
@@ -145,8 +160,8 @@ def findvideos(data, skip=False):
# Ejecuta el findvideos en cada servidor activo # Ejecuta el findvideos en cada servidor activo
for serverid in servers_list: for serverid in servers_list:
if not is_server_enabled(serverid): '''if not is_server_enabled(serverid):
continue continue'''
if config.get_setting("filter_servers") == True and config.get_setting("black_list", server=serverid): if config.get_setting("filter_servers") == True and config.get_setting("black_list", server=serverid):
is_filter_servers = True is_filter_servers = True
continue continue
@@ -167,6 +182,8 @@ def findvideosbyserver(data, serverid):
return [] return []
server_parameters = get_server_parameters(serverid) server_parameters = get_server_parameters(serverid)
if not server_parameters["active"]:
return []
devuelve = [] devuelve = []
if "find_videos" in server_parameters: if "find_videos" in server_parameters:
# Recorre los patrones # Recorre los patrones
@@ -229,6 +246,8 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
# Si el vídeo es "directo" o "local", no hay que buscar más # Si el vídeo es "directo" o "local", no hay que buscar más
if server == "directo" or server == "local": if server == "directo" or server == "local":
if isinstance(video_password, list):
return video_password, len(video_password) > 0, "<br/>".join(error_messages)
logger.info("Server: %s, la url es la buena" % server) logger.info("Server: %s, la url es la buena" % server)
video_urls.append(["%s [%s]" % (urlparse.urlparse(url)[2][-4:], server), url]) video_urls.append(["%s [%s]" % (urlparse.urlparse(url)[2][-4:], server), url])
@@ -309,7 +328,7 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
# Muestra el progreso # Muestra el progreso
if muestra_dialogo: if muestra_dialogo:
progreso.update((100 / len(opciones)) * opciones.index(opcion), config.get_localized_string(70180) % server_name) progreso.update((old_div(100, len(opciones))) * opciones.index(opcion), config.get_localized_string(70180) % server_name)
# Modo free # Modo free
if opcion == "free": if opcion == "free":
@@ -377,7 +396,7 @@ def get_server_name(serverid):
serverid = serverid.lower().split(".")[0] serverid = serverid.lower().split(".")[0]
# Obtenemos el listado de servers # Obtenemos el listado de servers
server_list = get_servers_list().keys() server_list = list(get_servers_list().keys())
# Si el nombre está en la lista # Si el nombre está en la lista
if serverid in server_list: if serverid in server_list:
@@ -445,25 +464,25 @@ def get_server_parameters(server):
if server not in dict_servers_parameters: if server not in dict_servers_parameters:
try: try:
# Servers # Servers
if os.path.isfile(os.path.join(config.get_runtime_path(), "servers", server + ".json")): if filetools.isfile(filetools.join(config.get_runtime_path(), "servers", server + ".json")):
path = os.path.join(config.get_runtime_path(), "servers", server + ".json") path = filetools.join(config.get_runtime_path(), "servers", server + ".json")
# Debriders # Debriders
elif os.path.isfile(os.path.join(config.get_runtime_path(), "servers", "debriders", server + ".json")): elif filetools.isfile(filetools.join(config.get_runtime_path(), "servers", "debriders", server + ".json")):
path = os.path.join(config.get_runtime_path(), "servers", "debriders", server + ".json") path = filetools.join(config.get_runtime_path(), "servers", "debriders", server + ".json")
# #
#Cuando no está bien definido el server en el canal (no existe conector), muestra error por no haber "path" y se tiene que revisar el canal #Cuando no está bien definido el server en el canal (no existe conector), muestra error por no haber "path" y se tiene que revisar el canal
# #
data = filetools.read(path) dict_server = jsontools.load(filetools.read(path))
dict_server = jsontools.load(data)
# Imagenes: se admiten url y archivos locales dentro de "resources/images" # Imagenes: se admiten url y archivos locales dentro de "resources/images"
if dict_server.get("thumbnail") and "://" not in dict_server["thumbnail"]: if dict_server.get("thumbnail") and "://" not in dict_server["thumbnail"]:
dict_server["thumbnail"] = os.path.join("https://raw.githubusercontent.com/kodiondemand/media/master/resources/servers", dict_server["thumbnail"]) dict_server["thumbnail"] = filetools.join(config.get_runtime_path(), "resources", "media",
"servers", dict_server["thumbnail"])
for k in ['premium', 'id']: for k in ['premium', 'id']:
dict_server[k] = dict_server.get(k, list()) dict_server[k] = dict_server.get(k, list())
if type(dict_server[k]) == str: if isinstance(dict_server[k], str):
dict_server[k] = [dict_server[k]] dict_server[k] = [dict_server[k]]
if "find_videos" in dict_server: if "find_videos" in dict_server:
@@ -497,7 +516,7 @@ def get_server_json(server_name):
server_json = jsontools.load(filetools.read(server_path)) server_json = jsontools.load(filetools.read(server_path))
# logger.info("server_json= %s" % server_json) # logger.info("server_json= %s" % server_json)
except Exception, ex: except Exception as ex:
template = "An exception of type %s occured. Arguments:\n%r" template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args) message = template % (type(ex).__name__, ex.args)
logger.error(" %s" % message) logger.error(" %s" % message)
@@ -554,16 +573,16 @@ def get_server_setting(name, server, default=None):
""" """
# Creamos la carpeta si no existe # Creamos la carpeta si no existe
if not os.path.exists(os.path.join(config.get_data_path(), "settings_servers")): if not filetools.exists(filetools.join(config.get_data_path(), "settings_servers")):
os.mkdir(os.path.join(config.get_data_path(), "settings_servers")) filetools.mkdir(filetools.join(config.get_data_path(), "settings_servers"))
file_settings = os.path.join(config.get_data_path(), "settings_servers", server + "_data.json") file_settings = filetools.join(config.get_data_path(), "settings_servers", server + "_data.json")
dict_settings = {} dict_settings = {}
dict_file = {} dict_file = {}
if os.path.exists(file_settings): if filetools.exists(file_settings):
# Obtenemos configuracion guardada de ../settings/channel_data.json # Obtenemos configuracion guardada de ../settings/channel_data.json
try: try:
dict_file = jsontools.load(open(file_settings, "rb").read()) dict_file = jsontools.load(filetools.read(file_settings))
if isinstance(dict_file, dict) and 'settings' in dict_file: if isinstance(dict_file, dict) and 'settings' in dict_file:
dict_settings = dict_file['settings'] dict_settings = dict_file['settings']
except EnvironmentError: except EnvironmentError:
@@ -580,10 +599,7 @@ def get_server_setting(name, server, default=None):
dict_settings = default_settings dict_settings = default_settings
dict_file['settings'] = dict_settings dict_file['settings'] = dict_settings
# Creamos el archivo ../settings/channel_data.json # Creamos el archivo ../settings/channel_data.json
json_data = jsontools.dump(dict_file) if not filetools.write(file_settings, jsontools.dump(dict_file)):
try:
open(file_settings, "wb").write(json_data)
except EnvironmentError:
logger.info("ERROR al salvar el archivo: %s" % file_settings) logger.info("ERROR al salvar el archivo: %s" % file_settings)
# Devolvemos el valor del parametro local 'name' si existe, si no se devuelve default # Devolvemos el valor del parametro local 'name' si existe, si no se devuelve default
@@ -592,18 +608,18 @@ def get_server_setting(name, server, default=None):
def set_server_setting(name, value, server): def set_server_setting(name, value, server):
# Creamos la carpeta si no existe # Creamos la carpeta si no existe
if not os.path.exists(os.path.join(config.get_data_path(), "settings_servers")): if not filetools.exists(filetools.join(config.get_data_path(), "settings_servers")):
os.mkdir(os.path.join(config.get_data_path(), "settings_servers")) filetools.mkdir(filetools.join(config.get_data_path(), "settings_servers"))
file_settings = os.path.join(config.get_data_path(), "settings_servers", server + "_data.json") file_settings = filetools.join(config.get_data_path(), "settings_servers", server + "_data.json")
dict_settings = {} dict_settings = {}
dict_file = None dict_file = None
if os.path.exists(file_settings): if filetools.exists(file_settings):
# Obtenemos configuracion guardada de ../settings/channel_data.json # Obtenemos configuracion guardada de ../settings/channel_data.json
try: try:
dict_file = jsontools.load(open(file_settings, "r").read()) dict_file = jsontools.load(filetools.read(file_settings))
dict_settings = dict_file.get('settings', {}) dict_settings = dict_file.get('settings', {})
except EnvironmentError: except EnvironmentError:
logger.info("ERROR al leer el archivo: %s" % file_settings) logger.info("ERROR al leer el archivo: %s" % file_settings)
@@ -617,10 +633,7 @@ def set_server_setting(name, value, server):
dict_file['settings'] = dict_settings dict_file['settings'] = dict_settings
# Creamos el archivo ../settings/channel_data.json # Creamos el archivo ../settings/channel_data.json
try: if not filetools.write(file_settings, jsontools.dump(dict_file)):
json_data = jsontools.dump(dict_file)
open(file_settings, "w").write(json_data)
except EnvironmentError:
logger.info("ERROR al salvar el archivo: %s" % file_settings) logger.info("ERROR al salvar el archivo: %s" % file_settings)
return None return None
@@ -636,11 +649,10 @@ def get_servers_list():
@rtype: dict @rtype: dict
""" """
server_list = {} server_list = {}
for server in os.listdir(os.path.join(config.get_runtime_path(), "servers")): for server in filetools.listdir(filetools.join(config.get_runtime_path(), "servers")):
if server.endswith(".json") and not server == "version.json": if server.endswith(".json") and not server == "version.json":
server_parameters = get_server_parameters(server) server_parameters = get_server_parameters(server)
if server_parameters["active"] == True: server_list[server.split(".")[0]] = server_parameters
server_list[server.split(".")[0]] = server_parameters
return server_list return server_list
@@ -654,7 +666,7 @@ def get_debriders_list():
@rtype: dict @rtype: dict
""" """
server_list = {} server_list = {}
for server in os.listdir(os.path.join(config.get_runtime_path(), "servers", "debriders")): for server in filetools.listdir(filetools.join(config.get_runtime_path(), "servers", "debriders")):
if server.endswith(".json"): if server.endswith(".json"):
server_parameters = get_server_parameters(server) server_parameters = get_server_parameters(server)
if server_parameters["active"] == True: if server_parameters["active"] == True:
@@ -678,6 +690,7 @@ def sort_servers(servers_list):
else: else:
servers_list = sorted(servers_list, servers_list = sorted(servers_list,
key=lambda x: config.get_setting("favorites_servers_list", server=x) or 100) key=lambda x: config.get_setting("favorites_servers_list", server=x) or 100)
return servers_list return servers_list
@@ -689,11 +702,16 @@ def filter_servers(servers_list):
u objetos Item. En cuyo caso es necesario q tengan un atributo item.server del tipo str. u objetos Item. En cuyo caso es necesario q tengan un atributo item.server del tipo str.
:return: Lista del mismo tipo de objetos que servers_list filtrada en funcion de la Lista Negra. :return: Lista del mismo tipo de objetos que servers_list filtrada en funcion de la Lista Negra.
""" """
#Eliminamos los inactivos
if servers_list:
servers_list = [i for i in servers_list if not i.server or is_server_enabled(i.server)]
if servers_list and config.get_setting('filter_servers'): if servers_list and config.get_setting('filter_servers'):
if isinstance(servers_list[0], Item): if isinstance(servers_list[0], Item):
servers_list_filter = filter(lambda x: not config.get_setting("black_list", server=x.server), servers_list) servers_list_filter = [x for x in servers_list if not config.get_setting("black_list", server=x.server)]
else: else:
servers_list_filter = filter(lambda x: not config.get_setting("black_list", server=x), servers_list) servers_list_filter = [x for x in servers_list if not config.get_setting("black_list", server=x)]
# Si no hay enlaces despues de filtrarlos # Si no hay enlaces despues de filtrarlos
if servers_list_filter or not platformtools.dialog_yesno(config.get_localized_string(60000), if servers_list_filter or not platformtools.dialog_yesno(config.get_localized_string(60000),
@@ -701,6 +719,9 @@ def filter_servers(servers_list):
config.get_localized_string(70281)): config.get_localized_string(70281)):
servers_list = servers_list_filter servers_list = servers_list_filter
if config.get_setting("favorites_servers") == True:
servers_list = sort_servers(servers_list)
return servers_list return servers_list
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -715,21 +736,31 @@ def check_list_links(itemlist, numero='', timeout=3):
El parámetro timeout indica un tope de espera para descargar la página El parámetro timeout indica un tope de espera para descargar la página
""" """
numero = ((int(numero) + 1) * 5) if numero != '' else 10 numero = ((int(numero) + 1) * 5) if numero != '' else 10
for it in itemlist: from lib.concurrent import futures
if numero > 0 and it.server != '' and it.url != '': with futures.ThreadPoolExecutor() as executor:
verificacion = check_video_link(it.url, it.server, timeout) checked = []
it.title = verificacion + ' ' + it.title.strip() for it in itemlist:
logger.info('VERIFICATION= '+ verificacion) if numero > 0 and it.server != '' and it.url != '':
it.alive = verificacion checked.append(executor.submit(check_video_link, it, timeout))
numero -= 1 numero -= 1
for link in futures.as_completed(checked):
res = link.result()
if res:
it = res[0]
verificacion = res[1]
it.title = verificacion + ' ' + it.title.strip()
logger.info('VERIFICATION= ' + verificacion)
it.alive = verificacion
return itemlist return itemlist
def check_video_link(url, server, timeout=3): def check_video_link(item, timeout=3):
"""
Comprueba si el enlace a un video es valido y devuelve un string de 2 posiciones con la verificacion.
:param url, server: Link y servidor
:return: str(2) '??':No se ha podido comprobar. 'Ok':Parece que el link funciona. 'NO':Parece que no funciona.
""" """
Comprueba si el enlace a un video es valido y devuelve un string de 2 posiciones con la verificacion.
:param url, server: Link y servidor
:return: str(2) '??':No se ha podido comprobar. 'Ok':Parece que el link funciona. 'NO':Parece que no funciona.
"""
url = item.url
server = item.server
NK = "[COLOR 0xFFF9B613][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]" NK = "[COLOR 0xFFF9B613][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]"
OK = "[COLOR 0xFF00C289][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]" OK = "[COLOR 0xFF00C289][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]"
@@ -744,7 +775,7 @@ def check_video_link(url, server, timeout=3):
except: except:
server_module = None server_module = None
logger.info("[check_video_link] No se puede importar el servidor! %s" % server) logger.info("[check_video_link] No se puede importar el servidor! %s" % server)
return NK return item, NK
if hasattr(server_module, 'test_video_exists'): if hasattr(server_module, 'test_video_exists'):
ant_timeout = httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT ant_timeout = httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT
@@ -764,7 +795,7 @@ def check_video_link(url, server, timeout=3):
finally: finally:
httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = ant_timeout # Restaurar tiempo de descarga httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = ant_timeout # Restaurar tiempo de descarga
return resultado return item, resultado
logger.info("[check_video_link] No hay test_video_exists para servidor: %s" % server) logger.info("[check_video_link] No hay test_video_exists para servidor: %s" % server)
return NK return item, NK
+59 -26
View File
@@ -10,8 +10,10 @@ from concurrent import futures
try: try:
import urllib.request as urllib import urllib.request as urllib
import urllib.parse as urlparse import urllib.parse as urlparse
from urllib.parse import urlencode
except ImportError: except ImportError:
import urllib, urlparse import urllib, urlparse
from urllib import urlencode
from channelselector import thumb from channelselector import thumb
from core import httptools, scrapertools, servertools, tmdb, channeltools from core import httptools, scrapertools, servertools, tmdb, channeltools
@@ -157,7 +159,8 @@ def scrapeLang(scraped, lang, longtitle):
return language, longtitle return language, longtitle
def cleantitle(title): def cleantitle(title):
cleantitle = scrapertools.htmlclean(scrapertools.decodeHtmlentities(title).replace('"', "'").replace('×', 'x').replace('', '-')).strip() if type(title) != str: title.decode('UTF-8')
cleantitle = title.replace('"', "'").replace('×', 'x').replace('', '-').strip()
return cleantitle return cleantitle
def scrapeBlock(item, args, block, patron, headers, action, pagination, debug, typeContentDict, typeActionDict, blacklist, search, pag, function, lang): def scrapeBlock(item, args, block, patron, headers, action, pagination, debug, typeContentDict, typeActionDict, blacklist, search, pag, function, lang):
@@ -192,16 +195,17 @@ def scrapeBlock(item, args, block, patron, headers, action, pagination, debug, t
for i, match in enumerate(matches): for i, match in enumerate(matches):
if pagination and (pag - 1) * pagination > i and not search: continue # pagination if pagination and (pag - 1) * pagination > i and not search: continue # pagination
if pagination and i >= pag * pagination and not search: break # pagination if pagination and i >= pag * pagination and not search: break # pagination
listGroups = match.keys() # listGroups = match.keys()
match = match.values() # match = match.values()
if len(listGroups) > len(match): # to fix a bug # if len(listGroups) > len(match): # to fix a bug
match = list(match) # match = list(match)
match.extend([''] * (len(listGroups) - len(match))) # match.extend([''] * (len(listGroups) - len(match)))
scraped = {} scraped = {}
for kk in known_keys: for kk in known_keys:
val = match[listGroups.index(kk)] if kk in listGroups else '' val = match[kk] if kk in match else ''
# val = match[listGroups.index(kk)] if kk in listGroups else ''
if val and (kk == "url" or kk == 'thumb') and 'http' not in val: if val and (kk == "url" or kk == 'thumb') and 'http' not in val:
val = scrapertools.find_single_match(item.url, 'https?://[a-z0-9.-]+') + (val if val.startswith('/') else '/' + val) val = scrapertools.find_single_match(item.url, 'https?://[a-z0-9.-]+') + (val if val.startswith('/') else '/' + val)
scraped[kk] = val scraped[kk] = val
@@ -294,8 +298,10 @@ def scrapeBlock(item, args, block, patron, headers, action, pagination, debug, t
other = scraped['other'] if scraped['other'] else '' other = scraped['other'] if scraped['other'] else ''
) )
for lg in list(set(listGroups).difference(known_keys)): # for lg in list(set(listGroups).difference(known_keys)):
it.__setattr__(lg, match[listGroups.index(lg)]) # it.__setattr__(lg, match[listGroups.index(lg)])
for lg in list(set(match.keys()).difference(known_keys)):
it.__setattr__(lg, match[lg])
if 'itemHook' in args: if 'itemHook' in args:
it = args['itemHook'](it) it = args['itemHook'](it)
@@ -367,7 +373,7 @@ def scrape(func):
log('PATRON= ', patron) log('PATRON= ', patron)
if not data: if not data:
page = httptools.downloadpage(item.url, headers=headers, ignore_response_code=True, session=item.session) page = httptools.downloadpage(item.url, headers=headers, ignore_response_code=True)
# if url may be changed and channel has findhost to update # if url may be changed and channel has findhost to update
if (not page.data or scrapertools.get_domain_from_url(page.url) != scrapertools.get_domain_from_url(item.url)) and 'findhost' in func.__globals__: if (not page.data or scrapertools.get_domain_from_url(page.url) != scrapertools.get_domain_from_url(item.url)) and 'findhost' in func.__globals__:
host = func.__globals__['findhost']() host = func.__globals__['findhost']()
@@ -376,8 +382,7 @@ def scrape(func):
jsontools.update_node(host, func.__module__.split('.')[-1], 'url') jsontools.update_node(host, func.__module__.split('.')[-1], 'url')
parse[1] = scrapertools.get_domain_from_url(host) parse[1] = scrapertools.get_domain_from_url(host)
item.url = urlparse.urlunparse(parse) item.url = urlparse.urlunparse(parse)
page = httptools.downloadpage(item.url, headers=headers, ignore_response_code=True, page = httptools.downloadpage(item.url, headers=headers, ignore_response_code=True)
session=item.session)
data = page.data.replace("'", '"') data = page.data.replace("'", '"')
data = re.sub('\n|\t', ' ', data) data = re.sub('\n|\t', ' ', data)
data = re.sub(r'>\s+<', '> <', data) data = re.sub(r'>\s+<', '> <', data)
@@ -468,7 +473,7 @@ def dooplay_get_links(item, host):
ret = [] ret = []
for type, post, nume, title, server in matches: for type, post, nume, title, server in matches:
postData = urllib.urlencode({ postData = urlencode({
"action": "doo_player_ajax", "action": "doo_player_ajax",
"post": post, "post": post,
"nume": nume, "nume": nume,
@@ -582,7 +587,7 @@ def swzz_get_url(item):
elif 'https://stayonline.pro' in item.url: elif 'https://stayonline.pro' in item.url:
id = item.url.split('/')[-2] id = item.url.split('/')[-2]
reqUrl = 'https://stayonline.pro/ajax/linkView.php' reqUrl = 'https://stayonline.pro/ajax/linkView.php'
p = urllib.urlencode({"id": id}) p = urlencode({"id": id})
data = httptools.downloadpage(reqUrl, post=p).data data = httptools.downloadpage(reqUrl, post=p).data
try: try:
import json import json
@@ -699,9 +704,9 @@ def menu(func):
if global_search: if global_search:
menuItem(itemlist, filename, config.get_localized_string(70741) % '… bold', 'search', host + dictUrl['search']) menuItem(itemlist, filename, config.get_localized_string(70741) % '… bold', 'search', host + dictUrl['search'])
if 'get_channel_results' not in inspect.stack()[1][3]:
autoplay.init(item.channel, list_servers, list_quality) autoplay.init(item.channel, list_servers, list_quality)
autoplay.show_option(item.channel, itemlist) autoplay.show_option(item.channel, itemlist)
channel_config(item, itemlist) channel_config(item, itemlist)
return itemlist return itemlist
@@ -744,7 +749,7 @@ def typo(string, typography=''):
if '{}' in string: if '{}' in string:
string = '{' + re.sub(r'\s\{\}','',string) + '}' string = '{' + re.sub(r'\s\{\}','',string) + '}'
if 'submenu' in string: if 'submenu' in string:
string = u"\u2022\u2022 ".encode('utf-8') + re.sub(r'\ssubmenu','',string) string = "•• " + re.sub(r'\ssubmenu','',string)
if 'color' in string: if 'color' in string:
color = scrapertools.find_single_match(string, 'color ([a-z]+)') color = scrapertools.find_single_match(string, 'color ([a-z]+)')
if color == 'kod' or '': color = kod_color if color == 'kod' or '': color = kod_color
@@ -758,7 +763,7 @@ def typo(string, typography=''):
if '--' in string: if '--' in string:
string = ' - ' + re.sub(r'\s--','',string) string = ' - ' + re.sub(r'\s--','',string)
if 'bullet' in string: if 'bullet' in string:
string = '[B]' + u"\u2022".encode('utf-8') + '[/B] ' + re.sub(r'\sbullet','',string) string = '[B]' + "" + '[/B] ' + re.sub(r'\sbullet','',string)
return string return string
@@ -766,10 +771,33 @@ def typo(string, typography=''):
def match(item_url_string, **args): def match(item_url_string, **args):
''' '''
match is a function that combines httptools and scraper tools: match is a function that combines httptools and scraper tools:
supports all httptools and the following arggs:
@param item_url_string: if it's a titem download the page item.url, if it's a URL download the page, if it's a string pass it to scrapertools
@type item_url_string: item or str
@param string: force item_url_string to be a string
@type string: bool
@param patronBlock: find first element in patron
@type patronBlock: str
@param patronBloks: find multiple matches
@type patronBloks: str or list
@param debugBlock: regex101.com for debug
@type debugBlock: bool
@param patron: find multiple matches on block, blocks or data
@type patron: str or list
@param debug: regex101.com for debug
@type debug: bool
Return a item with the following key:
data: data of the webpage
block: first block
blocks: all the blocks
match: first match
matches: all the matches
''' '''
log(item_url_string) log(item_url_string)
matches = [] matches = blocks = []
url = None url = None
# arguments allowed for scrape # arguments allowed for scrape
patron = args.get('patron', None) patron = args.get('patron', None)
@@ -778,12 +806,15 @@ def match(item_url_string, **args):
debug = args.get('debug', False) debug = args.get('debug', False)
debugBlock = args.get('debugBlock', False) debugBlock = args.get('debugBlock', False)
string = args.get('string', False) string = args.get('string', False)
# remove scrape arguments # remove scrape arguments
args = dict([(key, val) for key, val in args.items() if key not in ['patron', 'patronBlock', 'patronBlocks', 'debug', 'debugBlock', 'string']]) args = dict([(key, val) for key, val in args.items() if key not in ['patron', 'patronBlock', 'patronBlocks', 'debug', 'debugBlock', 'string']])
# dbg()
# check type of item_url_string # check type of item_url_string
if type(item_url_string) == str: if string:
if item_url_string.startswith('http') and not string: url = item_url_string data = item_url_string
elif type(item_url_string) == str:
if item_url_string.startswith('http'): url = item_url_string
else : data = item_url_string else : data = item_url_string
else: else:
# if item_url_string is an item use item.url as url # if item_url_string is an item use item.url as url
@@ -803,7 +834,9 @@ def match(item_url_string, **args):
if patronBlock: if patronBlock:
blocks = [scrapertools.find_single_match(data, patronBlock)] blocks = [scrapertools.find_single_match(data, patronBlock)]
elif patronBlocks: elif patronBlocks:
blocks = scrapertools.find_multiple_matches(data, patronBlock) if type(patronBlock) == str: patron = [patronBlock]
for p in patronBlock:
blocks += scrapertools.find_multiple_matches(data, p)
else: else:
blocks = [data] blocks = [data]
@@ -1010,7 +1043,7 @@ def server(item, data='', itemlist=[], headers='', AutoPlay=True, CheckLinks=Tru
item.title = typo(item.contentTitle.strip(),'bold') if item.contentType == 'movie' or (config.get_localized_string(30161) in item.title) else item.title item.title = typo(item.contentTitle.strip(),'bold') if item.contentType == 'movie' or (config.get_localized_string(30161) in item.title) else item.title
videoitem.plot= typo(videoitem.title, 'bold') videoitem.plot= typo(videoitem.title, 'bold') + typo(videoitem.quality, '_ [] bold')
videoitem.title = item.title + (typo(videoitem.title, '_ color kod [] bold') if videoitem.title else "") + (typo(videoitem.quality, '_ color kod []') if videoitem.quality else "") videoitem.title = item.title + (typo(videoitem.title, '_ color kod [] bold') if videoitem.title else "") + (typo(videoitem.quality, '_ color kod []') if videoitem.quality else "")
videoitem.fulltitle = item.fulltitle videoitem.fulltitle = item.fulltitle
videoitem.show = item.show videoitem.show = item.show
@@ -1036,7 +1069,7 @@ def controls(itemlist, item, AutoPlay=True, CheckLinks=True, down_load=True):
channel_node = autoplay_node.get(item.channel, {}) channel_node = autoplay_node.get(item.channel, {})
settings_node = channel_node.get('settings', {}) settings_node = channel_node.get('settings', {})
AP = get_setting('autoplay') or settings_node['active'] AP = get_setting('autoplay') or settings_node['active']
HS = config.get_setting('hide_servers') or (settings_node['hide_servers'] if settings_node.has_key('hide_server') else False) HS = config.get_setting('hide_servers') or (settings_node['hide_servers'] if 'hide_server' in settings_node else False)
if CL and not AP: if CL and not AP:
if get_setting('checklinks', item.channel): if get_setting('checklinks', item.channel):
+74 -38
View File
@@ -1,10 +1,26 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#from future import standard_library
#standard_library.install_aliases()
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo
else:
import urllib # Usamos el nativo de PY2 que es más rápido
from future.builtins import range
from future.builtins import object
import ast
import copy import copy
import re import re
import sqlite3 import sqlite3
import time import time
import urllib
import xbmcaddon import xbmcaddon
@@ -37,8 +53,8 @@ def_lang = addon.getSetting('language')
# tmdb.set_infoLabels(item, seekTmdb = True) # tmdb.set_infoLabels(item, seekTmdb = True)
# #
# Obtener datos basicos de una pelicula: # Obtener datos basicos de una pelicula:
# Antes de llamar al metodo set_infoLabels el titulo a buscar debe estar en item.fulltitle # Antes de llamar al metodo set_infoLabels el titulo a buscar debe estar en item.contentTitle
# o en item.contentTitle y el año en item.infoLabels['year']. # y el año en item.infoLabels['year'].
# #
# Obtener datos basicos de una serie: # Obtener datos basicos de una serie:
# Antes de llamar al metodo set_infoLabels el titulo a buscar debe estar en item.show o en # Antes de llamar al metodo set_infoLabels el titulo a buscar debe estar en item.show o en
@@ -73,7 +89,6 @@ def_lang = addon.getSetting('language')
otmdb_global = None otmdb_global = None
fname = filetools.join(config.get_data_path(), "kod_db.sqlite") fname = filetools.join(config.get_data_path(), "kod_db.sqlite")
def create_bd(): def create_bd():
conn = sqlite3.connect(fname) conn = sqlite3.connect(fname)
c = conn.cursor() c = conn.cursor()
@@ -160,7 +175,7 @@ def cache_response(fn):
conn = sqlite3.connect(fname, timeout=15) conn = sqlite3.connect(fname, timeout=15)
c = conn.cursor() c = conn.cursor()
url = re.sub('&year=-', '', args[0]) url = re.sub('&year=-', '', args[0])
# logger.error('la url %s' % url) if PY3: url = str.encode(url)
url_base64 = base64.b64encode(url) url_base64 = base64.b64encode(url)
c.execute("SELECT response, added FROM tmdb_cache WHERE url=?", (url_base64,)) c.execute("SELECT response, added FROM tmdb_cache WHERE url=?", (url_base64,))
row = c.fetchone() row = c.fetchone()
@@ -171,7 +186,9 @@ def cache_response(fn):
# si no se ha obtenido información, llamamos a la funcion # si no se ha obtenido información, llamamos a la funcion
if not result: if not result:
result = fn(*args) result = fn(*args)
result_base64 = base64.b64encode(str(result)) result = str(result)
if PY3: result = str.encode(result)
result_base64 = base64.b64encode(result)
c.execute("INSERT OR REPLACE INTO tmdb_cache (url, response, added) VALUES (?, ?, ?)", c.execute("INSERT OR REPLACE INTO tmdb_cache (url, response, added) VALUES (?, ?, ?)",
(url_base64, result_base64, time.time())) (url_base64, result_base64, time.time()))
@@ -375,17 +392,19 @@ def set_infoLabels_item(item, seekTmdb=True, idioma_busqueda=def_lang, lock=None
# ... buscar datos temporada # ... buscar datos temporada
item.infoLabels['mediatype'] = 'season' item.infoLabels['mediatype'] = 'season'
temporada = otmdb_global.get_temporada(numtemporada) temporada = otmdb_global.get_temporada(numtemporada)
if not isinstance(temporada, dict):
temporada = ast.literal_eval(temporada.decode('utf-8'))
if temporada: if temporada:
# Actualizar datos # Actualizar datos
__leer_datos(otmdb_global) __leer_datos(otmdb_global)
item.infoLabels['title'] = temporada['name'] if temporada.has_key('name') else '' item.infoLabels['title'] = temporada['name'] if 'name' in temporada else ''
if temporada.has_key('overview') and temporada['overview']: if 'overview' in temporada and temporada['overview']:
item.infoLabels['plot'] = temporada['overview'] item.infoLabels['plot'] = temporada['overview']
if temporada.has_key('air_date') and temporada['air_date']: if 'air_date' in temporada and temporada['air_date']:
date = temporada['air_date'].split('-') date = temporada['air_date'].split('-')
item.infoLabels['aired'] = date[2] + "/" + date[1] + "/" + date[0] item.infoLabels['aired'] = date[2] + "/" + date[1] + "/" + date[0]
if temporada.has_key('poster_path') and temporada['poster_path']: if 'poster_path' in temporada and temporada['poster_path']:
item.infoLabels['poster_path'] = 'http://image.tmdb.org/t/p/original' + temporada['poster_path'] item.infoLabels['poster_path'] = 'http://image.tmdb.org/t/p/original' + temporada['poster_path']
item.thumbnail = item.infoLabels['poster_path'] item.thumbnail = item.infoLabels['poster_path']
@@ -445,12 +464,8 @@ def set_infoLabels_item(item, seekTmdb=True, idioma_busqueda=def_lang, lock=None
# Busqueda de pelicula por titulo... # Busqueda de pelicula por titulo...
if item.infoLabels['year'] or item.infoLabels['filtro']: if item.infoLabels['year'] or item.infoLabels['filtro']:
# ...y año o filtro # ...y año o filtro
if item.contentTitle: searched_title = item.contentTitle if item.contentTitle else item.fulltitle
titulo_buscado = item.contentTitle otmdb = Tmdb(texto_buscado=searched_title, tipo=tipo_busqueda, idioma_busqueda=idioma_busqueda,
else:
titulo_buscado = item.fulltitle
otmdb = Tmdb(texto_buscado=titulo_buscado, tipo=tipo_busqueda, idioma_busqueda=idioma_busqueda,
filtro=item.infoLabels.get('filtro', {}), year=item.infoLabels['year']) filtro=item.infoLabels.get('filtro', {}), year=item.infoLabels['year'])
if otmdb is not None: if otmdb is not None:
if otmdb.get_id() and config.get_setting("tmdb_plus_info", default=False): if otmdb.get_id() and config.get_setting("tmdb_plus_info", default=False):
@@ -492,7 +507,7 @@ def find_and_set_infoLabels(item):
title = title.replace(year, "").strip() title = title.replace(year, "").strip()
item.infoLabels['year'] = year[1:-1] item.infoLabels['year'] = year[1:-1]
if not item.infoLabels.get("tmdb_id"): if not item.infoLabels.get("tmdb_id") or not item.infoLabels.get("tmdb_id")[0].isdigit():
if not item.infoLabels.get("imdb_id"): if not item.infoLabels.get("imdb_id"):
otmdb_global = Tmdb(texto_buscado=title, tipo=tipo_busqueda, year=item.infoLabels['year']) otmdb_global = Tmdb(texto_buscado=title, tipo=tipo_busqueda, year=item.infoLabels['year'])
else: else:
@@ -588,7 +603,6 @@ def get_genres(type):
return genres.dic_generos[lang] return genres.dic_generos[lang]
# Clase auxiliar # Clase auxiliar
class ResultDictDefault(dict): class ResultDictDefault(dict):
# Python 2.4 # Python 2.4
@@ -606,7 +620,7 @@ class ResultDictDefault(dict):
return list() return list()
elif key == 'images_posters': elif key == 'images_posters':
posters = dict() posters = dict()
if 'images' in super(ResultDictDefault, self).keys() and \ if 'images' in list(super(ResultDictDefault, self).keys()) and \
'posters' in super(ResultDictDefault, self).__getitem__('images'): 'posters' in super(ResultDictDefault, self).__getitem__('images'):
posters = super(ResultDictDefault, self).__getitem__('images')['posters'] posters = super(ResultDictDefault, self).__getitem__('images')['posters']
super(ResultDictDefault, self).__setattr__("images_posters", posters) super(ResultDictDefault, self).__setattr__("images_posters", posters)
@@ -615,7 +629,7 @@ class ResultDictDefault(dict):
elif key == "images_backdrops": elif key == "images_backdrops":
backdrops = dict() backdrops = dict()
if 'images' in super(ResultDictDefault, self).keys() and \ if 'images' in list(super(ResultDictDefault, self).keys()) and \
'backdrops' in super(ResultDictDefault, self).__getitem__('images'): 'backdrops' in super(ResultDictDefault, self).__getitem__('images'):
backdrops = super(ResultDictDefault, self).__getitem__('images')['backdrops'] backdrops = super(ResultDictDefault, self).__getitem__('images')['backdrops']
super(ResultDictDefault, self).__setattr__("images_backdrops", backdrops) super(ResultDictDefault, self).__setattr__("images_backdrops", backdrops)
@@ -624,7 +638,7 @@ class ResultDictDefault(dict):
elif key == "images_profiles": elif key == "images_profiles":
profiles = dict() profiles = dict()
if 'images' in super(ResultDictDefault, self).keys() and \ if 'images' in list(super(ResultDictDefault, self).keys()) and \
'profiles' in super(ResultDictDefault, self).__getitem__('images'): 'profiles' in super(ResultDictDefault, self).__getitem__('images'):
profiles = super(ResultDictDefault, self).__getitem__('images')['profiles'] profiles = super(ResultDictDefault, self).__getitem__('images')['profiles']
super(ResultDictDefault, self).__setattr__("images_profiles", profiles) super(ResultDictDefault, self).__setattr__("images_profiles", profiles)
@@ -640,7 +654,7 @@ class ResultDictDefault(dict):
def tostring(self, separador=',\n'): def tostring(self, separador=',\n'):
ls = [] ls = []
for i in super(ResultDictDefault, self).items(): for i in list(super(ResultDictDefault, self).items()):
i_str = str(i)[1:-1] i_str = str(i)[1:-1]
if isinstance(i[0], str): if isinstance(i[0], str):
old = i[0] + "'," old = i[0] + "',"
@@ -899,12 +913,16 @@ class Tmdb(object):
logger.info("[Tmdb.py] Filling in dictionary of genres") logger.info("[Tmdb.py] Filling in dictionary of genres")
resultado = cls.get_json(url) resultado = cls.get_json(url)
if not isinstance(resultado, dict):
resultado = ast.literal_eval(resultado.decode('utf-8'))
lista_generos = resultado["genres"] lista_generos = resultado["genres"]
for i in lista_generos: for i in lista_generos:
cls.dic_generos[idioma][tipo][str(i["id"])] = i["name"] cls.dic_generos[idioma][tipo][str(i["id"])] = i["name"]
except: except:
logger.error("Error generating dictionaries") logger.error("Error generating dictionaries")
import traceback
logger.error(traceback.format_exc())
def __by_id(self, source='tmdb'): def __by_id(self, source='tmdb'):
@@ -926,6 +944,8 @@ class Tmdb(object):
logger.info("[Tmdb.py] Searching %s:\n%s" % (buscando, url)) logger.info("[Tmdb.py] Searching %s:\n%s" % (buscando, url))
resultado = self.get_json(url) resultado = self.get_json(url)
if not isinstance(resultado, dict):
resultado = ast.literal_eval(resultado.decode('utf-8'))
if resultado: if resultado:
if source != "tmdb": if source != "tmdb":
@@ -942,14 +962,14 @@ class Tmdb(object):
else: else:
# No hay resultados de la busqueda # No hay resultados de la busqueda
msg = "The search of %s gave no results" % buscando msg = "The search of %s gave no results" % buscando
# logger.debug(msg) logger.debug(msg)
def __search(self, index_results=0, page=1): def __search(self, index_results=0, page=1):
self.result = ResultDictDefault() self.result = ResultDictDefault()
results = [] results = []
total_results = 0
text_simple = self.busqueda_texto.lower() text_simple = self.busqueda_texto.lower()
text_quote = urllib.quote(text_simple) text_quote = urllib.quote(text_simple)
total_results = 0
total_pages = 0 total_pages = 0
buscando = "" buscando = ""
@@ -957,15 +977,17 @@ class Tmdb(object):
# http://api.themoviedb.org/3/search/movie?api_key=a1ab8b8669da03637a4b98fa39c39228&query=superman&language=es # http://api.themoviedb.org/3/search/movie?api_key=a1ab8b8669da03637a4b98fa39c39228&query=superman&language=es
# &include_adult=false&page=1 # &include_adult=false&page=1
url = ('http://api.themoviedb.org/3/search/%s?api_key=a1ab8b8669da03637a4b98fa39c39228&query=%s&language=%s' url = ('http://api.themoviedb.org/3/search/%s?api_key=a1ab8b8669da03637a4b98fa39c39228&query=%s&language=%s'
'&include_adult=%s&page=%s' % (self.busqueda_tipo, text_quote.replace(' ', '%20'), '&include_adult=%s&page=%s' % (self.busqueda_tipo, text_quote,
self.busqueda_idioma, self.busqueda_include_adult, page)) self.busqueda_idioma, self.busqueda_include_adult, page))
if self.busqueda_year: if self.busqueda_year:
url += '&year=%s' % self.busqueda_year url += '&year=%s' % self.busqueda_year
buscando = self.busqueda_texto.capitalize() buscando = self.busqueda_texto.capitalize()
logger.info("[Tmdb.py] Searching %s on page %s:\n%s" % (buscando, page, url)) logger.info("[Tmdb.py] Buscando %s en pagina %s:\n%s" % (buscando, page, url))
resultado = self.get_json(url) resultado = self.get_json(url)
if not isinstance(resultado, dict):
resultado = ast.literal_eval(resultado.decode('utf-8'))
total_results = resultado.get("total_results", 0) total_results = resultado.get("total_results", 0)
total_pages = resultado.get("total_pages", 0) total_pages = resultado.get("total_pages", 0)
@@ -973,11 +995,13 @@ class Tmdb(object):
if total_results > 0: if total_results > 0:
results = resultado["results"] results = resultado["results"]
if self.busqueda_filtro and results: if self.busqueda_filtro and total_results > 1:
# TODO documentar esta parte # TODO documentar esta parte
for key, value in dict(self.busqueda_filtro).items(): for key, value in list(dict(self.busqueda_filtro).items()):
for r in results[:]: for r in results[:]:
if key not in r or r[key] != value: if not r[key]:
r[key] = str(r[key])
if key not in r or value not in r[key]:
results.remove(r) results.remove(r)
total_results -= 1 total_results -= 1
@@ -1015,7 +1039,7 @@ class Tmdb(object):
type_search = self.discover.get('url', '') type_search = self.discover.get('url', '')
if type_search: if type_search:
params = [] params = []
for key, value in self.discover.items(): for key, value in list(self.discover.items()):
if key != "url": if key != "url":
params.append(key + "=" + str(value)) params.append(key + "=" + str(value))
# http://api.themoviedb.org/3/discover/movie?api_key=a1ab8b8669da03637a4b98fa39c39228&query=superman&language=es # http://api.themoviedb.org/3/discover/movie?api_key=a1ab8b8669da03637a4b98fa39c39228&query=superman&language=es
@@ -1024,6 +1048,8 @@ class Tmdb(object):
logger.info("[Tmdb.py] Searcing %s:\n%s" % (type_search, url)) logger.info("[Tmdb.py] Searcing %s:\n%s" % (type_search, url))
resultado = self.get_json(url) resultado = self.get_json(url)
if not isinstance(resultado, dict):
resultado = ast.literal_eval(resultado.decode('utf-8'))
total_results = resultado.get("total_results", -1) total_results = resultado.get("total_results", -1)
total_pages = resultado.get("total_pages", 1) total_pages = resultado.get("total_pages", 1)
@@ -1036,7 +1062,7 @@ class Tmdb(object):
results = resultado["results"] results = resultado["results"]
if self.busqueda_filtro and results: if self.busqueda_filtro and results:
# TODO documentar esta parte # TODO documentar esta parte
for key, value in dict(self.busqueda_filtro).items(): for key, value in list(dict(self.busqueda_filtro).items()):
for r in results[:]: for r in results[:]:
if key not in r or r[key] != value: if key not in r or r[key] != value:
results.remove(r) results.remove(r)
@@ -1184,6 +1210,8 @@ class Tmdb(object):
(self.busqueda_tipo, self.busqueda_id, self.busqueda_idioma)) (self.busqueda_tipo, self.busqueda_id, self.busqueda_idioma))
resultado = self.get_json(url) resultado = self.get_json(url)
if not isinstance(resultado, dict):
resultado = ast.literal_eval(resultado.decode('utf-8'))
if 'overview' in resultado: if 'overview' in resultado:
self.result['overview'] = resultado['overview'] self.result['overview'] = resultado['overview']
@@ -1316,6 +1344,8 @@ class Tmdb(object):
logger.info("[Tmdb.py] Searcing " + buscando) logger.info("[Tmdb.py] Searcing " + buscando)
try: try:
self.temporada[numtemporada] = self.get_json(url) self.temporada[numtemporada] = self.get_json(url)
if not isinstance(self.temporada[numtemporada], dict):
self.temporada[numtemporada] = ast.literal_eval(self.temporada[numtemporada].decode('utf-8'))
except: except:
logger.error("Unable to get the season") logger.error("Unable to get the season")
@@ -1356,6 +1386,8 @@ class Tmdb(object):
return {} return {}
temporada = self.get_temporada(numtemporada) temporada = self.get_temporada(numtemporada)
if not isinstance(temporada, dict):
temporada = ast.literal_eval(temporada.decode('utf-8'))
if not temporada: if not temporada:
# Se ha producido un error # Se ha producido un error
return {} return {}
@@ -1388,9 +1420,9 @@ class Tmdb(object):
dic_aux = dict((i['id'], i) for i in ret_dic["temporada_crew"]) dic_aux = dict((i['id'], i) for i in ret_dic["temporada_crew"])
for e in temporada["episodes"]: for e in temporada["episodes"]:
for crew in e['crew']: for crew in e['crew']:
if crew['id'] not in dic_aux.keys(): if crew['id'] not in list(dic_aux.keys()):
dic_aux[crew['id']] = crew dic_aux[crew['id']] = crew
ret_dic["temporada_crew"] = dic_aux.values() ret_dic["temporada_crew"] = list(dic_aux.values())
# Obtener datos del capitulo si procede # Obtener datos del capitulo si procede
if capitulo != -1: if capitulo != -1:
@@ -1429,6 +1461,8 @@ class Tmdb(object):
% (self.busqueda_tipo, self.result['id'], self.busqueda_idioma) % (self.busqueda_tipo, self.result['id'], self.busqueda_idioma)
dict_videos = self.get_json(url) dict_videos = self.get_json(url)
if not isinstance(dict_videos, dict):
dict_videos = ast.literal_eval(dict_videos.decode('utf-8'))
if dict_videos['results']: if dict_videos['results']:
dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size'])) dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size']))
@@ -1440,6 +1474,8 @@ class Tmdb(object):
% (self.busqueda_tipo, self.result['id']) % (self.busqueda_tipo, self.result['id'])
dict_videos = self.get_json(url) dict_videos = self.get_json(url)
if not isinstance(dict_videos, dict):
dict_videos = ast.literal_eval(dict_videos.decode('utf-8'))
if dict_videos['results']: if dict_videos['results']:
dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size'])) dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size']))
@@ -1481,13 +1517,13 @@ class Tmdb(object):
if not origen: if not origen:
origen = self.result origen = self.result
if 'credits' in origen.keys(): if 'credits' in list(origen.keys()):
dic_origen_credits = origen['credits'] dic_origen_credits = origen['credits']
origen['credits_cast'] = dic_origen_credits.get('cast', []) origen['credits_cast'] = dic_origen_credits.get('cast', [])
origen['credits_crew'] = dic_origen_credits.get('crew', []) origen['credits_crew'] = dic_origen_credits.get('crew', [])
del origen['credits'] del origen['credits']
items = origen.items() items = list(origen.items())
# Informacion Temporada/episodio # Informacion Temporada/episodio
if ret_infoLabels['season'] and self.temporada.get(ret_infoLabels['season']): if ret_infoLabels['season'] and self.temporada.get(ret_infoLabels['season']):
@@ -1496,14 +1532,14 @@ class Tmdb(object):
if ret_infoLabels['episode']: if ret_infoLabels['episode']:
episodio = ret_infoLabels['episode'] episodio = ret_infoLabels['episode']
items.extend(self.get_episodio(ret_infoLabels['season'], episodio).items()) items.extend(list(self.get_episodio(ret_infoLabels['season'], episodio).items()))
# logger.info("ret_infoLabels" % ret_infoLabels) # logger.info("ret_infoLabels" % ret_infoLabels)
for k, v in items: for k, v in items:
if not v: if not v:
continue continue
elif type(v) == str: elif isinstance(v, str):
v = re.sub(r"\n|\r|\t", "", v) v = re.sub(r"\n|\r|\t", "", v)
# fix # fix
if v == "None": if v == "None":
@@ -1572,7 +1608,7 @@ class Tmdb(object):
elif k == 'credits_cast' or k == 'temporada_cast' or k == 'episodio_guest_stars': elif k == 'credits_cast' or k == 'temporada_cast' or k == 'episodio_guest_stars':
dic_aux = dict((name, character) for (name, character) in l_castandrole) dic_aux = dict((name, character) for (name, character) in l_castandrole)
l_castandrole.extend([(p['name'], p['character']) for p in v if p['name'] not in dic_aux.keys()]) l_castandrole.extend([(p['name'], p['character']) for p in v if p['name'] not in list(dic_aux.keys())])
elif k == 'videos': elif k == 'videos':
if not isinstance(v, list): if not isinstance(v, list):
+44 -42
View File
@@ -7,9 +7,14 @@
# del addon y también Kodi. # del addon y también Kodi.
# ------------------------------------------------------------ # ------------------------------------------------------------
import re from future import standard_library
standard_library.install_aliases()
#from builtins import str
from future.builtins import object
import urllib2 import urllib.request, urllib.error, urllib.parse
import re
from core import jsontools from core import jsontools
from core import scrapertools from core import scrapertools
@@ -218,7 +223,7 @@ def set_infoLabels_item(item):
break break
_next = list_episodes['links']['next'] _next = list_episodes['links']['next']
if type(_next) == int: if isinstance(_next, int):
page = _next page = _next
else: else:
break break
@@ -330,7 +335,7 @@ def completar_codigos(item):
break break
class Tvdb: class Tvdb(object):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.__check_token() self.__check_token()
@@ -398,12 +403,12 @@ class Tvdb:
params = {"apikey": apikey} params = {"apikey": apikey}
try: try:
req = urllib2.Request(url, data=jsontools.dump(params), headers=DEFAULT_HEADERS) req = urllib.request.Request(url, data=jsontools.dump(params), headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
logger.error("error en: %s" % message) logger.error("error en: %s" % message)
@@ -426,12 +431,12 @@ class Tvdb:
url = HOST + "/refresh_token" url = HOST + "/refresh_token"
try: try:
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except urllib2.HTTPError, err: except urllib.error.HTTPError as err:
logger.error("err.code es %s" % err.code) logger.error("err.code es %s" % err.code)
# si hay error 401 es que el token se ha pasado de tiempo y tenemos que volver a llamar a login # si hay error 401 es que el token se ha pasado de tiempo y tenemos que volver a llamar a login
if err.code == 401: if err.code == 401:
@@ -439,7 +444,7 @@ class Tvdb:
else: else:
raise raise
except Exception, ex: except Exception as ex:
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
logger.error("error en: %s" % message) logger.error("error en: %s" % message)
@@ -525,19 +530,18 @@ class Tvdb:
params = {"airedSeason": "%s" % season, "airedEpisode": "%s" % episode} params = {"airedSeason": "%s" % season, "airedEpisode": "%s" % episode}
try: try:
import urllib params = urllib.parse.urlencode(params)
params = urllib.urlencode(params)
url = HOST + "/series/%s/episodes/query?%s" % (_id, params) url = HOST + "/series/%s/episodes/query?%s" % (_id, params)
DEFAULT_HEADERS["Accept-Language"] = lang DEFAULT_HEADERS["Accept-Language"] = lang
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
logger.error("error en: %s" % message) logger.error("error en: %s" % message)
@@ -595,12 +599,12 @@ class Tvdb:
url = HOST + "/series/%s/episodes?page=%s" % (_id, page) url = HOST + "/series/%s/episodes?page=%s" % (_id, page)
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
logger.error("error en: %s" % message) logger.error("error en: %s" % message)
@@ -682,13 +686,13 @@ class Tvdb:
try: try:
DEFAULT_HEADERS["Accept-Language"] = lang DEFAULT_HEADERS["Accept-Language"] = lang
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
if type(ex) == urllib2.HTTPError: if isinstance(ex, urllib).HTTPError:
logger.debug("code es %s " % ex.code) logger.debug("code es %s " % ex.code)
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
@@ -741,20 +745,19 @@ class Tvdb:
elif zap2it_id: elif zap2it_id:
params["zap2itId"] = zap2it_id params["zap2itId"] = zap2it_id
import urllib params = urllib.parse.urlencode(params)
params = urllib.urlencode(params)
DEFAULT_HEADERS["Accept-Language"] = lang DEFAULT_HEADERS["Accept-Language"] = lang
url = HOST + "/search/series?%s" % params url = HOST + "/search/series?%s" % params
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
if type(ex) == urllib2.HTTPError: if isinstance(ex, urllib).HTTPError:
logger.debug("code es %s " % ex.code) logger.debug("code es %s " % ex.code)
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
@@ -835,15 +838,15 @@ class Tvdb:
try: try:
DEFAULT_HEADERS["Accept-Language"] = lang DEFAULT_HEADERS["Accept-Language"] = lang
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
if type(ex) == urllib2.HTTPError: if isinstance(ex, urllib).HTTPError:
logger.debug("code es %s " % ex.code) logger.debug("code es %s " % ex.code)
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
@@ -905,18 +908,17 @@ class Tvdb:
try: try:
import urllib params = urllib.parse.urlencode(params)
params = urllib.urlencode(params)
DEFAULT_HEADERS["Accept-Language"] = lang DEFAULT_HEADERS["Accept-Language"] = lang
url = HOST + "/series/%s/images/query?%s" % (_id, params) url = HOST + "/series/%s/images/query?%s" % (_id, params)
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
except Exception, ex: except Exception as ex:
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
logger.error("error en: %s" % message) logger.error("error en: %s" % message)
@@ -946,8 +948,8 @@ class Tvdb:
DEFAULT_HEADERS["Accept-Language"] = lang DEFAULT_HEADERS["Accept-Language"] = lang
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
req = urllib2.Request(url, headers=DEFAULT_HEADERS) req = urllib.request.Request(url, headers=DEFAULT_HEADERS)
response = urllib2.urlopen(req) response = urllib.request.urlopen(req)
html = response.read() html = response.read()
response.close() response.close()
@@ -1039,7 +1041,7 @@ class Tvdb:
# origen['credits_crew'] = dic_origen_credits.get('crew', []) # origen['credits_crew'] = dic_origen_credits.get('crew', [])
# del origen['credits'] # del origen['credits']
items = origen.items() items = list(origen.items())
for k, v in items: for k, v in items:
if not v: if not v:
@@ -1118,7 +1120,7 @@ class Tvdb:
elif k == 'cast': elif k == 'cast':
dic_aux = dict((name, character) for (name, character) in l_castandrole) dic_aux = dict((name, character) for (name, character) in l_castandrole)
l_castandrole.extend([(p['name'], p['role']) for p in v if p['name'] not in dic_aux.keys()]) l_castandrole.extend([(p['name'], p['role']) for p in v if p['name'] not in list(dic_aux.keys())])
else: else:
logger.debug("Atributos no añadidos: %s=%s" % (k, v)) logger.debug("Atributos no añadidos: %s=%s" % (k, v))
+119 -176
View File
@@ -3,6 +3,11 @@
# Common Library Tools # Common Library Tools
# ------------------------------------------------------------ # ------------------------------------------------------------
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import errno import errno
import math import math
import traceback import traceback
@@ -130,7 +135,10 @@ def save_movie(item):
else: else:
base_name = item.contentTitle base_name = item.contentTitle
base_name = unicode(filetools.validate_path(base_name.replace('/', '-')), "utf8").encode("utf8") if not PY3:
base_name = unicode(filetools.validate_path(base_name.replace('/', '-')), "utf8").encode("utf8")
else:
base_name = filetools.validate_path(base_name.replace('/', '-'))
if config.get_setting("lowerize_title", "videolibrary") == 0: if config.get_setting("lowerize_title", "videolibrary") == 0:
base_name = base_name.lower() base_name = base_name.lower()
@@ -191,9 +199,12 @@ def save_movie(item):
# Si se ha marcado la opción de url de emergencia, se añade ésta a la película después de haber ejecutado Findvideos del canal # Si se ha marcado la opción de url de emergencia, se añade ésta a la película después de haber ejecutado Findvideos del canal
try: try:
headers = {}
if item.headers:
headers = item.headers
channel = generictools.verify_channel(item.channel) channel = generictools.verify_channel(item.channel)
if config.get_setting("emergency_urls", channel) in [1, 3]: if config.get_setting("emergency_urls", channel) in [1, 3]:
item = emergency_urls(item, None, json_path) item = emergency_urls(item, None, json_path, headers=headers)
if item_nfo.emergency_urls and not isinstance(item_nfo.emergency_urls, dict): if item_nfo.emergency_urls and not isinstance(item_nfo.emergency_urls, dict):
del item_nfo.emergency_urls del item_nfo.emergency_urls
if not item_nfo.emergency_urls: if not item_nfo.emergency_urls:
@@ -224,7 +235,7 @@ def save_movie(item):
return 0, 0, -1 return 0, 0, -1
def filter_list(episodelist, action=None, path=None): def filter_list(episodelist, action=None, path=None):
if path: path = path.decode('utf8') # if path: path = path.decode('utf8')
# import xbmc # import xbmc
# if xbmc.getCondVisibility('system.platform.windows') > 0: path = path.replace('smb:','').replace('/','\\') # if xbmc.getCondVisibility('system.platform.windows') > 0: path = path.replace('smb:','').replace('/','\\')
channel_prefs = {} channel_prefs = {}
@@ -397,17 +408,29 @@ def save_tvshow(item, episodelist):
return 0, 0, -1, path return 0, 0, -1, path
_id = item.infoLabels['code'][0] _id = item.infoLabels['code'][0]
if not item.infoLabels['code'][0] or item.infoLabels['code'][0] == 'None':
if item.infoLabels['code'][1] and item.infoLabels['code'][1] != 'None':
_id = item.infoLabels['code'][1]
elif item.infoLabels['code'][2] and item.infoLabels['code'][2] != 'None':
_id = item.infoLabels['code'][2]
else:
logger.error("NO ENCONTRADO EN SCRAPER O NO TIENE code: " + item.url
+ ' / ' + item.infoLabels['code'])
return 0, 0, -1, path
if config.get_setting("original_title_folder", "videolibrary") == 1 and item.infoLabels['originaltitle']: if config.get_setting("original_title_folder", "videolibrary") == 1 and item.infoLabels['originaltitle']:
base_name = item.infoLabels[u'originaltitle'] base_name = item.infoLabels['originaltitle']
elif item.infoLabels['tvshowtitle']: elif item.infoLabels['tvshowtitle']:
base_name = item.infoLabels[u'tvshowtitle'] base_name = item.infoLabels['tvshowtitle']
elif item.infoLabels['title']: elif item.infoLabels['title']:
base_name = item.infoLabels[u'title'] base_name = item.infoLabels['title']
else: else:
base_name = u'%s' % item.contentSerieName base_name = item.contentSerieName
base_name = unicode(filetools.validate_path(base_name.replace('/', '-')), "utf8").encode("utf8") if not PY3:
base_name = unicode(filetools.validate_path(base_name.replace('/', '-')), "utf8").encode("utf8")
else:
base_name = filetools.validate_path(base_name.replace('/', '-'))
if config.get_setting("lowerize_title", "videolibrary") == 0: if config.get_setting("lowerize_title", "videolibrary") == 0:
base_name = base_name.lower() base_name = base_name.lower()
@@ -415,7 +438,7 @@ def save_tvshow(item, episodelist):
for raiz, subcarpetas, ficheros in filetools.walk(TVSHOWS_PATH): for raiz, subcarpetas, ficheros in filetools.walk(TVSHOWS_PATH):
for c in subcarpetas: for c in subcarpetas:
code = scrapertools.find_single_match(c, '\[(.*?)\]') code = scrapertools.find_single_match(c, '\[(.*?)\]')
if code and code in item.infoLabels['code']: if code and code != 'None' and code in item.infoLabels['code']:
path = filetools.join(raiz, c) path = filetools.join(raiz, c)
_id = code _id = code
break break
@@ -425,7 +448,7 @@ def save_tvshow(item, episodelist):
logger.info("Creating series directory: " + path) logger.info("Creating series directory: " + path)
try: try:
filetools.mkdir(path) filetools.mkdir(path)
except OSError, exception: except OSError as exception:
if exception.errno != errno.EEXIST: if exception.errno != errno.EEXIST:
raise raise
@@ -518,7 +541,7 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
news_in_playcounts = {} news_in_playcounts = {}
# Listamos todos los ficheros de la serie, asi evitamos tener que comprobar si existe uno por uno # Listamos todos los ficheros de la serie, asi evitamos tener que comprobar si existe uno por uno
raiz, carpetas_series, ficheros = filetools.walk(path).next() raiz, carpetas_series, ficheros = next(filetools.walk(path))
ficheros = [filetools.join(path, f) for f in ficheros] ficheros = [filetools.join(path, f) for f in ficheros]
nostrm_episodelist = [] nostrm_episodelist = []
@@ -550,7 +573,11 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
tags = [] tags = []
if config.get_setting("enable_filter", "videolibrary"): if config.get_setting("enable_filter", "videolibrary"):
tags = [x.strip() for x in config.get_setting("filters", "videolibrary").lower().split(",")] tags = [x.strip() for x in config.get_setting("filters", "videolibrary").lower().split(",")]
for e in episodelist: for e in episodelist:
headers = {}
if e.headers:
headers = e.headers
if tags != [] and tags != None and any(tag in e.title.lower() for tag in tags): if tags != [] and tags != None and any(tag in e.title.lower() for tag in tags):
continue continue
@@ -567,31 +594,34 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
if overwrite: #pero solo si se se sobrescriben los .json if overwrite: #pero solo si se se sobrescriben los .json
json_epi = Item().fromjson(filetools.read(json_path)) #Leemos el .json json_epi = Item().fromjson(filetools.read(json_path)) #Leemos el .json
if json_epi.emergency_urls: #si existen las urls de emergencia... if json_epi.emergency_urls: #si existen las urls de emergencia...
e.emergency_urls = json_epi.emergency_urls #... las copiamos e.emergency_urls = json_epi.emergency_urls #... las copiamos
else: #y si no... else: #y si no...
e = emergency_urls(e, channel, json_path) #... las generamos e = emergency_urls(e, channel, json_path, headers=headers) #... las generamos
else: else:
e = emergency_urls(e, channel, json_path) #Si el episodio no existe, generamos las urls e = emergency_urls(e, channel, json_path, headers=headers) #Si el episodio no existe, generamos las urls
if e.emergency_urls: #Si ya tenemos urls... if e.emergency_urls: #Si ya tenemos urls...
emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo
elif emergency_urls_stat == 2 and e.contentType == 'episode': #Borramos urls de emergencia? elif emergency_urls_stat == 2 and e.contentType == 'episode': #Borramos urls de emergencia?
if e.emergency_urls: del e.emergency_urls if e.emergency_urls: del e.emergency_urls
emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo
elif emergency_urls_stat == 3 and e.contentType == 'episode': #Actualizamos urls de emergencia? elif emergency_urls_stat == 3 and e.contentType == 'episode': #Actualizamos urls de emergencia?
if not silent: if not silent:
p_dialog.update(0, 'Cacheando enlaces y archivos .torrent...', e.title) #progress dialog p_dialog.update(0, 'Cacheando enlaces y archivos .torrent...', e.title) #progress dialog
e = emergency_urls(e, channel, json_path) #generamos las urls e = emergency_urls(e, channel, json_path, headers=headers) #generamos las urls
if e.emergency_urls: #Si ya tenemos urls... if e.emergency_urls: #Si ya tenemos urls...
emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo
if not e.infoLabels["tmdb_id"] or (serie.infoLabels["tmdb_id"] and e.infoLabels["tmdb_id"] != serie.infoLabels["tmdb_id"]): #en series multicanal, prevalece el infolabels... if not e.infoLabels["tmdb_id"] or (serie.infoLabels["tmdb_id"] and e.infoLabels["tmdb_id"] != serie.infoLabels["tmdb_id"]): #en series multicanal, prevalece el infolabels...
e.infoLabels = serie.infoLabels #... del canal actual y no el del original e.infoLabels = serie.infoLabels #... del canal actual y no el del original
e.contentSeason, e.contentEpisodeNumber = season_episode.split("x") e.contentSeason, e.contentEpisodeNumber = season_episode.split("x")
if e.videolibray_emergency_urls:
del e.videolibray_emergency_urls
if e.channel_redir:
del e.channel_redir #... y se borran las marcas de redirecciones
new_episodelist.append(e) new_episodelist.append(e)
except: except:
if e.contentType == 'episode': if e.contentType == 'episode':
logger.error("Unable to save %s emergency urls in the video library" % e.contentTitle) logger.error("Unable to save %s emergency urls in the video library" % e.contentTitle)
logger.error(traceback.format_exc())
continue continue
# No hay lista de episodios, no hay nada que guardar # No hay lista de episodios, no hay nada que guardar
@@ -600,18 +630,35 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
return 0, 0, 0 return 0, 0, 0
# fix float porque la division se hace mal en python 2.x # fix float porque la division se hace mal en python 2.x
t = float(100) / len(new_episodelist) try:
t = float(100) / len(new_episodelist)
except:
t = 0
last_season_episode = ''
for i, e in enumerate(scraper.sort_episode_list(new_episodelist)): for i, e in enumerate(scraper.sort_episode_list(new_episodelist)):
if not silent: if not silent:
p_dialog.update(int(math.ceil((i + 1) * t)), config.get_localized_string(60064), e.title) p_dialog.update(int(math.ceil((i + 1) * t)), config.get_localized_string(60064), e.title)
high_sea = e.contentSeason
high_epi = e.contentEpisodeNumber
if scrapertools.find_single_match(e.title, '[a|A][l|L]\s*(\d+)'):
high_epi = int(scrapertools.find_single_match(e.title, 'al\s*(\d+)'))
max_sea = e.infoLabels["number_of_seasons"]
max_epi = 0
if e.infoLabels["number_of_seasons"] and (e.infoLabels["temporada_num_episodios"] or e.infoLabels["number_of_seasons"] == 1):
if e.infoLabels["number_of_seasons"] == 1 and e.infoLabels["number_of_episodes"]:
max_epi = e.infoLabels["number_of_episodes"]
else:
max_epi = e.infoLabels["temporada_num_episodios"]
season_episode = "%sx%s" % (e.contentSeason, str(e.contentEpisodeNumber).zfill(2)) season_episode = "%sx%s" % (e.contentSeason, str(e.contentEpisodeNumber).zfill(2))
strm_path = filetools.join(path, "%s.strm" % season_episode) strm_path = filetools.join(path, "%s.strm" % season_episode)
nfo_path = filetools.join(path, "%s.nfo" % season_episode) nfo_path = filetools.join(path, "%s.nfo" % season_episode)
json_path = filetools.join(path, ("%s [%s].json" % (season_episode, e.channel)).lower()) json_path = filetools.join(path, ("%s [%s].json" % (season_episode, e.channel)).lower())
if season_episode in nostrm_episodelist: if season_episode in nostrm_episodelist:
logger.error('Error in the structure of the Video Library: Seriese ' + serie.contentSerieName + ' ' + season_episode)
continue continue
strm_exists = strm_path in ficheros strm_exists = strm_path in ficheros
nfo_exists = nfo_path in ficheros nfo_exists = nfo_path in ficheros
@@ -659,8 +706,10 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
if not item_nfo: if not item_nfo:
head_nfo, item_nfo = read_nfo(nfo_path) head_nfo, item_nfo = read_nfo(nfo_path)
if not e.infoLabels["tmdb_id"] or (item_nfo.infoLabels["tmdb_id"] and e.infoLabels["tmdb_id"] != item_nfo.infoLabels["tmdb_id"]): #en series multicanal, prevalece el infolabels... # En series multicanal, prevalece el infolabels del canal actual y no el del original
e.infoLabels = item_nfo.infoLabels #... del canal actual y no el del original if not e.infoLabels["tmdb_id"] or (item_nfo.infoLabels["tmdb_id"] \
and e.infoLabels["tmdb_id"] != item_nfo.infoLabels["tmdb_id"]):
e.infoLabels = item_nfo.infoLabels
if filetools.write(json_path, e.tojson()): if filetools.write(json_path, e.tojson()):
if not json_exists: if not json_exists:
@@ -688,10 +737,12 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
if not silent and p_dialog.iscanceled(): if not silent and p_dialog.iscanceled():
break break
#logger.debug('high_sea x high_epi: %sx%s' % (str(high_sea), str(high_epi)))
#logger.debug('max_sea x max_epi: %sx%s' % (str(max_sea), str(max_epi)))
if not silent: if not silent:
p_dialog.close() p_dialog.close()
if news_in_playcounts: if news_in_playcounts or emergency_urls_succ or serie.infoLabels["status"] == "Ended" or serie.infoLabels["status"] == "Canceled":
# Si hay nuevos episodios los marcamos como no vistos en tvshow.nfo ... # Si hay nuevos episodios los marcamos como no vistos en tvshow.nfo ...
tvshow_path = filetools.join(path, "tvshow.nfo") tvshow_path = filetools.join(path, "tvshow.nfo")
try: try:
@@ -703,16 +754,27 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
if emergency_urls_succ: if emergency_urls_succ:
if tvshow_item.emergency_urls and not isinstance(tvshow_item.emergency_urls, dict): if tvshow_item.emergency_urls and not isinstance(tvshow_item.emergency_urls, dict):
del tvshow_item.emergency_urls del tvshow_item.emergency_urls
if emergency_urls_stat in [1, 3]: #Operación de guardar/actualizar enlaces if emergency_urls_stat in [1, 3]: #Operación de guardar/actualizar enlaces
if not tvshow_item.emergency_urls: if not tvshow_item.emergency_urls:
tvshow_item.emergency_urls = dict() tvshow_item.emergency_urls = dict()
tvshow_item.emergency_urls.update({serie.channel: True}) if tvshow_item.library_urls.get(serie.channel, False):
elif emergency_urls_stat == 2: #Operación de Borrar enlaces tvshow_item.emergency_urls.update({serie.channel: True})
elif emergency_urls_stat == 2: #Operación de Borrar enlaces
if tvshow_item.emergency_urls and tvshow_item.emergency_urls.get(serie.channel, False): if tvshow_item.emergency_urls and tvshow_item.emergency_urls.get(serie.channel, False):
tvshow_item.emergency_urls.pop(serie.channel, None) #borramos la entrada del .nfo tvshow_item.emergency_urls.pop(serie.channel, None) #borramos la entrada del .nfo
if tvshow_item.active == 30: if tvshow_item.active == 30:
tvshow_item.active = 1 tvshow_item.active = 1
if tvshow_item.infoLabels["tmdb_id"] == serie.infoLabels["tmdb_id"]:
tvshow_item.infoLabels = serie.infoLabels
tvshow_item.infoLabels["title"] = tvshow_item.infoLabels["tvshowtitle"]
if max_sea == high_sea and max_epi == high_epi and (tvshow_item.infoLabels["status"] == "Ended"
or tvshow_item.infoLabels["status"] == "Canceled") and insertados == 0 and fallidos == 0:
tvshow_item.active = 0 # ... no la actualizaremos más
logger.debug("%s [%s]: serie 'Terminada' o 'Cancelada'. Se desactiva la actualización periódica" % \
(serie.contentSerieName, serie.channel))
update_last = datetime.date.today() update_last = datetime.date.today()
tvshow_item.update_last = update_last.strftime('%Y-%m-%d') tvshow_item.update_last = update_last.strftime('%Y-%m-%d')
update_next = datetime.date.today() + datetime.timedelta(days=int(tvshow_item.active)) update_next = datetime.date.today() + datetime.timedelta(days=int(tvshow_item.active))
@@ -819,10 +881,10 @@ def add_tvshow(item, channel=None):
if not channel: if not channel:
try: try:
#channel = __import__('channels.%s' % item.channel, fromlist=["channels.%s" % item.channel]) # channel = __import__('channels.%s' % item.channel, fromlist=["channels.%s" % item.channel])
channel = __import__('specials.%s' % channel_alt, fromlist=["specials.%s" % channel_alt]) channel = __import__('specials.%s' % channel_alt, fromlist=["specials.%s" % channel_alt])
except ImportError: except ImportError:
exec "import channels." + item.channel + " as channel" exec("import channels." + item.channel + " as channel")
#Para desambiguar títulos, se provoca que TMDB pregunte por el título realmente deseado #Para desambiguar títulos, se provoca que TMDB pregunte por el título realmente deseado
#El usuario puede seleccionar el título entre los ofrecidos en la primera pantalla #El usuario puede seleccionar el título entre los ofrecidos en la primera pantalla
@@ -836,15 +898,15 @@ def add_tvshow(item, channel=None):
# del item.tmdb_stat #Limpiamos el status para que no se grabe en la Videoteca # del item.tmdb_stat #Limpiamos el status para que no se grabe en la Videoteca
# Obtiene el listado de episodios # Obtiene el listado de episodios
#if item.channel == 'community':
itemlist = getattr(channel, item.action)(item) itemlist = getattr(channel, item.action)(item)
global magnet_caching
magnet_caching = False
insertados, sobreescritos, fallidos, path = save_tvshow(item, itemlist) insertados, sobreescritos, fallidos, path = save_tvshow(item, itemlist)
if not insertados and not sobreescritos and not fallidos: if not insertados and not sobreescritos and not fallidos:
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60067)) platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60067))
logger.error("The %s series could not be added to the video library. Could not get any episode" logger.error("The %s series could not be added to the video library. Could not get any episode" % item.show)
% item.show)
elif fallidos == -1: elif fallidos == -1:
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60068)) platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60068))
@@ -856,8 +918,7 @@ def add_tvshow(item, channel=None):
else: else:
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60070)) platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60070))
logger.info("%s episodes of the %s series have been added to the video library" % logger.info("%s episodes of the %s series have been added to the video library" % (insertados, item.show))
(insertados, item.show))
if config.is_xbmc(): if config.is_xbmc():
if config.get_setting("sync_trakt_new_tvshow", "videolibrary"): if config.get_setting("sync_trakt_new_tvshow", "videolibrary"):
import xbmc import xbmc
@@ -872,9 +933,15 @@ def add_tvshow(item, channel=None):
xbmc_videolibrary.sync_trakt_addon(path) xbmc_videolibrary.sync_trakt_addon(path)
def emergency_urls(item, channel=None, path=None): def emergency_urls(item, channel=None, path=None, headers={}):
logger.info() logger.info()
import re import re
from servers import torrent
try:
magnet_caching_e = magnet_caching
except:
magnet_caching_e = True
""" """
Llamamos a Findvideos del canal con la variable "item.videolibray_emergency_urls = True" para obtener la variable Llamamos a Findvideos del canal con la variable "item.videolibray_emergency_urls = True" para obtener la variable
"item.emergency_urls" con la lista de listas de tuplas de los enlaces torrent y de servidores directos para ese episodio o película "item.emergency_urls" con la lista de listas de tuplas de los enlaces torrent y de servidores directos para ese episodio o película
@@ -890,16 +957,27 @@ def emergency_urls(item, channel=None, path=None):
if hasattr(channel, 'findvideos'): #Si el canal tiene "findvideos"... if hasattr(channel, 'findvideos'): #Si el canal tiene "findvideos"...
item.videolibray_emergency_urls = True #... se marca como "lookup" item.videolibray_emergency_urls = True #... se marca como "lookup"
channel_save = item.channel #... guarda el canal original por si hay fail-over en Newpct1 channel_save = item.channel #... guarda el canal original por si hay fail-over en Newpct1
category_save = item.category #... guarda la categoría original por si hay fail-over o redirección en Newpct1
if item.channel_redir: #... si hay un redir, se restaura temporamente el canal alternativo
item.channel = scrapertools.find_single_match(item.url, 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').lower()
item.category = scrapertools.find_single_match(item.url, 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').capitalize()
item_res = getattr(channel, 'findvideos')(item) #... se procesa Findvideos item_res = getattr(channel, 'findvideos')(item) #... se procesa Findvideos
item_res.channel = channel_save #... restaura el canal original por si hay fail-over en Newpct1 item_res.channel = channel_save #... restaura el canal original por si hay fail-over en Newpct1
item_res.category = channel_save.capitalize() #... y la categoría item_res.category = category_save #... restaura la categoría original por si hay fail-over o redirección en Newpct1
item.category = category_save #... restaura la categoría original por si hay fail-over o redirección en Newpct1
del item_res.videolibray_emergency_urls #... y se borra la marca de lookup del item_res.videolibray_emergency_urls #... y se borra la marca de lookup
if item.videolibray_emergency_urls:
del item.videolibray_emergency_urls #... y se borra la marca de lookup original
except: except:
logger.error('ERROR when processing the title in Findvideos del Canal: ' + item.channel + ' / ' + item.title) logger.error('ERROR when processing the title in Findvideos del Canal: ' + item.channel + ' / ' + item.title)
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
item.channel = channel_save #... restaura el canal original por si hay fail-over o redirección en Newpct1
item.category = category_save #... restaura la categoría original por si hay fail-over o redirección en Newpct1
item_res = item.clone() #Si ha habido un error, se devuelve el Item original item_res = item.clone() #Si ha habido un error, se devuelve el Item original
if item_res.videolibray_emergency_urls: if item_res.videolibray_emergency_urls:
del item_res.videolibray_emergency_urls #... y se borra la marca de lookup del item_res.videolibray_emergency_urls #... y se borra la marca de lookup
if item.videolibray_emergency_urls:
del item.videolibray_emergency_urls #... y se borra la marca de lookup original
#Si el usuario ha activado la opción "emergency_urls_torrents", se descargarán los archivos .torrent de cada título #Si el usuario ha activado la opción "emergency_urls_torrents", se descargarán los archivos .torrent de cada título
else: #Si se han cacheado con éxito los enlaces... else: #Si se han cacheado con éxito los enlaces...
@@ -921,7 +999,9 @@ def emergency_urls(item, channel=None, path=None):
if item_res.post: post = item_res.post if item_res.post: post = item_res.post
for url in item_res.emergency_urls[0]: #Recorremos las urls de emergencia... for url in item_res.emergency_urls[0]: #Recorremos las urls de emergencia...
torrents_path = re.sub(r'(?:\.\w+$)', '_%s.torrent' % str(i).zfill(2), path) torrents_path = re.sub(r'(?:\.\w+$)', '_%s.torrent' % str(i).zfill(2), path)
path_real = caching_torrents(url, referer, post, torrents_path=torrents_path) #... para descargar los .torrents path_real = ''
if magnet_caching_e or not url.startswith('magnet'):
path_real = torrent.caching_torrents(url, referer, post, torrents_path=torrents_path, headers=headers) #... para descargar los .torrents
if path_real: #Si ha tenido éxito... if path_real: #Si ha tenido éxito...
item_res.emergency_urls[0][i-1] = path_real.replace(videolibrary_path, '') #se guarda el "path" relativo item_res.emergency_urls[0][i-1] = path_real.replace(videolibrary_path, '') #se guarda el "path" relativo
i += 1 i += 1
@@ -944,140 +1024,3 @@ def emergency_urls(item, channel=None, path=None):
#logger.debug(item_res.emergency_urls) #logger.debug(item_res.emergency_urls)
return item_res #Devolvemos el Item actualizado con los enlaces de emergencia return item_res #Devolvemos el Item actualizado con los enlaces de emergencia
def caching_torrents(url, referer=None, post=None, torrents_path=None, timeout=10, lookup=False, data_torrent=False):
if torrents_path != None:
logger.info("path = " + torrents_path)
else:
logger.info()
if referer and post:
logger.info('REFERER: ' + referer)
from core import httptools
torrent_file = ''
headers = {'Content-Type': 'application/x-www-form-urlencoded', 'Referer': referer} #Necesario para el Post del .Torrent
"""
Descarga en el path recibido el .torrent de la url recibida, y pasa el decode
Devuelve el path real del .torrent, o el path vacío si la operación no ha tenido éxito
"""
videolibrary_path = config.get_videolibrary_path() #Calculamos el path absoluto a partir de la Videoteca
if torrents_path == None:
if not videolibrary_path:
torrents_path = ''
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path #Si hay un error, devolvemos el "path" vacío
torrents_path = filetools.join(videolibrary_path, 'temp_torrents_Alfa', 'cliente_torrent_Alfa.torrent') #path de descarga temporal
if '.torrent' not in torrents_path:
torrents_path += '.torrent' #path para dejar el .torrent
torrents_path_encode = filetools.encode(torrents_path) #encode utf-8 del path
if url.endswith(".rar") or url.startswith("magnet:"): #No es un archivo .torrent
logger.error('It is not a Torrent file: ' + url)
torrents_path = ''
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path #Si hay un error, devolvemos el "path" vacío
try:
#Descargamos el .torrent
if referer and post: #Descarga con POST
response = httptools.downloadpage(url, headers=headers, post=post, follow_redirects=False, timeout=timeout)
else: #Descarga sin post
response = httptools.downloadpage(url, timeout=timeout)
if not response.sucess:
logger.error('.Torrent file not found: ' + url)
torrents_path = ''
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path #Si hay un error, devolvemos el "path" vacío
torrent_file = response.data
if "used CloudFlare" in torrent_file: #Si tiene CloudFlare, usamos este proceso
response = httptools.downloadpage("http://anonymouse.org/cgi-bin/anon-www.cgi/" + url.strip(), timeout=timeout)
if not response.sucess:
logger.error('Archivo .torrent no encontrado: ' + url)
torrents_path = ''
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path #Si hay un error, devolvemos el "path" vacío
torrent_file = response.data
#Si es un archivo .ZIP tratamos de extraer el contenido
if torrent_file.startswith("PK"):
logger.info('It is a .ZIP file: ' + url)
torrents_path_zip = filetools.join(videolibrary_path, 'temp_torrents_zip') #Carpeta de trabajo
torrents_path_zip = filetools.encode(torrents_path_zip)
torrents_path_zip_file = filetools.join(torrents_path_zip, 'temp_torrents_zip.zip') #Nombre del .zip
import time
filetools.rmdirtree(torrents_path_zip) #Borramos la carpeta temporal
time.sleep(1) #Hay que esperar, porque si no da error
filetools.mkdir(torrents_path_zip) #La creamos de nuevo
if filetools.write(torrents_path_zip_file, torrent_file): #Salvamos el .zip
torrent_file = '' #Borramos el contenido en memoria
try: #Extraemos el .zip
from core import ziptools
unzipper = ziptools.ziptools()
unzipper.extract(torrents_path_zip_file, torrents_path_zip)
except:
import xbmc
xbmc.executebuiltin('XBMC.Extract("%s", "%s")' % (torrents_path_zip_file, torrents_path_zip))
time.sleep(1)
import os
for root, folders, files in os.walk(torrents_path_zip): #Recorremos la carpeta para leer el .torrent
for file in files:
if file.endswith(".torrent"):
input_file = filetools.join(root, file) #nombre del .torrent
torrent_file = filetools.read(input_file) #leemos el .torrent
filetools.rmdirtree(torrents_path_zip) #Borramos la carpeta temporal
#Si no es un archivo .torrent (RAR, HTML,..., vacío) damos error
if not scrapertools.find_single_match(torrent_file, '^d\d+:.*?\d+:'):
logger.error('It is not a Torrent file: ' + url)
torrents_path = ''
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path #Si hay un error, devolvemos el "path" vacío
#Salvamos el .torrent
if not lookup:
if not filetools.write(torrents_path_encode, torrent_file):
logger.error('ERROR: Unwritten .torrent file: ' + torrents_path_encode)
torrents_path = '' #Si hay un error, devolvemos el "path" vacío
torrent_file = '' #... y el buffer del .torrent
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path
except:
torrents_path = '' #Si hay un error, devolvemos el "path" vacío
torrent_file = '' #... y el buffer del .torrent
logger.error('ERROR: .Torrent download process failed: ' + url + ' / ' + torrents_path_encode)
logger.error(traceback.format_exc())
#logger.debug(torrents_path)
if data_torrent:
return (torrents_path, torrent_file)
return torrents_path
def verify_url_torrent(url, timeout=5):
"""
Verifica si el archivo .torrent al que apunta la url está disponible, descargándolo en un area temporal
Entrada: url
Salida: True o False dependiendo del resultado de la operación
"""
if not url or url == 'javascript:;': #Si la url viene vacía...
return False #... volvemos con error
torrents_path = caching_torrents(url, timeout=timeout, lookup=True) #Descargamos el .torrent
if torrents_path: #Si ha tenido éxito...
return True
else:
return False
+35 -25
View File
@@ -3,22 +3,26 @@
# Zip Tools # Zip Tools
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
import io from builtins import object
import os import sys
PY3 = False
VFS = True
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int; VFS = False
import zipfile import zipfile
from platformcode import config, logger from platformcode import config, logger
from core import filetools
class ziptools: class ziptools(object):
def extract(self, file, dir, folder_to_extract="", overwrite_question=False, backup=False): def extract(self, file, dir, folder_to_extract="", overwrite_question=False, backup=False):
logger.info("file=%s" % file) logger.info("file=%s" % file)
logger.info("dir=%s" % dir) logger.info("dir=%s" % dir)
if not dir.endswith(':') and not os.path.exists(dir): if not dir.endswith(':') and not filetools.exists(dir):
os.mkdir(dir) filetools.mkdir(dir)
file = io.FileIO(file)
zf = zipfile.ZipFile(file) zf = zipfile.ZipFile(file)
if not folder_to_extract: if not folder_to_extract:
self._createstructure(file, dir) self._createstructure(file, dir)
@@ -30,60 +34,66 @@ class ziptools:
if not name.endswith('/'): if not name.endswith('/'):
logger.info("no es un directorio") logger.info("no es un directorio")
try: try:
(path, filename) = os.path.split(os.path.join(dir, name)) (path, filename) = filetools.split(filetools.join(dir, name))
logger.info("path=%s" % path) logger.info("path=%s" % path)
logger.info("name=%s" % name) logger.info("name=%s" % name)
if folder_to_extract: if folder_to_extract:
if path != os.path.join(dir, folder_to_extract): if path != filetools.join(dir, folder_to_extract):
break break
else: else:
os.makedirs(path) filetools.mkdir(path)
except: except:
pass pass
if folder_to_extract: if folder_to_extract:
outfilename = os.path.join(dir, filename) outfilename = filetools.join(dir, filename)
else: else:
outfilename = os.path.join(dir, name) outfilename = filetools.join(dir, name)
logger.info("outfilename=%s" % outfilename) logger.info("outfilename=%s" % outfilename)
try: try:
if os.path.exists(outfilename) and overwrite_question: if filetools.exists(outfilename) and overwrite_question:
from platformcode import platformtools from platformcode import platformtools
dyesno = platformtools.dialog_yesno("El archivo ya existe", dyesno = platformtools.dialog_yesno("El archivo ya existe",
"El archivo %s a descomprimir ya existe" \ "El archivo %s a descomprimir ya existe" \
", ¿desea sobrescribirlo?" \ ", ¿desea sobrescribirlo?" \
% os.path.basename(outfilename)) % filetools.basename(outfilename))
if not dyesno: if not dyesno:
break break
if backup: if backup:
import time import time
import shutil
hora_folder = "Copia seguridad [%s]" % time.strftime("%d-%m_%H-%M", time.localtime()) hora_folder = "Copia seguridad [%s]" % time.strftime("%d-%m_%H-%M", time.localtime())
backup = os.path.join(config.get_data_path(), 'backups', hora_folder, folder_to_extract) backup = filetools.join(config.get_data_path(), 'backups', hora_folder, folder_to_extract)
if not os.path.exists(backup): if not filetools.exists(backup):
os.makedirs(backup) filetools.mkdir(backup)
shutil.copy2(outfilename, os.path.join(backup, os.path.basename(outfilename))) filetools.copy(outfilename, filetools.join(backup, filetools.basename(outfilename)))
outfile = open(outfilename, 'wb') if not filetools.write(outfilename, zf.read(nameo), silent=True, vfs=VFS): #TRUNCA en FINAL en Kodi 19 con VFS
outfile.write(zf.read(nameo)) logger.error("Error en fichero " + nameo)
except: except:
import traceback
logger.error(traceback.format_exc())
logger.error("Error en fichero " + nameo) logger.error("Error en fichero " + nameo)
try:
zf.close()
except:
logger.info("Error cerrando .zip " + file)
def _createstructure(self, file, dir): def _createstructure(self, file, dir):
self._makedirs(self._listdirs(file), dir) self._makedirs(self._listdirs(file), dir)
def create_necessary_paths(filename): def create_necessary_paths(filename):
try: try:
(path, name) = os.path.split(filename) (path, name) = filetools.split(filename)
os.makedirs(path) filetools.mkdir(path)
except: except:
pass pass
def _makedirs(self, directories, basedir): def _makedirs(self, directories, basedir):
for dir in directories: for dir in directories:
curdir = os.path.join(basedir, dir) curdir = filetools.join(basedir, dir)
if not os.path.exists(curdir): if not filetools.exists(curdir):
os.mkdir(curdir) filetools.mkdir(curdir)
def _listdirs(self, file): def _listdirs(self, file):
zf = zipfile.ZipFile(file) zf = zipfile.ZipFile(file)
+1 -1
View File
@@ -12,7 +12,7 @@ from platformcode import config, logger
logger.info("init...") logger.info("init...")
librerias = xbmc.translatePath(os.path.join(config.get_runtime_path(), 'lib')) librerias = xbmc.translatePath(os.path.join(config.get_runtime_path(), 'lib'))
sys.path.insert(0, librerias) sys.path.append(librerias)
if not config.dev_mode(): if not config.dev_mode():
from platformcode import updater from platformcode import updater
+12
View File
@@ -0,0 +1,12 @@
from __future__ import absolute_import
import sys
__future_module__ = True
if sys.version_info[0] < 3:
from __builtin__ import *
# Overwrite any old definitions with the equivalent future.builtins ones:
from future.builtins import *
else:
raise ImportError('This package should not be accessible on Python 3. '
'Either you are trying to run from the python-future src folder '
'or your installation of python-future is corrupted.')
+67 -44
View File
@@ -1,4 +1,6 @@
# https://github.com/VeNoMouS/cloudscraper/tree/master
import logging import logging
import os
import re import re
import sys import sys
import ssl import ssl
@@ -9,6 +11,14 @@ try:
except ImportError: except ImportError:
import copy_reg as copyreg import copy_reg as copyreg
try:
from HTMLParser import HTMLParser
except ImportError:
if sys.version_info >= (3, 4):
import html
else:
from html.parser import HTMLParser
from copy import deepcopy from copy import deepcopy
from time import sleep from time import sleep
from collections import OrderedDict from collections import OrderedDict
@@ -31,13 +41,17 @@ except ImportError:
pass pass
try: try:
from urlparse import urlparse from urlparse import urlparse, urljoin
except ImportError: except ImportError:
from urllib.parse import urlparse from urllib.parse import urlparse, urljoin
# Add exceptions path
sys.path.append(os.path.join(os.path.dirname(__file__), 'exceptions'))
import cloudflare_exceptions # noqa: E402
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
__version__ = '1.2.19' __version__ = '1.2.24'
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@@ -91,6 +105,7 @@ class CloudScraper(Session):
'allow_brotli', 'allow_brotli',
True if 'brotli' in sys.modules.keys() else False True if 'brotli' in sys.modules.keys() else False
) )
self.user_agent = User_Agent( self.user_agent = User_Agent(
allow_brotli=self.allow_brotli, allow_brotli=self.allow_brotli,
browser=kwargs.pop('browser', None) browser=kwargs.pop('browser', None)
@@ -107,13 +122,16 @@ class CloudScraper(Session):
# Set a random User-Agent if no custom User-Agent has been set # Set a random User-Agent if no custom User-Agent has been set
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
self.headers = self.user_agent.headers self.headers = self.user_agent.headers
if not self.cipherSuite:
self.cipherSuite = self.user_agent.cipherSuite
if isinstance(self.cipherSuite, list):
self.cipherSuite = ':'.join(self.cipherSuite)
self.mount( self.mount(
'https://', 'https://',
CipherSuiteAdapter( CipherSuiteAdapter(
cipherSuite=':'.join(self.user_agent.cipherSuite) cipherSuite=self.cipherSuite
if not self.cipherSuite else ':'.join(self.cipherSuite)
if isinstance(self.cipherSuite, list) else self.cipherSuite
) )
) )
@@ -138,6 +156,20 @@ class CloudScraper(Session):
except ValueError as e: except ValueError as e:
print("Debug Error: {}".format(getattr(e, 'message', e))) print("Debug Error: {}".format(getattr(e, 'message', e)))
# ------------------------------------------------------------------------------- #
# Unescape / decode html entities
# ------------------------------------------------------------------------------- #
@staticmethod
def unescape(html_text):
if sys.version_info >= (3, 0):
if sys.version_info >= (3, 4):
return html.unescape(html_text)
return HTMLParser().unescape(html_text)
return HTMLParser().unescape(html_text)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
# Decode Brotli on older versions of urllib3 manually # Decode Brotli on older versions of urllib3 manually
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@@ -186,7 +218,7 @@ class CloudScraper(Session):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
_ = self._solveDepthCnt _ = self._solveDepthCnt
self._solveDepthCnt = 0 self._solveDepthCnt = 0
raise RuntimeError( raise cloudflare_exceptions.Cloudflare_Loop_Protection(
"!!Loop Protection!! We have tried to solve {} time(s) in a row.".format(_) "!!Loop Protection!! We have tried to solve {} time(s) in a row.".format(_)
) )
@@ -269,7 +301,7 @@ class CloudScraper(Session):
def is_Challenge_Request(self, resp): def is_Challenge_Request(self, resp):
if self.is_Firewall_Blocked(resp): if self.is_Firewall_Blocked(resp):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError('Cloudflare has blocked this request (Code 1020 Detected).') raise cloudflare_exceptions.Cloudflare_Block('Cloudflare has blocked this request (Code 1020 Detected).')
if self.is_reCaptcha_Challenge(resp) or self.is_IUAM_Challenge(resp): if self.is_reCaptcha_Challenge(resp) or self.is_IUAM_Challenge(resp):
return True return True
@@ -280,17 +312,18 @@ class CloudScraper(Session):
# Try to solve cloudflare javascript challenge. # Try to solve cloudflare javascript challenge.
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@staticmethod def IUAM_Challenge_Response(self, body, url, interpreter):
def IUAM_Challenge_Response(body, url, interpreter):
try: try:
challengeUUID = re.search( challengeUUID = re.search(
r'id="challenge-form" action="(?P<challengeUUID>\S+)"', r'id="challenge-form" action="(?P<challengeUUID>\S+)"',
body, re.M | re.DOTALL body, re.M | re.DOTALL
).groupdict().get('challengeUUID', '') ).groupdict().get('challengeUUID', '')
payload = OrderedDict(re.findall(r'name="(r|jschl_vc|pass)"\svalue="(.*?)"', body)) payload = OrderedDict(re.findall(r'name="(r|jschl_vc|pass)"\svalue="(.*?)"', body))
except AttributeError: except AttributeError:
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError( raise cloudflare_exceptions.Cloudflare_Error_IUAM(
"Cloudflare IUAM detected, unfortunately we can't extract the parameters correctly." "Cloudflare IUAM detected, unfortunately we can't extract the parameters correctly."
) )
@@ -301,7 +334,7 @@ class CloudScraper(Session):
interpreter interpreter
).solveChallenge(body, hostParsed.netloc) ).solveChallenge(body, hostParsed.netloc)
except Exception as e: except Exception as e:
raise RuntimeError( raise cloudflare_exceptions.Cloudflare_Error_IUAM(
'Unable to parse Cloudflare anti-bots page: {}'.format( 'Unable to parse Cloudflare anti-bots page: {}'.format(
getattr(e, 'message', e) getattr(e, 'message', e)
) )
@@ -311,7 +344,7 @@ class CloudScraper(Session):
'url': '{}://{}{}'.format( 'url': '{}://{}{}'.format(
hostParsed.scheme, hostParsed.scheme,
hostParsed.netloc, hostParsed.netloc,
challengeUUID self.unescape(challengeUUID)
), ),
'data': payload 'data': payload
} }
@@ -320,8 +353,7 @@ class CloudScraper(Session):
# Try to solve the reCaptcha challenge via 3rd party. # Try to solve the reCaptcha challenge via 3rd party.
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@staticmethod def reCaptcha_Challenge_Response(self, provider, provider_params, body, url):
def reCaptcha_Challenge_Response(provider, provider_params, body, url):
try: try:
payload = re.search( payload = re.search(
r'(name="r"\svalue="(?P<r>\S+)"|).*?challenge-form" action="(?P<challengeUUID>\S+)".*?' r'(name="r"\svalue="(?P<r>\S+)"|).*?challenge-form" action="(?P<challengeUUID>\S+)".*?'
@@ -330,7 +362,7 @@ class CloudScraper(Session):
).groupdict() ).groupdict()
except (AttributeError): except (AttributeError):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError( raise cloudflare_exceptions.Cloudflare_Error_reCaptcha(
"Cloudflare reCaptcha detected, unfortunately we can't extract the parameters correctly." "Cloudflare reCaptcha detected, unfortunately we can't extract the parameters correctly."
) )
@@ -339,7 +371,7 @@ class CloudScraper(Session):
'url': '{}://{}{}'.format( 'url': '{}://{}{}'.format(
hostParsed.scheme, hostParsed.scheme,
hostParsed.netloc, hostParsed.netloc,
payload.get('challengeUUID', '') self.unescape(payload.get('challengeUUID', ''))
), ),
'data': OrderedDict([ 'data': OrderedDict([
('r', payload.get('r', '')), ('r', payload.get('r', '')),
@@ -377,7 +409,7 @@ class CloudScraper(Session):
if not self.recaptcha or not isinstance(self.recaptcha, dict) or not self.recaptcha.get('provider'): if not self.recaptcha or not isinstance(self.recaptcha, dict) or not self.recaptcha.get('provider'):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError( raise cloudflare_exceptions.Cloudflare_reCaptcha_Provider(
"Cloudflare reCaptcha detected, unfortunately you haven't loaded an anti reCaptcha provider " "Cloudflare reCaptcha detected, unfortunately you haven't loaded an anti reCaptcha provider "
"correctly via the 'recaptcha' parameter." "correctly via the 'recaptcha' parameter."
) )
@@ -413,7 +445,7 @@ class CloudScraper(Session):
self.delay = delay self.delay = delay
except (AttributeError, ValueError): except (AttributeError, ValueError):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError("Cloudflare IUAM possibility malformed, issue extracing delay value.") raise cloudflare_exceptions.Cloudflare_Error_IUAM("Cloudflare IUAM possibility malformed, issue extracing delay value.")
sleep(self.delay) sleep(self.delay)
@@ -473,34 +505,25 @@ class CloudScraper(Session):
return challengeSubmitResponse return challengeSubmitResponse
else: else:
cloudflare_kwargs = deepcopy(kwargs) cloudflare_kwargs = deepcopy(kwargs)
cloudflare_kwargs['headers'] = updateAttr(
cloudflare_kwargs,
'headers',
{'Referer': challengeSubmitResponse.url}
)
if not urlparse(challengeSubmitResponse.headers['Location']).netloc: if not urlparse(challengeSubmitResponse.headers['Location']).netloc:
cloudflare_kwargs['headers'] = updateAttr( redirect_location = urljoin(
cloudflare_kwargs, challengeSubmitResponse.url,
'headers', challengeSubmitResponse.headers['Location']
{'Referer': '{}://{}'.format(urlParsed.scheme, urlParsed.netloc)}
)
return self.request(
resp.request.method,
'{}://{}{}'.format(
urlParsed.scheme,
urlParsed.netloc,
challengeSubmitResponse.headers['Location']
),
**cloudflare_kwargs
) )
else: else:
redirectParsed = urlparse(challengeSubmitResponse.headers['Location']) redirect_location = challengeSubmitResponse.headers['Location']
cloudflare_kwargs['headers'] = updateAttr(
cloudflare_kwargs, return self.request(
'headers', resp.request.method,
{'Referer': '{}://{}'.format(redirectParsed.scheme, redirectParsed.netloc)} redirect_location,
) **cloudflare_kwargs
return self.request( )
resp.request.method,
challengeSubmitResponse.headers['Location'],
**cloudflare_kwargs
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
# We shouldn't be here... # We shouldn't be here...
@@ -561,7 +584,7 @@ class CloudScraper(Session):
break break
else: else:
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError( raise cloudflare_exceptions.Cloudflare_Error_IUAM(
"Unable to find Cloudflare cookies. Does the site actually " "Unable to find Cloudflare cookies. Does the site actually "
"have Cloudflare IUAM (I'm Under Attack Mode) enabled?" "have Cloudflare IUAM (I'm Under Attack Mode) enabled?"
) )
@@ -0,0 +1,31 @@
# ------------------------------------------------------------------------------- #
class Cloudflare_Loop_Protection(Exception):
"""
Raise error for recursive depth protection
"""
class Cloudflare_Block(Exception):
"""
Raise error for Cloudflare 1020 block
"""
class Cloudflare_Error_IUAM(Exception):
"""
Raise error for problem extracting IUAM paramters from Cloudflare payload
"""
class Cloudflare_Error_reCaptcha(Exception):
"""
Raise error for problem extracting reCaptcha paramters from Cloudflare payload
"""
class Cloudflare_reCaptcha_Provider(Exception):
"""
Raise error for reCaptcha from Cloudflare, no provider loaded.
"""
@@ -0,0 +1,49 @@
# ------------------------------------------------------------------------------- #
class reCaptcha_Service_Unavailable(Exception):
"""
Raise error for external services that cannot be reached
"""
class reCaptcha_Error_From_API(Exception):
"""
Raise error for error from API response.
"""
class reCaptcha_Account_Error(Exception):
"""
Raise error for reCaptcha provider account problem.
"""
class reCaptcha_Timeout(Exception):
"""
Raise error for reCaptcha provider taking too long.
"""
class reCaptcha_Bad_Parameter(NotImplementedError):
"""
Raise error for bad or missing Parameter.
"""
class reCaptcha_Bad_Job_ID(Exception):
"""
Raise error for invalid job id.
"""
class reCaptcha_Report_Error(Exception):
"""
Raise error for reCaptcha provider unable to report bad solve.
"""
class reCaptcha_Import_Error(Exception):
"""
Raise error for reCaptcha, cannot import a module.
"""
+36 -13
View File
@@ -1,13 +1,17 @@
from __future__ import absolute_import from __future__ import absolute_import
import requests import requests
import reCaptcha_exceptions
try: try:
import polling import polling
except ImportError: except ImportError:
import sys import sys
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError("Please install the python module 'polling' via pip or download it from https://github.com/justiniso/polling/") raise reCaptcha_exceptions.reCaptcha_Import_Error(
"Please install the python module 'polling' via pip or download it from "
"https://github.com/justiniso/polling/"
)
from . import reCaptcha from . import reCaptcha
@@ -24,7 +28,7 @@ class captchaSolver(reCaptcha):
@staticmethod @staticmethod
def checkErrorStatus(response, request_type): def checkErrorStatus(response, request_type):
if response.status_code in [500, 502]: if response.status_code in [500, 502]:
raise RuntimeError('2Captcha: Server Side Error {}'.format(response.status_code)) raise reCaptcha_exceptions.reCaptcha_Service_Unavailable('2Captcha: Server Side Error {}'.format(response.status_code))
errors = { errors = {
'in.php': { 'in.php': {
@@ -71,16 +75,23 @@ class captchaSolver(reCaptcha):
} }
if response.json().get('status') is False and response.json().get('request') in errors.get(request_type): if response.json().get('status') is False and response.json().get('request') in errors.get(request_type):
raise RuntimeError('{} {}'.format(response.json().get('request'), errors.get(request_type).get(response.json().get('request')))) raise reCaptcha_exceptions.reCaptcha_Error_From_API(
'{} {}'.format(
response.json().get('request'),
errors.get(request_type).get(response.json().get('request'))
)
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def reportJob(self, jobID): def reportJob(self, jobID):
if not jobID: if not jobID:
raise RuntimeError("2Captcha: Error bad job id to request reCaptcha.") raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID(
"2Captcha: Error bad job id to request reCaptcha."
)
def _checkRequest(response): def _checkRequest(response):
if response.status_code in [200, 303] and response.json().get('status') == 1: if response.ok and response.json().get('status') == 1:
return response return response
self.checkErrorStatus(response, 'res.php') self.checkErrorStatus(response, 'res.php')
@@ -105,7 +116,9 @@ class captchaSolver(reCaptcha):
if response: if response:
return True return True
else: else:
raise RuntimeError("2Captcha: Error - Failed to report bad reCaptcha solve.") raise reCaptcha_exceptions.reCaptcha_Report_Error(
"2Captcha: Error - Failed to report bad reCaptcha solve."
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@@ -114,7 +127,7 @@ class captchaSolver(reCaptcha):
raise RuntimeError("2Captcha: Error bad job id to request reCaptcha.") raise RuntimeError("2Captcha: Error bad job id to request reCaptcha.")
def _checkRequest(response): def _checkRequest(response):
if response.status_code in [200, 303] and response.json().get('status') == 1: if response.ok and response.json().get('status') == 1:
return response return response
self.checkErrorStatus(response, 'res.php') self.checkErrorStatus(response, 'res.php')
@@ -139,13 +152,15 @@ class captchaSolver(reCaptcha):
if response: if response:
return response.json().get('request') return response.json().get('request')
else: else:
raise RuntimeError("2Captcha: Error failed to solve reCaptcha.") raise reCaptcha_exceptions.reCaptcha_Timeout(
"2Captcha: Error failed to solve reCaptcha."
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def requestSolve(self, site_url, site_key): def requestSolve(self, site_url, site_key):
def _checkRequest(response): def _checkRequest(response):
if response.status_code in [200, 303] and response.json().get("status") == 1 and response.json().get('request'): if response.ok and response.json().get("status") == 1 and response.json().get('request'):
return response return response
self.checkErrorStatus(response, 'in.php') self.checkErrorStatus(response, 'in.php')
@@ -173,7 +188,9 @@ class captchaSolver(reCaptcha):
if response: if response:
return response.json().get('request') return response.json().get('request')
else: else:
raise RuntimeError('2Captcha: Error no job id was returned.') raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID(
'2Captcha: Error no job id was returned.'
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@@ -181,7 +198,9 @@ class captchaSolver(reCaptcha):
jobID = None jobID = None
if not reCaptchaParams.get('api_key'): if not reCaptchaParams.get('api_key'):
raise ValueError("2Captcha: Missing api_key parameter.") raise reCaptcha_exceptions.reCaptcha_Bad_Parameter(
"2Captcha: Missing api_key parameter."
)
self.api_key = reCaptchaParams.get('api_key') self.api_key = reCaptchaParams.get('api_key')
@@ -196,9 +215,13 @@ class captchaSolver(reCaptcha):
if jobID: if jobID:
self.reportJob(jobID) self.reportJob(jobID)
except polling.TimeoutException: except polling.TimeoutException:
raise RuntimeError("2Captcha: reCaptcha solve took to long and also failed reporting the job.") raise reCaptcha_exceptions.reCaptcha_Timeout(
"2Captcha: reCaptcha solve took to long and also failed reporting the job the job id {}.".format(jobID)
)
raise RuntimeError("2Captcha: reCaptcha solve took to long to execute, aborting.") raise reCaptcha_exceptions.reCaptcha_Timeout(
"2Captcha: reCaptcha solve took to long to execute job id {}, aborting.".format(jobID)
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
+202
View File
@@ -0,0 +1,202 @@
from __future__ import absolute_import
import re
import requests
import reCaptcha_exceptions
try:
import polling
except ImportError:
import sys
sys.tracebacklimit = 0
raise reCaptcha_exceptions.reCaptcha_Import_Error(
"Please install the python module 'polling' via pip or download it from "
"https://github.com/justiniso/polling/"
)
from . import reCaptcha
class captchaSolver(reCaptcha):
def __init__(self):
super(captchaSolver, self).__init__('9kw')
self.host = 'https://www.9kw.eu/index.cgi'
self.maxtimeout = 180
self.session = requests.Session()
# ------------------------------------------------------------------------------- #
@staticmethod
def checkErrorStatus(response):
if response.status_code in [500, 502]:
raise reCaptcha_exceptions.reCaptcha_Service_Unavailable(
'9kw: Server Side Error {}'.format(response.status_code)
)
error_codes = {
1: 'No API Key available.',
2: 'No API key found.',
3: 'No active API key found.',
4: 'API Key has been disabled by the operator. ',
5: 'No user found.',
6: 'No data found.',
7: 'Found No ID.',
8: 'found No captcha.',
9: 'No image found.',
10: 'Image size not allowed.',
11: 'credit is not sufficient.',
12: 'what was done.',
13: 'No answer contain.',
14: 'Captcha already been answered.',
15: 'Captcha to quickly filed.',
16: 'JD check active.',
17: 'Unknown problem.',
18: 'Found No ID.',
19: 'Incorrect answer.',
20: 'Do not timely filed (Incorrect UserID).',
21: 'Link not allowed.',
22: 'Prohibited submit.',
23: 'Entering prohibited.',
24: 'Too little credit.',
25: 'No entry found.',
26: 'No Conditions accepted.',
27: 'No coupon code found in the database.',
28: 'Already unused voucher code.',
29: 'maxTimeout under 60 seconds.',
30: 'User not found.',
31: 'An account is not yet 24 hours in system.',
32: 'An account does not have the full rights.',
33: 'Plugin needed a update.',
34: 'No HTTPS allowed.',
35: 'No HTTP allowed.',
36: 'Source not allowed.',
37: 'Transfer denied.',
38: 'Incorrect answer without space',
39: 'Incorrect answer with space',
40: 'Incorrect answer with not only numbers',
41: 'Incorrect answer with not only A-Z, a-z',
42: 'Incorrect answer with not only 0-9, A-Z, a-z',
43: 'Incorrect answer with not only [0-9,- ]',
44: 'Incorrect answer with not only [0-9A-Za-z,- ]',
45: 'Incorrect answer with not only coordinates',
46: 'Incorrect answer with not only multiple coordinates',
47: 'Incorrect answer with not only data',
48: 'Incorrect answer with not only rotate number',
49: 'Incorrect answer with not only text',
50: 'Incorrect answer with not only text and too short',
51: 'Incorrect answer with not enough chars',
52: 'Incorrect answer with too many chars',
53: 'Incorrect answer without no or yes',
54: 'Assignment was not found.'
}
if response.text.startswith('{'):
if response.json().get('error'):
raise reCaptcha_exceptions.reCaptcha_Error_From_API(error_codes.get(int(response.json().get('error'))))
else:
error_code = int(re.search(r'^00(?P<error_code>\d+)', response.text).groupdict().get('error_code', 0))
if error_code:
raise reCaptcha_exceptions.reCaptcha_Error_From_API(error_codes.get(error_code))
# ------------------------------------------------------------------------------- #
def requestJob(self, jobID):
if not jobID:
raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID(
"9kw: Error bad job id to request reCaptcha against."
)
def _checkRequest(response):
if response.ok and response.json().get('answer') != 'NO DATA':
return response
self.checkErrorStatus(response)
return None
response = polling.poll(
lambda: self.session.get(
self.host,
params={
'apikey': self.api_key,
'action': 'usercaptchacorrectdata',
'id': jobID,
'info': 1,
'json': 1
}
),
check_success=_checkRequest,
step=10,
timeout=(self.maxtimeout + 10)
)
if response:
return response.json().get('answer')
else:
raise reCaptcha_exceptions.reCaptcha_Timeout("9kw: Error failed to solve reCaptcha.")
# ------------------------------------------------------------------------------- #
def requestSolve(self, site_url, site_key):
def _checkRequest(response):
if response.ok and response.text.startswith('{') and response.json().get('captchaid'):
return response
self.checkErrorStatus(response)
return None
response = polling.poll(
lambda: self.session.post(
self.host,
data={
'apikey': self.api_key,
'action': 'usercaptchaupload',
'interactive': 1,
'file-upload-01': site_key,
'oldsource': 'recaptchav2',
'pageurl': site_url,
'maxtimeout': self.maxtimeout,
'json': 1
},
allow_redirects=False
),
check_success=_checkRequest,
step=5,
timeout=(self.maxtimeout + 10)
)
if response:
return response.json().get('captchaid')
else:
raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID('9kw: Error no valid job id was returned.')
# ------------------------------------------------------------------------------- #
def getCaptchaAnswer(self, site_url, site_key, reCaptchaParams):
jobID = None
if not reCaptchaParams.get('api_key'):
raise reCaptcha_exceptions.reCaptcha_Bad_Parameter("9kw: Missing api_key parameter.")
self.api_key = reCaptchaParams.get('api_key')
if reCaptchaParams.get('maxtimeout'):
self.maxtimeout = reCaptchaParams.get('maxtimeout')
if reCaptchaParams.get('proxy'):
self.session.proxies = reCaptchaParams.get('proxies')
try:
jobID = self.requestSolve(site_url, site_key)
return self.requestJob(jobID)
except polling.TimeoutException:
raise reCaptcha_exceptions.reCaptcha_Timeout(
"9kw: reCaptcha solve took to long to execute 'captchaid' {}, aborting.".format(jobID)
)
# ------------------------------------------------------------------------------- #
captchaSolver()
+13 -3
View File
@@ -1,12 +1,16 @@
from __future__ import absolute_import from __future__ import absolute_import
import sys import sys
import reCaptcha_exceptions
try: try:
from python_anticaptcha import AnticaptchaClient, NoCaptchaTaskProxylessTask from python_anticaptcha import AnticaptchaClient, NoCaptchaTaskProxylessTask
except ImportError: except ImportError:
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError("Please install the python module 'python_anticaptcha' via pip or download it from https://github.com/ad-m/python-anticaptcha") raise reCaptcha_exceptions.reCaptcha_Import_Error(
"Please install the python module 'python_anticaptcha' via pip or download it from "
"https://github.com/ad-m/python-anticaptcha"
)
from . import reCaptcha from . import reCaptcha
@@ -16,9 +20,11 @@ class captchaSolver(reCaptcha):
def __init__(self): def __init__(self):
super(captchaSolver, self).__init__('anticaptcha') super(captchaSolver, self).__init__('anticaptcha')
# ------------------------------------------------------------------------------- #
def getCaptchaAnswer(self, site_url, site_key, reCaptchaParams): def getCaptchaAnswer(self, site_url, site_key, reCaptchaParams):
if not reCaptchaParams.get('api_key'): if not reCaptchaParams.get('api_key'):
raise ValueError("reCaptcha provider 'anticaptcha' was not provided an 'api_key' parameter.") raise reCaptcha_exceptions.reCaptcha_Bad_Parameter("anticaptcha: Missing api_key parameter.")
client = AnticaptchaClient(reCaptchaParams.get('api_key')) client = AnticaptchaClient(reCaptchaParams.get('api_key'))
@@ -29,10 +35,14 @@ class captchaSolver(reCaptcha):
if not hasattr(client, 'createTaskSmee'): if not hasattr(client, 'createTaskSmee'):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError("Please upgrade 'python_anticaptcha' via pip or download it from https://github.com/ad-m/python-anticaptcha") raise reCaptcha_exceptions.reCaptcha_Import_Error(
"Please upgrade 'python_anticaptcha' via pip or download it from https://github.com/ad-m/python-anticaptcha"
)
job = client.createTaskSmee(task) job = client.createTaskSmee(task)
return job.get_solution_response() return job.get_solution_response()
# ------------------------------------------------------------------------------- #
captchaSolver() captchaSolver()
+41 -21
View File
@@ -2,13 +2,17 @@ from __future__ import absolute_import
import json import json
import requests import requests
import reCaptcha_exceptions
try: try:
import polling import polling
except ImportError: except ImportError:
import sys import sys
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError("Please install the python module 'polling' via pip or download it from https://github.com/justiniso/polling/") raise reCaptcha_exceptions.reCaptcha_Import_Error(
"Please install the python module 'polling' via pip or download it from "
"https://github.com/justiniso/polling/"
)
from . import reCaptcha from . import reCaptcha
@@ -20,7 +24,7 @@ class captchaSolver(reCaptcha):
self.host = 'http://api.dbcapi.me/api' self.host = 'http://api.dbcapi.me/api'
self.session = requests.Session() self.session = requests.Session()
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
@staticmethod @staticmethod
def checkErrorStatus(response): def checkErrorStatus(response):
@@ -34,21 +38,21 @@ class captchaSolver(reCaptcha):
) )
if response.status_code in errors: if response.status_code in errors:
raise RuntimeError(errors.get(response.status_code)) raise reCaptcha_exceptions.reCaptcha_Service_Unavailable(errors.get(response.status_code))
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def login(self, username, password): def login(self, username, password):
self.username = username self.username = username
self.password = password self.password = password
def _checkRequest(response): def _checkRequest(response):
if response.status_code == 200: if response.ok:
if response.json().get('is_banned'): if response.json().get('is_banned'):
raise RuntimeError('DeathByCaptcha: Your account is banned.') raise reCaptcha_exceptions.reCaptcha_Account_Error('DeathByCaptcha: Your account is banned.')
if response.json().get('balanace') == 0: if response.json().get('balanace') == 0:
raise RuntimeError('DeathByCaptcha: insufficient credits.') raise reCaptcha_exceptions.reCaptcha_Account_Error('DeathByCaptcha: insufficient credits.')
return response return response
@@ -72,11 +76,13 @@ class captchaSolver(reCaptcha):
self.debugRequest(response) self.debugRequest(response)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def reportJob(self, jobID): def reportJob(self, jobID):
if not jobID: if not jobID:
raise RuntimeError("DeathByCaptcha: Error bad job id to report failed reCaptcha.") raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID(
"DeathByCaptcha: Error bad job id to report failed reCaptcha."
)
def _checkRequest(response): def _checkRequest(response):
if response.status_code == 200: if response.status_code == 200:
@@ -103,16 +109,20 @@ class captchaSolver(reCaptcha):
if response: if response:
return True return True
else: else:
raise RuntimeError("DeathByCaptcha: Error report failed reCaptcha.") raise reCaptcha_exceptions.reCaptcha_Report_Error(
"DeathByCaptcha: Error report failed reCaptcha."
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def requestJob(self, jobID): def requestJob(self, jobID):
if not jobID: if not jobID:
raise RuntimeError("DeathByCaptcha: Error bad job id to request reCaptcha.") raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID(
"DeathByCaptcha: Error bad job id to request reCaptcha."
)
def _checkRequest(response): def _checkRequest(response):
if response.status_code in [200, 303] and response.json().get('text'): if response.ok and response.json().get('text'):
return response return response
self.checkErrorStatus(response) self.checkErrorStatus(response)
@@ -132,13 +142,15 @@ class captchaSolver(reCaptcha):
if response: if response:
return response.json().get('text') return response.json().get('text')
else: else:
raise RuntimeError("DeathByCaptcha: Error failed to solve reCaptcha.") raise reCaptcha_exceptions.reCaptcha_Timeout(
"DeathByCaptcha: Error failed to solve reCaptcha."
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def requestSolve(self, site_url, site_key): def requestSolve(self, site_url, site_key):
def _checkRequest(response): def _checkRequest(response):
if response.status_code in [200, 303] and response.json().get("is_correct") and response.json().get('captcha'): if response.ok and response.json().get("is_correct") and response.json().get('captcha'):
return response return response
self.checkErrorStatus(response) self.checkErrorStatus(response)
@@ -168,16 +180,20 @@ class captchaSolver(reCaptcha):
if response: if response:
return response.json().get('captcha') return response.json().get('captcha')
else: else:
raise RuntimeError('DeathByCaptcha: Error no job id was returned.') raise reCaptcha_exceptions.reCaptcha_Bad_Job_ID(
'DeathByCaptcha: Error no job id was returned.'
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
def getCaptchaAnswer(self, site_url, site_key, reCaptchaParams): def getCaptchaAnswer(self, site_url, site_key, reCaptchaParams):
jobID = None jobID = None
for param in ['username', 'password']: for param in ['username', 'password']:
if not reCaptchaParams.get(param): if not reCaptchaParams.get(param):
raise ValueError("DeathByCaptcha: Missing '{}' parameter.".format(param)) raise reCaptcha_exceptions.reCaptcha_Bad_Parameter(
"DeathByCaptcha: Missing '{}' parameter.".format(param)
)
setattr(self, param, reCaptchaParams.get(param)) setattr(self, param, reCaptchaParams.get(param))
if reCaptchaParams.get('proxy'): if reCaptchaParams.get('proxy'):
@@ -191,9 +207,13 @@ class captchaSolver(reCaptcha):
if jobID: if jobID:
self.reportJob(jobID) self.reportJob(jobID)
except polling.TimeoutException: except polling.TimeoutException:
raise RuntimeError("DeathByCaptcha: reCaptcha solve took to long and also failed reporting the job.") raise reCaptcha_exceptions.reCaptcha_Timeout(
"DeathByCaptcha: reCaptcha solve took to long and also failed reporting the job id {}.".format(jobID)
)
raise RuntimeError("DeathByCaptcha: reCaptcha solve took to long to execute, aborting.") raise reCaptcha_exceptions.reCaptcha_Timeout(
"DeathByCaptcha: reCaptcha solve took to long to execute job id {}, aborting.".format(jobID)
)
# ------------------------------------------------------------------------------- # # ------------------------------------------------------------------------------- #
+6 -5
View File
@@ -47,7 +47,7 @@ class User_Agent():
for browser in user_agents: for browser in user_agents:
for release in user_agents[browser]['releases']: for release in user_agents[browser]['releases']:
for platform in ['mobile', 'desktop']: for platform in ['mobile', 'desktop']:
if re.search(self.custom, ' '.join(user_agents[browser]['releases'][release]['User-Agent'][platform])): if re.search(re.escape(self.custom), ' '.join(user_agents[browser]['releases'][release]['User-Agent'][platform])):
self.browser = browser self.browser = browser
self.loadHeaders(user_agents, release) self.loadHeaders(user_agents, release)
self.headers['User-Agent'] = self.custom self.headers['User-Agent'] = self.custom
@@ -74,10 +74,11 @@ class User_Agent():
sys.tracebacklimit = 0 sys.tracebacklimit = 0
raise RuntimeError("Sorry you can't have mobile and desktop disabled at the same time.") raise RuntimeError("Sorry you can't have mobile and desktop disabled at the same time.")
user_agents = json.load( with open(os.path.join(os.path.dirname(__file__), 'browsers.json'), 'r') as fp:
open(os.path.join(os.path.dirname(__file__), 'browsers.json'), 'r'), user_agents = json.load(
object_pairs_hook=OrderedDict fp,
) object_pairs_hook=OrderedDict
)
if self.custom: if self.custom:
if not self.tryMatchCustom(user_agents): if not self.tryMatchCustom(user_agents):
+10
View File
@@ -13,9 +13,14 @@
"TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256",
"ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256",
"ECDHE-ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-CHACHA20-POLY1305", "ECDHE-ECDSA-CHACHA20-POLY1305",
"ECDHE-RSA-CHACHA20-POLY1305",
"ECDHE-RSA-AES256-SHA",
"AES128-GCM-SHA256", "AES128-GCM-SHA256",
"AES256-GCM-SHA384", "AES256-GCM-SHA384",
"AES128-SHA",
"AES256-SHA" "AES256-SHA"
], ],
"releases": { "releases": {
@@ -12814,10 +12819,15 @@
"ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256",
"ECDHE-ECDSA-CHACHA20-POLY1305", "ECDHE-ECDSA-CHACHA20-POLY1305",
"ECDHE-RSA-CHACHA20-POLY1305",
"ECDHE-ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-AES256-SHA", "ECDHE-ECDSA-AES256-SHA",
"ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA",
"ECDHE-RSA-AES256-SHA",
"DHE-RSA-AES128-SHA", "DHE-RSA-AES128-SHA",
"DHE-RSA-AES256-SHA", "DHE-RSA-AES256-SHA",
"AES128-SHA",
"AES256-SHA" "AES256-SHA"
], ],
"releases": { "releases": {
+1
View File
@@ -28,6 +28,7 @@ from __future__ import division
from __future__ import absolute_import from __future__ import absolute_import
from future.builtins import range from future.builtins import range
from future.builtins import bytes from future.builtins import bytes
from future.builtins import str
__all__ = [ __all__ = [
'body_decode', 'body_decode',
View File
+2 -2
View File
@@ -11,9 +11,9 @@ an application may want to handle an exception like a regular
response. response.
""" """
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
from ... import standard_library from future import standard_library
from . import response as urllib_response from future.backports.urllib import response as urllib_response
__all__ = ['URLError', 'HTTPError', 'ContentTooShortError'] __all__ = ['URLError', 'HTTPError', 'ContentTooShortError']
+4 -4
View File
@@ -87,7 +87,7 @@ def clear_cache():
# decoding and encoding. If valid use cases are # decoding and encoding. If valid use cases are
# presented, we may relax this by using latin-1 # presented, we may relax this by using latin-1
# decoding internally for 3.3 # decoding internally for 3.3
_implicit_encoding = 'utf8' _implicit_encoding = 'ascii'
_implicit_errors = 'strict' _implicit_errors = 'strict'
def _noop(obj): def _noop(obj):
@@ -122,7 +122,7 @@ class _ResultMixinStr(object):
"""Standard approach to encoding parsed results from str to bytes""" """Standard approach to encoding parsed results from str to bytes"""
__slots__ = () __slots__ = ()
def encode(self, encoding='utf8', errors='strict'): def encode(self, encoding='ascii', errors='strict'):
return self._encoded_counterpart(*(x.encode(encoding, errors) for x in self)) return self._encoded_counterpart(*(x.encode(encoding, errors) for x in self))
@@ -130,7 +130,7 @@ class _ResultMixinBytes(object):
"""Standard approach to decoding parsed results from bytes to str""" """Standard approach to decoding parsed results from bytes to str"""
__slots__ = () __slots__ = ()
def decode(self, encoding='utf8', errors='strict'): def decode(self, encoding='ascii', errors='strict'):
return self._decoded_counterpart(*(x.decode(encoding, errors) for x in self)) return self._decoded_counterpart(*(x.decode(encoding, errors) for x in self))
@@ -730,7 +730,7 @@ def quote_from_bytes(bs, safe='/'):
### ###
if isinstance(safe, str): if isinstance(safe, str):
# Normalize 'safe' by converting to bytes and removing non-ASCII chars # Normalize 'safe' by converting to bytes and removing non-ASCII chars
safe = str(safe).encode('utf8', 'ignore') safe = str(safe).encode('ascii', 'ignore')
else: else:
### For Python-Future: ### For Python-Future:
safe = bytes(safe) safe = bytes(safe)
+9 -19
View File
@@ -827,7 +827,7 @@ class ProxyHandler(BaseHandler):
if user and password: if user and password:
user_pass = '%s:%s' % (unquote(user), user_pass = '%s:%s' % (unquote(user),
unquote(password)) unquote(password))
creds = base64.b64encode(user_pass.encode()).decode("utf8") creds = base64.b64encode(user_pass.encode()).decode("ascii")
req.add_header('Proxy-authorization', 'Basic ' + creds) req.add_header('Proxy-authorization', 'Basic ' + creds)
hostport = unquote(hostport) hostport = unquote(hostport)
req.set_proxy(hostport, proxy_type) req.set_proxy(hostport, proxy_type)
@@ -977,7 +977,7 @@ class AbstractBasicAuthHandler(object):
user, pw = self.passwd.find_user_password(realm, host) user, pw = self.passwd.find_user_password(realm, host)
if pw is not None: if pw is not None:
raw = "%s:%s" % (user, pw) raw = "%s:%s" % (user, pw)
auth = "Basic " + base64.b64encode(raw.encode()).decode("utf8") auth = "Basic " + base64.b64encode(raw.encode()).decode("ascii")
if req.headers.get(self.auth_header, None) == auth: if req.headers.get(self.auth_header, None) == auth:
return None return None
req.add_unredirected_header(self.auth_header, auth) req.add_unredirected_header(self.auth_header, auth)
@@ -1080,7 +1080,7 @@ class AbstractDigestAuthHandler(object):
# authentication, and to provide some message integrity protection. # authentication, and to provide some message integrity protection.
# This isn't a fabulous effort, but it's probably Good Enough. # This isn't a fabulous effort, but it's probably Good Enough.
s = "%s:%s:%s:" % (self.nonce_count, nonce, time.ctime()) s = "%s:%s:%s:" % (self.nonce_count, nonce, time.ctime())
b = s.encode("utf8") + _randombytes(8) b = s.encode("ascii") + _randombytes(8)
dig = hashlib.sha1(b).hexdigest() dig = hashlib.sha1(b).hexdigest()
return dig[:16] return dig[:16]
@@ -1147,9 +1147,9 @@ class AbstractDigestAuthHandler(object):
def get_algorithm_impls(self, algorithm): def get_algorithm_impls(self, algorithm):
# lambdas assume digest modules are imported at the top level # lambdas assume digest modules are imported at the top level
if algorithm == 'MD5': if algorithm == 'MD5':
H = lambda x: hashlib.md5(x.encode("utf8")).hexdigest() H = lambda x: hashlib.md5(x.encode("ascii")).hexdigest()
elif algorithm == 'SHA': elif algorithm == 'SHA':
H = lambda x: hashlib.sha1(x.encode("utf8")).hexdigest() H = lambda x: hashlib.sha1(x.encode("ascii")).hexdigest()
# XXX MD5-sess # XXX MD5-sess
KD = lambda s, d: H("%s:%s" % (s, d)) KD = lambda s, d: H("%s:%s" % (s, d))
return H, KD return H, KD
@@ -1829,13 +1829,13 @@ class URLopener(object):
if proxy_passwd: if proxy_passwd:
proxy_passwd = unquote(proxy_passwd) proxy_passwd = unquote(proxy_passwd)
proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('utf8') proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii')
else: else:
proxy_auth = None proxy_auth = None
if user_passwd: if user_passwd:
user_passwd = unquote(user_passwd) user_passwd = unquote(user_passwd)
auth = base64.b64encode(user_passwd.encode()).decode('utf8') auth = base64.b64encode(user_passwd.encode()).decode('ascii')
else: else:
auth = None auth = None
http_conn = connection_factory(host) http_conn = connection_factory(host)
@@ -2040,7 +2040,7 @@ class URLopener(object):
msg.append('Content-type: %s' % type) msg.append('Content-type: %s' % type)
if encoding == 'base64': if encoding == 'base64':
# XXX is this encoding/decoding ok? # XXX is this encoding/decoding ok?
data = base64.decodebytes(data.encode('utf8')).decode('latin-1') data = base64.decodebytes(data.encode('ascii')).decode('latin-1')
else: else:
data = unquote(data) data = unquote(data)
msg.append('Content-Length: %d' % len(data)) msg.append('Content-Length: %d' % len(data))
@@ -2498,17 +2498,7 @@ def _proxy_bypass_macosx_sysconf(host, proxy_settings):
if sys.platform == 'darwin': if sys.platform == 'darwin':
try: from _scproxy import _get_proxy_settings, _get_proxies
from _scproxy import _get_proxy_settings, _get_proxies
except:
try:
# By default use environment variables
_get_proxy_settings = getproxies_environment
_get_proxies = proxy_bypass_environment
getproxies = getproxies_environment
proxy_bypass = proxy_bypass_environment
except:
pass
def proxy_bypass_macosx_sysconf(host): def proxy_bypass_macosx_sysconf(host):
proxy_settings = _get_proxy_settings() proxy_settings = _get_proxy_settings()
+3 -3
View File
@@ -1,5 +1,5 @@
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
from ...builtins import str from future.builtins import str
""" robotparser.py """ robotparser.py
Copyright (C) 2000 Bastian Kleineidam Copyright (C) 2000 Bastian Kleineidam
@@ -13,8 +13,8 @@ from ...builtins import str
""" """
# Was: import urllib.parse, urllib.request # Was: import urllib.parse, urllib.request
from .. import urllib from future.backports import urllib
from . import parse as _parse, request as _request from future.backports.urllib import parse as _parse, request as _request
urllib.parse = _parse urllib.parse = _parse
urllib.request = _request urllib.request = _request
+10 -7
View File
@@ -2,6 +2,7 @@
``python-future``: pure Python implementation of Python 3 round(). ``python-future``: pure Python implementation of Python 3 round().
""" """
from __future__ import division
from future.utils import PYPY, PY26, bind_method from future.utils import PYPY, PY26, bind_method
# Use the decimal module for simplicity of implementation (and # Use the decimal module for simplicity of implementation (and
@@ -29,8 +30,6 @@ def newround(number, ndigits=None):
if hasattr(number, '__round__'): if hasattr(number, '__round__'):
return number.__round__(ndigits) return number.__round__(ndigits)
if ndigits < 0:
raise NotImplementedError('negative ndigits not supported yet')
exponent = Decimal('10') ** (-ndigits) exponent = Decimal('10') ** (-ndigits)
if PYPY: if PYPY:
@@ -42,15 +41,19 @@ def newround(number, ndigits=None):
d = number d = number
else: else:
if not PY26: if not PY26:
d = Decimal.from_float(number).quantize(exponent, d = Decimal.from_float(number)
rounding=ROUND_HALF_EVEN)
else: else:
d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN) d = from_float_26(number)
if ndigits < 0:
result = newround(d / exponent) * exponent
else:
result = d.quantize(exponent, rounding=ROUND_HALF_EVEN)
if return_int: if return_int:
return int(d) return int(result)
else: else:
return float(d) return float(result)
### From Python 2.7's decimal.py. Only needed to support Py2.6: ### From Python 2.7's decimal.py. Only needed to support Py2.6:
+6
View File
@@ -10,3 +10,9 @@ else:
except ImportError: except ImportError:
raise ImportError('The FileDialog module is missing. Does your Py2 ' raise ImportError('The FileDialog module is missing. Does your Py2 '
'installation include tkinter?') 'installation include tkinter?')
try:
from tkFileDialog import *
except ImportError:
raise ImportError('The tkFileDialog module is missing. Does your Py2 '
'installation include tkinter?')
+43 -77
View File
@@ -450,63 +450,35 @@ def install_aliases():
# if hasattr(install_aliases, 'run_already'): # if hasattr(install_aliases, 'run_already'):
# return # return
for (newmodname, newobjname, oldmodname, oldobjname) in MOVES: for (newmodname, newobjname, oldmodname, oldobjname) in MOVES:
try: __import__(newmodname)
__import__(newmodname) # We look up the module in sys.modules because __import__ just returns the
# We look up the module in sys.modules because __import__ just returns the # top-level package:
# top-level package: newmod = sys.modules[newmodname]
newmod = sys.modules[newmodname] # newmod.__future_module__ = True
# newmod.__future_module__ = True
__import__(oldmodname) __import__(oldmodname)
oldmod = sys.modules[oldmodname] oldmod = sys.modules[oldmodname]
obj = getattr(oldmod, oldobjname) obj = getattr(oldmod, oldobjname)
setattr(newmod, newobjname, obj) setattr(newmod, newobjname, obj)
except:
try:
flog.warning('*** FUTURE ERROR in module %s %s ' % (str(oldmod), str(oldobjname)))
except:
pass
# Hack for urllib so it appears to have the same structure on Py2 as on Py3 # Hack for urllib so it appears to have the same structure on Py2 as on Py3
try: import urllib
import urllib from future.backports.urllib import request
from future.backports.urllib import response from future.backports.urllib import response
urllib.response = response from future.backports.urllib import parse
sys.modules['urllib.response'] = response from future.backports.urllib import error
from future.backports.urllib import parse from future.backports.urllib import robotparser
urllib.parse = parse urllib.request = request
sys.modules['urllib.parse'] = parse urllib.response = response
from future.backports.urllib import error urllib.parse = parse
urllib.error = error urllib.error = error
sys.modules['urllib.error'] = error urllib.robotparser = robotparser
except ImportError: sys.modules['urllib.request'] = request
try: sys.modules['urllib.response'] = response
flog.warning('*** FUTURE ERROR importing URLLIB.response, parse, error') sys.modules['urllib.parse'] = parse
urllib.response = urllib sys.modules['urllib.error'] = error
sys.modules['urllib.response'] = urllib sys.modules['urllib.robotparser'] = robotparser
urllib.parse = urllib
sys.modules['urllib.parse'] = urllib
urllib.error = urllib
sys.modules['urllib.error'] = urllib
except:
pass
try:
from future.backports.urllib import request
urllib.request = request
sys.modules['urllib.request'] = request
from future.backports.urllib import robotparser
urllib.robotparser = robotparser
sys.modules['urllib.robotparser'] = robotparser
except ImportError:
try:
flog.warning('*** FUTURE ERROR importing URLLIB.Request')
urllib.request = urllib
sys.modules['urllib.request'] = urllib
urllib.robotparser = urllib
sys.modules['urllib.robotparser'] = urllib
except:
pass
# Patch the test module so it appears to have the same structure on Py2 as on Py3 # Patch the test module so it appears to have the same structure on Py2 as on Py3
try: try:
@@ -518,11 +490,8 @@ def install_aliases():
except ImportError: except ImportError:
pass pass
else: else:
try: test.support = support
test.support = support sys.modules['test.support'] = support
sys.modules['test.support'] = support
except:
pass
# Patch the dbm module so it appears to have the same structure on Py2 as on Py3 # Patch the dbm module so it appears to have the same structure on Py2 as on Py3
try: try:
@@ -530,26 +499,23 @@ def install_aliases():
except ImportError: except ImportError:
pass pass
else: else:
from future.moves.dbm import dumb
dbm.dumb = dumb
sys.modules['dbm.dumb'] = dumb
try: try:
from future.moves.dbm import dumb from future.moves.dbm import gnu
dbm.dumb = dumb except ImportError:
sys.modules['dbm.dumb'] = dumb pass
try: else:
from future.moves.dbm import gnu dbm.gnu = gnu
except ImportError: sys.modules['dbm.gnu'] = gnu
pass try:
else: from future.moves.dbm import ndbm
dbm.gnu = gnu except ImportError:
sys.modules['dbm.gnu'] = gnu pass
try: else:
from future.moves.dbm import ndbm dbm.ndbm = ndbm
except ImportError: sys.modules['dbm.ndbm'] = ndbm
pass
else:
dbm.ndbm = ndbm
sys.modules['dbm.ndbm'] = ndbm
except:
flog.warning('*** FUTURE ERROR importing MOVES.dbm')
# install_aliases.run_already = True # install_aliases.run_already = True
+2 -2
View File
@@ -527,9 +527,9 @@ def implements_iterator(cls):
return cls return cls
if PY3: if PY3:
get_next = lambda x: x.next
else:
get_next = lambda x: x.__next__ get_next = lambda x: x.__next__
else:
get_next = lambda x: x.next
def encode_filename(filename): def encode_filename(filename):
+6 -2
View File
@@ -11,7 +11,10 @@ import re
import os import os
import sys import sys
import urllib import urllib
import urlparse try:
import urlparse
except:
import urllib.parse as urlparse
import datetime import datetime
import time import time
import traceback import traceback
@@ -161,7 +164,8 @@ def update_title(item):
item.channel = new_item.channel #Restuaramos el nombre del canal, por si lo habíamos cambiado item.channel = new_item.channel #Restuaramos el nombre del canal, por si lo habíamos cambiado
if item.tmdb_stat == True: if item.tmdb_stat == True:
if new_item.contentSerieName: #Si es serie... if new_item.contentSerieName: #Si es serie...
if config.get_setting("filter_languages", item.channel) >= 0: filter_languages = config.get_setting("filter_languages", item.channel)
if filter_languages and filter_languages >= 0:
item.title_from_channel = new_item.contentSerieName #Guardo el título incial para Filtertools item.title_from_channel = new_item.contentSerieName #Guardo el título incial para Filtertools
item.contentSerieName = new_item.contentSerieName #Guardo el título incial para Filtertools item.contentSerieName = new_item.contentSerieName #Guardo el título incial para Filtertools
else: else:
-1
View File
@@ -9,7 +9,6 @@ has been tested with Python2.7 and Python 3.4.
from __future__ import print_function from __future__ import print_function
import argparse
import os import os
import stat import stat
import sys import sys
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -238,7 +238,15 @@ class socksocket(socket.socket):
headers - Additional or modified headers for the proxy connect headers - Additional or modified headers for the proxy connect
request. request.
""" """
self.__proxy = (proxytype, addr, port, rdns, username, password, headers) self.__proxy = (
proxytype,
addr,
port,
rdns,
username.encode() if username else None,
password.encode() if password else None,
headers,
)
def __negotiatesocks5(self, destaddr, destport): def __negotiatesocks5(self, destaddr, destport):
"""__negotiatesocks5(self,destaddr,destport) """__negotiatesocks5(self,destaddr,destport)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
"""Utilities for certificate management."""
import os
certifi_available = False
certifi_where = None
try:
from certifi import where as certifi_where
certifi_available = True
except ImportError:
pass
custom_ca_locater_available = False
custom_ca_locater_where = None
try:
from ca_certs_locater import get as custom_ca_locater_where
custom_ca_locater_available = True
except ImportError:
pass
BUILTIN_CA_CERTS = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "cacerts.txt"
)
def where():
env = os.environ.get("HTTPLIB2_CA_CERTS")
if env is not None:
if os.path.isfile(env):
return env
else:
raise RuntimeError("Environment variable HTTPLIB2_CA_CERTS not a valid file")
if custom_ca_locater_available:
return custom_ca_locater_where()
if certifi_available:
return certifi_where()
return BUILTIN_CA_CERTS
if __name__ == "__main__":
print(where())
+124
View File
@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
"""Converts an IRI to a URI."""
__author__ = "Joe Gregorio (joe@bitworking.org)"
__copyright__ = "Copyright 2006, Joe Gregorio"
__contributors__ = []
__version__ = "1.0.0"
__license__ = "MIT"
import urllib.parse
# Convert an IRI to a URI following the rules in RFC 3987
#
# The characters we need to enocde and escape are defined in the spec:
#
# iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
# ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
# / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
# / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
# / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
# / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
# / %xD0000-DFFFD / %xE1000-EFFFD
escape_range = [
(0xA0, 0xD7FF),
(0xE000, 0xF8FF),
(0xF900, 0xFDCF),
(0xFDF0, 0xFFEF),
(0x10000, 0x1FFFD),
(0x20000, 0x2FFFD),
(0x30000, 0x3FFFD),
(0x40000, 0x4FFFD),
(0x50000, 0x5FFFD),
(0x60000, 0x6FFFD),
(0x70000, 0x7FFFD),
(0x80000, 0x8FFFD),
(0x90000, 0x9FFFD),
(0xA0000, 0xAFFFD),
(0xB0000, 0xBFFFD),
(0xC0000, 0xCFFFD),
(0xD0000, 0xDFFFD),
(0xE1000, 0xEFFFD),
(0xF0000, 0xFFFFD),
(0x100000, 0x10FFFD),
]
def encode(c):
retval = c
i = ord(c)
for low, high in escape_range:
if i < low:
break
if i >= low and i <= high:
retval = "".join(["%%%2X" % o for o in c.encode("utf-8")])
break
return retval
def iri2uri(uri):
"""Convert an IRI to a URI. Note that IRIs must be
passed in a unicode strings. That is, do not utf-8 encode
the IRI before passing it into the function."""
if isinstance(uri, str):
(scheme, authority, path, query, fragment) = urllib.parse.urlsplit(uri)
authority = authority.encode("idna").decode("utf-8")
# For each character in 'ucschar' or 'iprivate'
# 1. encode as utf-8
# 2. then %-encode each octet of that utf-8
uri = urllib.parse.urlunsplit((scheme, authority, path, query, fragment))
uri = "".join([encode(c) for c in uri])
return uri
if __name__ == "__main__":
import unittest
class Test(unittest.TestCase):
def test_uris(self):
"""Test that URIs are invariant under the transformation."""
invariant = [
"ftp://ftp.is.co.za/rfc/rfc1808.txt",
"http://www.ietf.org/rfc/rfc2396.txt",
"ldap://[2001:db8::7]/c=GB?objectClass?one",
"mailto:John.Doe@example.com",
"news:comp.infosystems.www.servers.unix",
"tel:+1-816-555-1212",
"telnet://192.0.2.16:80/",
"urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
]
for uri in invariant:
self.assertEqual(uri, iri2uri(uri))
def test_iri(self):
"""Test that the right type of escaping is done for each part of the URI."""
self.assertEqual(
"http://xn--o3h.com/%E2%98%84",
iri2uri("http://\N{COMET}.com/\N{COMET}"),
)
self.assertEqual(
"http://bitworking.org/?fred=%E2%98%84",
iri2uri("http://bitworking.org/?fred=\N{COMET}"),
)
self.assertEqual(
"http://bitworking.org/#%E2%98%84",
iri2uri("http://bitworking.org/#\N{COMET}"),
)
self.assertEqual("#%E2%98%84", iri2uri("#\N{COMET}"))
self.assertEqual(
"/fred?bar=%E2%98%9A#%E2%98%84",
iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"),
)
self.assertEqual(
"/fred?bar=%E2%98%9A#%E2%98%84",
iri2uri(iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")),
)
self.assertNotEqual(
"/fred?bar=%E2%98%9A#%E2%98%84",
iri2uri(
"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode("utf-8")
),
)
unittest.main()
+518
View File
@@ -0,0 +1,518 @@
"""SocksiPy - Python SOCKS module.
Version 1.00
Copyright 2006 Dan-Haim. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of Dan Haim nor the names of his contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies.
Minor modifications made by Christopher Gilbert (http://motomastyle.com/) for
use in PyLoris (http://pyloris.sourceforge.net/).
Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
mainly to merge bug fixes found in Sourceforge.
"""
import base64
import socket
import struct
import sys
if getattr(socket, "socket", None) is None:
raise ImportError("socket.socket missing, proxy support unusable")
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
PROXY_TYPE_HTTP = 3
PROXY_TYPE_HTTP_NO_TUNNEL = 4
_defaultproxy = None
_orgsocket = socket.socket
class ProxyError(Exception):
pass
class GeneralProxyError(ProxyError):
pass
class Socks5AuthError(ProxyError):
pass
class Socks5Error(ProxyError):
pass
class Socks4Error(ProxyError):
pass
class HTTPError(ProxyError):
pass
_generalerrors = (
"success",
"invalid data",
"not connected",
"not available",
"bad proxy type",
"bad input",
)
_socks5errors = (
"succeeded",
"general SOCKS server failure",
"connection not allowed by ruleset",
"Network unreachable",
"Host unreachable",
"Connection refused",
"TTL expired",
"Command not supported",
"Address type not supported",
"Unknown error",
)
_socks5autherrors = (
"succeeded",
"authentication is required",
"all offered authentication methods were rejected",
"unknown username or invalid password",
"unknown error",
)
_socks4errors = (
"request granted",
"request rejected or failed",
"request rejected because SOCKS server cannot connect to identd on the client",
"request rejected because the client program and identd report different "
"user-ids",
"unknown error",
)
def setdefaultproxy(
proxytype=None, addr=None, port=None, rdns=True, username=None, password=None
):
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Sets a default proxy which all further socksocket objects will use,
unless explicitly changed.
"""
global _defaultproxy
_defaultproxy = (proxytype, addr, port, rdns, username, password)
def wrapmodule(module):
"""wrapmodule(module)
Attempts to replace a module's socket library with a SOCKS socket. Must set
a default proxy using setdefaultproxy(...) first.
This will only work on modules that import socket directly into the
namespace;
most of the Python Standard Library falls into this category.
"""
if _defaultproxy != None:
module.socket.socket = socksocket
else:
raise GeneralProxyError((4, "no proxy specified"))
class socksocket(socket.socket):
"""socksocket([family[, type[, proto]]]) -> socket object
Open a SOCKS enabled socket. The parameters are the same as
those of the standard socket init. In order for SOCKS to work,
you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
"""
def __init__(
self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None
):
_orgsocket.__init__(self, family, type, proto, _sock)
if _defaultproxy != None:
self.__proxy = _defaultproxy
else:
self.__proxy = (None, None, None, None, None, None)
self.__proxysockname = None
self.__proxypeername = None
self.__httptunnel = True
def __recvall(self, count):
"""__recvall(count) -> data
Receive EXACTLY the number of bytes requested from the socket.
Blocks until the required number of bytes have been received.
"""
data = self.recv(count)
while len(data) < count:
d = self.recv(count - len(data))
if not d:
raise GeneralProxyError((0, "connection closed unexpectedly"))
data = data + d
return data
def sendall(self, content, *args):
""" override socket.socket.sendall method to rewrite the header
for non-tunneling proxies if needed
"""
if not self.__httptunnel:
content = self.__rewriteproxy(content)
return super(socksocket, self).sendall(content, *args)
def __rewriteproxy(self, header):
""" rewrite HTTP request headers to support non-tunneling proxies
(i.e. those which do not support the CONNECT method).
This only works for HTTP (not HTTPS) since HTTPS requires tunneling.
"""
host, endpt = None, None
hdrs = header.split("\r\n")
for hdr in hdrs:
if hdr.lower().startswith("host:"):
host = hdr
elif hdr.lower().startswith("get") or hdr.lower().startswith("post"):
endpt = hdr
if host and endpt:
hdrs.remove(host)
hdrs.remove(endpt)
host = host.split(" ")[1]
endpt = endpt.split(" ")
if self.__proxy[4] != None and self.__proxy[5] != None:
hdrs.insert(0, self.__getauthheader())
hdrs.insert(0, "Host: %s" % host)
hdrs.insert(0, "%s http://%s%s %s" % (endpt[0], host, endpt[1], endpt[2]))
return "\r\n".join(hdrs)
def __getauthheader(self):
auth = self.__proxy[4] + b":" + self.__proxy[5]
return "Proxy-Authorization: Basic " + base64.b64encode(auth).decode()
def setproxy(
self,
proxytype=None,
addr=None,
port=None,
rdns=True,
username=None,
password=None,
headers=None,
):
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Sets the proxy to be used.
proxytype - The type of the proxy to be used. Three types
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
addr - The address of the server (IP or DNS).
port - The port of the server. Defaults to 1080 for SOCKS
servers and 8080 for HTTP proxy servers.
rdns - Should DNS queries be preformed on the remote side
(rather than the local side). The default is True.
Note: This has no effect with SOCKS4 servers.
username - Username to authenticate with to the server.
The default is no authentication.
password - Password to authenticate with to the server.
Only relevant when username is also provided.
headers - Additional or modified headers for the proxy connect
request.
"""
self.__proxy = (
proxytype,
addr,
port,
rdns,
username.encode() if username else None,
password.encode() if password else None,
headers,
)
def __negotiatesocks5(self, destaddr, destport):
"""__negotiatesocks5(self,destaddr,destport)
Negotiates a connection through a SOCKS5 server.
"""
# First we'll send the authentication packages we support.
if (self.__proxy[4] != None) and (self.__proxy[5] != None):
# The username/password details were supplied to the
# setproxy method so we support the USERNAME/PASSWORD
# authentication (in addition to the standard none).
self.sendall(struct.pack("BBBB", 0x05, 0x02, 0x00, 0x02))
else:
# No username/password were entered, therefore we
# only support connections with no authentication.
self.sendall(struct.pack("BBB", 0x05, 0x01, 0x00))
# We'll receive the server's response to determine which
# method was selected
chosenauth = self.__recvall(2)
if chosenauth[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
# Check the chosen authentication method
if chosenauth[1:2] == chr(0x00).encode():
# No authentication is required
pass
elif chosenauth[1:2] == chr(0x02).encode():
# Okay, we need to perform a basic username/password
# authentication.
packet = bytearray()
packet.append(0x01)
packet.append(len(self.__proxy[4]))
packet.extend(self.__proxy[4])
packet.append(len(self.__proxy[5]))
packet.extend(self.__proxy[5])
self.sendall(packet)
authstat = self.__recvall(2)
if authstat[0:1] != chr(0x01).encode():
# Bad response
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
if authstat[1:2] != chr(0x00).encode():
# Authentication failed
self.close()
raise Socks5AuthError((3, _socks5autherrors[3]))
# Authentication succeeded
else:
# Reaching here is always bad
self.close()
if chosenauth[1] == chr(0xFF).encode():
raise Socks5AuthError((2, _socks5autherrors[2]))
else:
raise GeneralProxyError((1, _generalerrors[1]))
# Now we can request the actual connection
req = struct.pack("BBB", 0x05, 0x01, 0x00)
# If the given destination address is an IP address, we'll
# use the IPv4 address request even if remote resolving was specified.
try:
ipaddr = socket.inet_aton(destaddr)
req = req + chr(0x01).encode() + ipaddr
except socket.error:
# Well it's not an IP number, so it's probably a DNS name.
if self.__proxy[3]:
# Resolve remotely
ipaddr = None
req = (
req
+ chr(0x03).encode()
+ chr(len(destaddr)).encode()
+ destaddr.encode()
)
else:
# Resolve locally
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
req = req + chr(0x01).encode() + ipaddr
req = req + struct.pack(">H", destport)
self.sendall(req)
# Get the response
resp = self.__recvall(4)
if resp[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
if ord(resp[1:2]) <= 8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
# Get the bound address/port
elif resp[3:4] == chr(0x01).encode():
boundaddr = self.__recvall(4)
elif resp[3:4] == chr(0x03).encode():
resp = resp + self.recv(1)
boundaddr = self.__recvall(ord(resp[4:5]))
else:
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
self.__proxysockname = (boundaddr, boundport)
if ipaddr != None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else:
self.__proxypeername = (destaddr, destport)
def getproxysockname(self):
"""getsockname() -> address info
Returns the bound IP address and port number at the proxy.
"""
return self.__proxysockname
def getproxypeername(self):
"""getproxypeername() -> address info
Returns the IP and port number of the proxy.
"""
return _orgsocket.getpeername(self)
def getpeername(self):
"""getpeername() -> address info
Returns the IP address and port number of the destination
machine (note: getproxypeername returns the proxy)
"""
return self.__proxypeername
def __negotiatesocks4(self, destaddr, destport):
"""__negotiatesocks4(self,destaddr,destport)
Negotiates a connection through a SOCKS4 server.
"""
# Check if the destination address provided is an IP address
rmtrslv = False
try:
ipaddr = socket.inet_aton(destaddr)
except socket.error:
# It's a DNS name. Check where it should be resolved.
if self.__proxy[3]:
ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
rmtrslv = True
else:
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
# Construct the request packet
req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
# The username parameter is considered userid for SOCKS4
if self.__proxy[4] != None:
req = req + self.__proxy[4]
req = req + chr(0x00).encode()
# DNS name if remote resolving is required
# NOTE: This is actually an extension to the SOCKS4 protocol
# called SOCKS4A and may not be supported in all cases.
if rmtrslv:
req = req + destaddr + chr(0x00).encode()
self.sendall(req)
# Get the response from the server
resp = self.__recvall(8)
if resp[0:1] != chr(0x00).encode():
# Bad data
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
if resp[1:2] != chr(0x5A).encode():
# Server returned an error
self.close()
if ord(resp[1:2]) in (91, 92, 93):
self.close()
raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
else:
raise Socks4Error((94, _socks4errors[4]))
# Get the bound address/port
self.__proxysockname = (
socket.inet_ntoa(resp[4:]),
struct.unpack(">H", resp[2:4])[0],
)
if rmtrslv != None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else:
self.__proxypeername = (destaddr, destport)
def __negotiatehttp(self, destaddr, destport):
"""__negotiatehttp(self,destaddr,destport)
Negotiates a connection through an HTTP server.
"""
# If we need to resolve locally, we do this now
if not self.__proxy[3]:
addr = socket.gethostbyname(destaddr)
else:
addr = destaddr
headers = ["CONNECT ", addr, ":", str(destport), " HTTP/1.1\r\n"]
wrote_host_header = False
wrote_auth_header = False
if self.__proxy[6] != None:
for key, val in self.__proxy[6].iteritems():
headers += [key, ": ", val, "\r\n"]
wrote_host_header = key.lower() == "host"
wrote_auth_header = key.lower() == "proxy-authorization"
if not wrote_host_header:
headers += ["Host: ", destaddr, "\r\n"]
if not wrote_auth_header:
if self.__proxy[4] != None and self.__proxy[5] != None:
headers += [self.__getauthheader(), "\r\n"]
headers.append("\r\n")
self.sendall("".join(headers).encode())
# We read the response until we get the string "\r\n\r\n"
resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -1:
resp = resp + self.recv(1)
# We just need the first line to check if the connection
# was successful
statusline = resp.splitlines()[0].split(" ".encode(), 2)
if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
try:
statuscode = int(statusline[1])
except ValueError:
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
if statuscode != 200:
self.close()
raise HTTPError((statuscode, statusline[2]))
self.__proxysockname = ("0.0.0.0", 0)
self.__proxypeername = (addr, destport)
def connect(self, destpair):
"""connect(self, despair)
Connects to the specified destination through a proxy.
destpar - A tuple of the IP/DNS address and the port number.
(identical to socket's connect).
To select the proxy server use setproxy().
"""
# Do a minimal input check first
if (
(not type(destpair) in (list, tuple))
or (len(destpair) < 2)
or (not isinstance(destpair[0], (str, bytes)))
or (type(destpair[1]) != int)
):
raise GeneralProxyError((5, _generalerrors[5]))
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks5(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks4(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_HTTP:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 8080
_orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatehttp(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_HTTP_NO_TUNNEL:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 8080
_orgsocket.connect(self, (self.__proxy[1], portnum))
if destpair[1] == 443:
self.__negotiatehttp(destpair[0], destpair[1])
else:
self.__httptunnel = False
elif self.__proxy[0] == None:
_orgsocket.connect(self, (destpair[0], destpair[1]))
else:
raise GeneralProxyError((4, _generalerrors[4]))
+9
View File
@@ -0,0 +1,9 @@
from __future__ import absolute_import
import sys
if sys.version_info[0] < 3:
from repr import *
else:
raise ImportError('This package should not be accessible on Python 3. '
'Either you are trying to run from the python-future src folder '
'or your installation of python-future is corrupted.')
+31 -5
View File
@@ -3,6 +3,11 @@
# Parámetros de configuración (kodi) # Parámetros de configuración (kodi)
# ------------------------------------------------------------ # ------------------------------------------------------------
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import os import os
import re import re
@@ -62,10 +67,12 @@ def get_platform(full_version=False):
ret = {} ret = {}
codename = {"10": "dharma", "11": "eden", "12": "frodo", codename = {"10": "dharma", "11": "eden", "12": "frodo",
"13": "gotham", "14": "helix", "15": "isengard", "13": "gotham", "14": "helix", "15": "isengard",
"16": "jarvis", "17": "krypton", "18": "leia"} "16": "jarvis", "17": "krypton", "18": "leia",
"19": "matrix"}
code_db = {'10': 'MyVideos37.db', '11': 'MyVideos60.db', '12': 'MyVideos75.db', code_db = {'10': 'MyVideos37.db', '11': 'MyVideos60.db', '12': 'MyVideos75.db',
'13': 'MyVideos78.db', '14': 'MyVideos90.db', '15': 'MyVideos93.db', '13': 'MyVideos78.db', '14': 'MyVideos90.db', '15': 'MyVideos93.db',
'16': 'MyVideos99.db', '17': 'MyVideos107.db', '18': 'MyVideos116.db'} '16': 'MyVideos99.db', '17': 'MyVideos107.db', '18': 'MyVideos116.db',
'19': 'MyVideos116.db'}
num_version = xbmc.getInfoLabel('System.BuildVersion') num_version = xbmc.getInfoLabel('System.BuildVersion')
num_version = re.match("\d+\.\d+", num_version).group(0) num_version = re.match("\d+\.\d+", num_version).group(0)
@@ -334,7 +341,7 @@ def set_setting(name, value, channel="", server=""):
__settings__.setSetting(name, value) __settings__.setSetting(name, value)
except Exception, ex: except Exception as ex:
from platformcode import logger from platformcode import logger
logger.error("Error al convertir '%s' no se guarda el valor \n%s" % (name, ex)) logger.error("Error al convertir '%s' no se guarda el valor \n%s" % (name, ex))
return None return None
@@ -346,7 +353,18 @@ def get_localized_string(code):
dev = __language__(code) dev = __language__(code)
try: try:
dev = dev.encode("utf-8") # Unicode to utf8
if isinstance(dev, unicode):
dev = dev.encode("utf8")
if PY3: dev = dev.decode("utf8")
# All encodings to utf8
elif not PY3 and isinstance(dev, str):
dev = unicode(dev, "utf8", errors="replace").encode("utf8")
# Bytes encodings to utf8
elif PY3 and isinstance(dev, bytes):
dev = dev.decode("utf8")
except: except:
pass pass
@@ -356,7 +374,7 @@ def get_localized_category(categ):
categories = {'movie': get_localized_string(30122), 'tvshow': get_localized_string(30123), categories = {'movie': get_localized_string(30122), 'tvshow': get_localized_string(30123),
'anime': get_localized_string(30124), 'documentary': get_localized_string(30125), 'anime': get_localized_string(30124), 'documentary': get_localized_string(30125),
'vos': get_localized_string(30136), 'sub-ita': get_localized_string(70566), 'adult': get_localized_string(30126), 'vos': get_localized_string(30136), 'sub-ita': get_localized_string(70566), 'adult': get_localized_string(30126),
'direct': get_localized_string(30137), 'torrent': get_localized_string(70015)} 'direct': get_localized_string(30137), 'torrent': get_localized_string(70015), 'live': get_localized_string(30138)}
return categories[categ] if categ in categories else categ return categories[categ] if categ in categories else categ
@@ -391,6 +409,14 @@ def get_data_path():
return dev return dev
def get_icon():
return xbmc.translatePath(__settings__.getAddonInfo('icon'))
def get_fanart():
return xbmc.translatePath(__settings__.getAddonInfo('fanart'))
def get_cookie_data(): def get_cookie_data():
import os import os
ficherocookies = os.path.join(get_data_path(), 'cookies.dat') ficherocookies = os.path.join(get_data_path(), 'cookies.dat')
+202 -52
View File
@@ -3,17 +3,23 @@
# Updater (kodi) # Updater (kodi)
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
import json #from builtins import str
import os import sys
import traceback PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import traceback
import xbmc import xbmc
import xbmcaddon import xbmcaddon
import threading
import subprocess
import time
from core import filetools
from core import jsontools
from platformcode import config, logger, platformtools from platformcode import config, logger, platformtools
from core import jsontools
from core import filetools
json_data_file_name = 'custom_code.json' json_data_file_name = 'custom_code.json'
@@ -21,10 +27,9 @@ def init():
logger.info() logger.info()
""" """
Todo el código añadido al add-on se borra con cada actualización. Esta función permite restaurarlo automáticamente con cada actualización. Todo el código añadido al add-on se borra con cada actualización. Esta función permite restaurarlo automáticamente con cada actualización. Esto permite al usuario tener su propio código, bajo su responsabilidad, y restaurarlo al add-on cada vez que se actualiza.
Esto permite al usuario tener su propio código, bajo su responsabilidad, y restaurarlo al add-on cada vez que se actualiza.
El mecanismo funciona copiando el contenido de la carpeta-arbol ".\userdata\addon_data\plugin.video.alfa\custom_code\..." sobre El mecanismo funciona copiando el contenido de la carpeta-arbol "./userdata/addon_data/plugin.video.alfa/custom_code/..." sobre
las carpetas de código del add-on. No verifica el contenido, solo vuelca(reemplaza) el contenido de "custom_code". las carpetas de código del add-on. No verifica el contenido, solo vuelca(reemplaza) el contenido de "custom_code".
El usuario almacenará en las subcarpetas de "custom_code" su código actualizado y listo para ser copiado en cualquier momento. El usuario almacenará en las subcarpetas de "custom_code" su código actualizado y listo para ser copiado en cualquier momento.
@@ -37,7 +42,7 @@ def init():
from platformcode import custom_code from platformcode import custom_code
custom_code.init() custom_code.init()
2.- En el inicio de Kodi, comprueba si existe la carpeta "custom_code" en ".\userdata\addon_data\plugin.video.alfa\". 2.- En el inicio de Kodi, comprueba si existe la carpeta "custom_code" en "./userdata/addon_data/plugin.video.alfa/".
Si no existe, la crea y sale sin más, dando al ususario la posibilidad de copiar sobre esa estructura su código, Si no existe, la crea y sale sin más, dando al ususario la posibilidad de copiar sobre esa estructura su código,
y que la función la vuelque sobre el add-on en el próximo inicio de Kodi. y que la función la vuelque sobre el add-on en el próximo inicio de Kodi.
@@ -57,29 +62,43 @@ def init():
""" """
try: try:
#Borra el .zip de instalación de Alfa de la carpeta Packages, por si está corrupto, y que así se pueda descargar de nuevo
version = 'plugin.video.alfa-%s.zip' % config.get_addon_version(with_fix=False)
filetools.remove(filetools.join(xbmc.translatePath('special://home'), 'addons', 'packages', version), True)
#Verifica si Kodi tiene algún achivo de Base de Datos de Vídeo de versiones anteriores, entonces los borra #Verifica si Kodi tiene algún achivo de Base de Datos de Vídeo de versiones anteriores, entonces los borra
verify_Kodi_video_DB() verify_Kodi_video_DB()
#LIBTORRENT: se descarga el binario de Libtorrent cada vez que se actualiza Alfa
try:
threading.Thread(target=update_libtorrent).start() # Creamos un Thread independiente, hasta el fin de Kodi
time.sleep(2) # Dejamos terminar la inicialización...
except: # Si hay problemas de threading, nos vamos
logger.error(traceback.format_exc())
#QUASAR: Preguntamos si se hacen modificaciones a Quasar #QUASAR: Preguntamos si se hacen modificaciones a Quasar
if not filetools.exists(os.path.join(config.get_data_path(), "quasar.json")) and not config.get_setting('addon_quasar_update', default=False): if not filetools.exists(filetools.join(config.get_data_path(), "quasar.json")) \
and not config.get_setting('addon_quasar_update', default=False):
question_update_external_addon("quasar") question_update_external_addon("quasar")
#QUASAR: Hacemos las modificaciones a Quasar, si está permitido, y si está instalado #QUASAR: Hacemos las modificaciones a Quasar, si está permitido, y si está instalado
if config.get_setting('addon_quasar_update', default=False): if config.get_setting('addon_quasar_update', default=False) or \
(filetools.exists(filetools.join(config.get_data_path(), \
"quasar.json")) and not xbmc.getCondVisibility('System.HasAddon("plugin.video.quasar")')):
if not update_external_addon("quasar"): if not update_external_addon("quasar"):
platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log") platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log")
#Existe carpeta "custom_code" ? Si no existe se crea y se sale #Existe carpeta "custom_code" ? Si no existe se crea y se sale
custom_code_dir = os.path.join(config.get_data_path(), 'custom_code') custom_code_dir = filetools.join(config.get_data_path(), 'custom_code')
if os.path.exists(custom_code_dir) == False: if not filetools.exists(custom_code_dir):
create_folder_structure(custom_code_dir) create_folder_structure(custom_code_dir)
return return
else: else:
#Existe "custom_code.json" ? Si no existe se crea #Existe "custom_code.json" ? Si no existe se crea
custom_code_json_path = config.get_runtime_path() custom_code_json_path = config.get_runtime_path()
custom_code_json = os.path.join(custom_code_json_path, 'custom_code.json') custom_code_json = filetools.join(custom_code_json_path, 'custom_code.json')
if os.path.exists(custom_code_json) == False: if not filetools.exists(custom_code_json):
create_json(custom_code_json_path) create_json(custom_code_json_path)
#Se verifica si la versión del .json y del add-on son iguales. Si es así se sale. Si no se copia "custom_code" al add-on #Se verifica si la versión del .json y del add-on son iguales. Si es así se sale. Si no se copia "custom_code" al add-on
@@ -92,13 +111,13 @@ def create_folder_structure(custom_code_dir):
logger.info() logger.info()
#Creamos todas las carpetas. La importante es "custom_code". Las otras sirven meramente de guía para evitar errores de nombres... #Creamos todas las carpetas. La importante es "custom_code". Las otras sirven meramente de guía para evitar errores de nombres...
os.mkdir(custom_code_dir) filetools.mkdir(custom_code_dir)
os.mkdir(filetools.join(custom_code_dir, 'channels')) filetools.mkdir(filetools.join(custom_code_dir, 'channels'))
os.mkdir(filetools.join(custom_code_dir, 'core')) filetools.mkdir(filetools.join(custom_code_dir, 'core'))
os.mkdir(filetools.join(custom_code_dir, 'lib')) filetools.mkdir(filetools.join(custom_code_dir, 'lib'))
os.mkdir(filetools.join(custom_code_dir, 'platformcode')) filetools.mkdir(filetools.join(custom_code_dir, 'platformcode'))
os.mkdir(filetools.join(custom_code_dir, 'resources')) filetools.mkdir(filetools.join(custom_code_dir, 'resources'))
os.mkdir(filetools.join(custom_code_dir, 'servers')) filetools.mkdir(filetools.join(custom_code_dir, 'servers'))
return return
@@ -108,9 +127,9 @@ def create_json(custom_code_json_path, json_name=json_data_file_name):
#Guardamaos el json con la versión de Alfa vacía, para permitir hacer la primera copia #Guardamaos el json con la versión de Alfa vacía, para permitir hacer la primera copia
json_data_file = filetools.join(custom_code_json_path, json_name) json_data_file = filetools.join(custom_code_json_path, json_name)
json_file = open(json_data_file, "a+") if filetools.exists(json_data_file):
json_file.write(json.dumps({"addon_version": ""})) filetools.remove(json_data_file)
json_file.close() result = filetools.write(json_data_file, jsontools.dump({"addon_version": ""}))
return return
@@ -122,15 +141,21 @@ def verify_copy_folders(custom_code_dir, custom_code_json_path):
json_data_file = filetools.join(custom_code_json_path, json_data_file_name) json_data_file = filetools.join(custom_code_json_path, json_data_file_name)
json_data = jsontools.load(filetools.read(json_data_file)) json_data = jsontools.load(filetools.read(json_data_file))
current_version = config.get_addon_version(with_fix=False) current_version = config.get_addon_version(with_fix=False)
if current_version == json_data['addon_version']: if not json_data or not 'addon_version' in json_data:
return create_json(custom_code_json_path)
json_data = jsontools.load(filetools.read(json_data_file))
try:
if current_version == json_data['addon_version']:
return
except:
logger.error(traceback.format_exc(1))
#Ahora copiamos los archivos desde el área de Userdata, Custom_code, sobre las carpetas del add-on #Ahora copiamos los archivos desde el área de Userdata, Custom_code, sobre las carpetas del add-on
for root, folders, files in os.walk(custom_code_dir): for root, folders, files in filetools.walk(custom_code_dir):
for file in files: for file in files:
input_file = filetools.join(root, file) input_file = filetools.join(root, file)
output_file = input_file.replace(custom_code_dir, custom_code_json_path) output_file = input_file.replace(custom_code_dir, custom_code_json_path)
if filetools.copy(input_file, output_file, silent=True) == False: if not filetools.copy(input_file, output_file, silent=True):
return return
#Guardamaos el json con la versión actual de Alfa, para no volver a hacer la copia hasta la nueva versión #Guardamaos el json con la versión actual de Alfa, para no volver a hacer la copia hasta la nueva versión
@@ -161,37 +186,162 @@ def question_update_external_addon(addon_name):
return stat return stat
def update_external_addon(addon_name): def update_external_addon(addon_name):
logger.info(addon_name) logger.info(addon_name)
#Verificamos que el addon está instalado try:
if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name): #Verificamos que el addon está instalado
#Path de actuali<aciones de Alfa if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name):
alfa_addon_updates = filetools.join(config.get_runtime_path(), filetools.join("lib", addon_name)) #Path de actualizaciones de Alfa
alfa_addon_updates_mig = filetools.join(config.get_runtime_path(), "lib")
alfa_addon_updates = filetools.join(alfa_addon_updates_mig, addon_name)
#Path de destino en addon externo #Path de destino en addon externo
__settings__ = xbmcaddon.Addon(id="plugin.video." + addon_name) __settings__ = xbmcaddon.Addon(id="plugin.video." + addon_name)
if addon_name.lower() in ['quasar', 'elementum']: if addon_name.lower() in ['quasar', 'elementum']:
addon_path = filetools.join(xbmc.translatePath(__settings__.getAddonInfo('Path')), filetools.join("resources", filetools.join("site-packages", addon_name))) addon_path_mig = filetools.join(xbmc.translatePath(__settings__.getAddonInfo('Path')), \
filetools.join("resources", "site-packages"))
addon_path = filetools.join(addon_path_mig, addon_name)
else:
addon_path_mig = ''
addon_path = ''
#Hay modificaciones en Alfa? Las copiamos al addon, incuidas las carpetas de migración a PY3
if filetools.exists(alfa_addon_updates) and filetools.exists(addon_path):
for root, folders, files in filetools.walk(alfa_addon_updates_mig):
if ('future' in root or 'past' in root) and not 'concurrent' in root:
for file in files:
alfa_addon_updates_mig_folder = root.replace(alfa_addon_updates_mig, addon_path_mig)
if not filetools.exists(alfa_addon_updates_mig_folder):
filetools.mkdir(alfa_addon_updates_mig_folder)
if file.endswith('.pyo') or file.endswith('.pyd'):
continue
input_file = filetools.join(root, file)
output_file = input_file.replace(alfa_addon_updates_mig, addon_path_mig)
if not filetools.copy(input_file, output_file, silent=True):
logger.error('Error en la copia de MIGRACIÓN: Input: %s o Output: %s' % (input_file, output_file))
return False
for root, folders, files in filetools.walk(alfa_addon_updates):
for file in files:
input_file = filetools.join(root, file)
output_file = input_file.replace(alfa_addon_updates, addon_path)
if not filetools.copy(input_file, output_file, silent=True):
logger.error('Error en la copia: Input: %s o Output: %s' % (input_file, output_file))
return False
return True
else:
logger.error('Alguna carpeta no existe: Alfa: %s o %s: %s' % (alfa_addon_updates, addon_name, addon_path))
# Se ha desinstalado Quasar, reseteamos la opción
else: else:
addon_path = '' config.set_setting('addon_quasar_update', False)
if filetools.exists(filetools.join(config.get_data_path(), "%s.json" % addon_name)):
#Hay modificaciones en Alfa? Las copiamos al addon filetools.remove(filetools.join(config.get_data_path(), "%s.json" % addon_name))
if filetools.exists(alfa_addon_updates) and filetools.exists(addon_path):
for root, folders, files in os.walk(alfa_addon_updates):
for file in files:
input_file = filetools.join(root, file)
output_file = input_file.replace(alfa_addon_updates, addon_path)
if filetools.copy(input_file, output_file, silent=True) == False:
logger.error('Error en la copia: Input: %s o Output: %s' % (input_file, output_file))
return False
return True return True
else: except:
logger.error('Alguna carpeta no existe: Alfa: %s o %s: %s' % (alfa_addon_updates, addon_name, addon_path)) logger.error(traceback.format_exc())
return False return False
def update_libtorrent():
logger.info()
if not config.get_setting("mct_buffer", server="torrent", default=""):
default = config.get_setting("torrent_client", server="torrent", default=0)
config.set_setting("torrent_client", default, server="torrent")
config.set_setting("mct_buffer", "50", server="torrent")
if config.get_setting("mct_download_path", server="torrent", default=config.get_setting("downloadpath")):
config.set_setting("mct_download_path", config.get_setting("downloadpath"), server="torrent")
config.set_setting("mct_background_download", True, server="torrent")
config.set_setting("mct_rar_unpack", True, server="torrent")
config.set_setting("bt_buffer", "50", server="torrent")
if config.get_setting("bt_download_path", server="torrent", default=config.get_setting("downloadpath")):
config.set_setting("bt_download_path", config.get_setting("downloadpath"), server="torrent")
config.set_setting("mct_download_limit", "", server="torrent")
config.set_setting("magnet2torrent", False, server="torrent")
if not filetools.exists(filetools.join(config.get_runtime_path(), "custom_code.json")) or not \
config.get_setting("unrar_path", server="torrent", default=""):
path = filetools.join(config.get_runtime_path(), 'lib', 'rarfiles')
creationflags = ''
sufix = ''
unrar = ''
for device in filetools.listdir(path):
if xbmc.getCondVisibility("system.platform.android") and 'android' not in device: continue
if xbmc.getCondVisibility("system.platform.windows") and 'windows' not in device: continue
if not xbmc.getCondVisibility("system.platform.windows") and not xbmc.getCondVisibility("system.platform.android") \
and ('android' in device or 'windows' in device): continue
if 'windows' in device:
creationflags = 0x08000000
sufix = '.exe'
else:
creationflags = ''
sufix = ''
unrar = filetools.join(path, device, 'unrar%s') % sufix
if not filetools.exists(unrar): unrar = ''
if unrar:
if not xbmc.getCondVisibility("system.platform.windows"):
try:
if xbmc.getCondVisibility("system.platform.android"):
# Para Android copiamos el binario a la partición del sistema
unrar_org = unrar
unrar = filetools.join(xbmc.translatePath('special://xbmc/'), 'files').replace('/cache/apk/assets', '')
if not filetools.exists(unrar):
filetools.mkdir(unrar)
unrar = filetools.join(unrar, 'unrar')
filetools.copy(unrar_org, unrar, silent=True)
command = ['chmod', '777', '%s' % unrar]
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output_cmd, error_cmd = p.communicate()
command = ['ls', '-l', unrar]
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output_cmd, error_cmd = p.communicate()
xbmc.log('######## UnRAR file: %s' % str(output_cmd), xbmc.LOGNOTICE)
except:
xbmc.log('######## UnRAR ERROR in path: %s' % str(unrar), xbmc.LOGNOTICE)
logger.error(traceback.format_exc(1))
try:
if xbmc.getCondVisibility("system.platform.windows"):
p = subprocess.Popen(unrar, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=creationflags)
else:
p = subprocess.Popen(unrar, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output_cmd, error_cmd = p.communicate()
if p.returncode != 0 or error_cmd:
xbmc.log('######## UnRAR returncode in module %s: %s, %s in %s' % \
(device, str(p.returncode), str(error_cmd), unrar), xbmc.LOGNOTICE)
unrar = ''
else:
xbmc.log('######## UnRAR OK in %s: %s' % (device, unrar), xbmc.LOGNOTICE)
break
except:
xbmc.log('######## UnRAR ERROR in module %s: %s' % (device, unrar), xbmc.LOGNOTICE)
logger.error(traceback.format_exc(1))
unrar = ''
if unrar: config.set_setting("unrar_path", unrar, server="torrent")
if filetools.exists(filetools.join(config.get_runtime_path(), "custom_code.json")) and \
config.get_setting("libtorrent_path", server="torrent", default="") :
return
try:
from lib.python_libtorrent.python_libtorrent import get_libtorrent
except Exception as e:
logger.error(traceback.format_exc(1))
if not PY3:
e = unicode(str(e), "utf8", errors="replace").encode("utf8")
config.set_setting("libtorrent_path", "", server="torrent")
if not config.get_setting("libtorrent_error", server="torrent", default=''):
config.set_setting("libtorrent_error", str(e), server="torrent")
return
def verify_Kodi_video_DB(): def verify_Kodi_video_DB():
logger.info() logger.info()
import random import random
@@ -204,12 +354,12 @@ def verify_Kodi_video_DB():
path = filetools.join(xbmc.translatePath("special://masterprofile/"), "Database") path = filetools.join(xbmc.translatePath("special://masterprofile/"), "Database")
if filetools.exists(path): if filetools.exists(path):
platform = config.get_platform(full_version=True) platform = config.get_platform(full_version=True)
if platform: if platform and platform['num_version'] <= 19:
db_files = filetools.walk(path) db_files = filetools.walk(path)
if filetools.exists(filetools.join(path, platform['video_db'])): if filetools.exists(filetools.join(path, platform['video_db'])):
for root, folders, files in db_files: for root, folders, files in db_files:
for file in files: for file in files:
if file != platform['video_db']: if platform['video_db'] not in file:
if file.startswith('MyVideos'): if file.startswith('MyVideos'):
randnum = str(random.randrange(1, 999999)) randnum = str(random.randrange(1, 999999))
filetools.rename(filetools.join(path, file), 'OLD_' + randnum +'_' + file) filetools.rename(filetools.join(path, file), 'OLD_' + randnum +'_' + file)
+20 -12
View File
@@ -5,17 +5,25 @@
# Based on code from the Mega add-on (xbmchub.com) # Based on code from the Mega add-on (xbmchub.com)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
from __future__ import division
from future import standard_library
standard_library.install_aliases()
#from builtins import str
from past.utils import old_div
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import urllib.request, urllib.parse, urllib.error
import os import os
import re import re
import socket import socket
import threading import threading
import time import time
import urllib
import urllib2
import xbmc import xbmc
import xbmcgui import xbmcgui
from core import downloadtools from core import downloadtools
from platformcode import config, logger from platformcode import config, logger
@@ -43,7 +51,7 @@ def download_and_play(url, file_name, download_path):
while not cancelled and download_thread.isAlive(): while not cancelled and download_thread.isAlive():
dialog.update(download_thread.get_progress(), config.get_localized_string(60313), dialog.update(download_thread.get_progress(), config.get_localized_string(60313),
"Velocidad: " + str(int(download_thread.get_speed() / 1024)) + " KB/s " + str( "Velocidad: " + str(int(old_div(download_thread.get_speed(), 1024))) + " KB/s " + str(
download_thread.get_actual_size()) + "MB de " + str( download_thread.get_actual_size()) + "MB de " + str(
download_thread.get_total_size()) + "MB", download_thread.get_total_size()) + "MB",
"Tiempo restante: " + str(downloadtools.sec_to_hms(download_thread.get_remaining_time()))) "Tiempo restante: " + str(downloadtools.sec_to_hms(download_thread.get_remaining_time())))
@@ -232,7 +240,7 @@ class DownloadThread(threading.Thread):
for additional_header in additional_headers: for additional_header in additional_headers:
logger.info("additional_header: " + additional_header) logger.info("additional_header: " + additional_header)
name = re.findall("(.*?)=.*?", additional_header)[0] name = re.findall("(.*?)=.*?", additional_header)[0]
value = urllib.unquote_plus(re.findall(".*?=(.*?)$", additional_header)[0]) value = urllib.parse.unquote_plus(re.findall(".*?=(.*?)$", additional_header)[0])
headers.append([name, value]) headers.append([name, value])
self.url = self.url.split("|")[0] self.url = self.url.split("|")[0]
@@ -242,18 +250,18 @@ class DownloadThread(threading.Thread):
socket.setdefaulttimeout(60) socket.setdefaulttimeout(60)
# Crea la petición y añade las cabeceras # Crea la petición y añade las cabeceras
h = urllib2.HTTPHandler(debuglevel=0) h = urllib.request.HTTPHandler(debuglevel=0)
request = urllib2.Request(self.url) request = urllib.request.Request(self.url)
for header in headers: for header in headers:
logger.info("Header=" + header[0] + ": " + header[1]) logger.info("Header=" + header[0] + ": " + header[1])
request.add_header(header[0], header[1]) request.add_header(header[0], header[1])
# Lanza la petición # Lanza la petición
opener = urllib2.build_opener(h) opener = urllib.request.build_opener(h)
urllib2.install_opener(opener) urllib.request.install_opener(opener)
try: try:
connexion = opener.open(request) connexion = opener.open(request)
except urllib2.HTTPError, e: except urllib.error.HTTPError as e:
logger.error("error %d (%s) al abrir la url %s" % (e.code, e.msg, self.url)) logger.error("error %d (%s) al abrir la url %s" % (e.code, e.msg, self.url))
# print e.code # print e.code
# print e.msg # print e.msg
@@ -315,10 +323,10 @@ class DownloadThread(threading.Thread):
bloqueleido = connexion.read(blocksize) bloqueleido = connexion.read(blocksize)
after = time.time() after = time.time()
if (after - before) > 0: if (after - before) > 0:
self.velocidad = len(bloqueleido) / ((after - before)) self.velocidad = old_div(len(bloqueleido), ((after - before)))
falta = totalfichero - grabado falta = totalfichero - grabado
if self.velocidad > 0: if self.velocidad > 0:
self.tiempofalta = falta / self.velocidad self.tiempofalta = old_div(falta, self.velocidad)
else: else:
self.tiempofalta = 0 self.tiempofalta = 0
break break
+613
View File
@@ -0,0 +1,613 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Localiza las variables de entorno más habituales (kodi)
# ------------------------------------------------------------
from __future__ import division
# from builtins import str
from past.utils import old_div
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import xbmc
import xbmcaddon
import os
import subprocess
import re
import platform
try:
import ctypes
except:
pass
import traceback
from core import filetools, scrapertools
from platformcode import logger, config, platformtools
def get_environment():
"""
Devuelve las variables de entorno del OS, de Kodi y de Alfa más habituales,
necesarias para el diagnóstico de fallos
"""
try:
import base64
import ast
environment = config.get_platform(full_version=True)
environment['num_version'] = str(environment['num_version'])
environment['python_version'] = str(platform.python_version())
environment['os_release'] = str(platform.release())
if xbmc.getCondVisibility("system.platform.Windows"):
try:
if platform._syscmd_ver()[2]:
environment['os_release'] = str(platform._syscmd_ver()[2])
except:
pass
environment['prod_model'] = ''
if xbmc.getCondVisibility("system.platform.Android"):
environment['os_name'] = 'Android'
try:
for label_a in subprocess.check_output('getprop').split('\n'):
if 'build.version.release' in label_a:
environment['os_release'] = str(scrapertools.find_single_match(label_a, ':\s*\[(.*?)\]$'))
if 'product.model' in label_a:
environment['prod_model'] = str(scrapertools.find_single_match(label_a, ':\s*\[(.*?)\]$'))
except:
try:
for label_a in filetools.read(os.environ['ANDROID_ROOT'] + '/build.prop').split():
if 'build.version.release' in label_a:
environment['os_release'] = str(scrapertools.find_single_match(label_a, '=(.*?)$'))
if 'product.model' in label_a:
environment['prod_model'] = str(scrapertools.find_single_match(label_a, '=(.*?)$'))
except:
pass
elif xbmc.getCondVisibility("system.platform.Linux.RaspberryPi"):
environment['os_name'] = 'RaspberryPi'
else:
environment['os_name'] = str(platform.system())
environment['machine'] = str(platform.machine())
environment['architecture'] = str(sys.maxsize > 2 ** 32 and "64-bit" or "32-bit")
environment['language'] = str(xbmc.getInfoLabel('System.Language'))
environment['cpu_usage'] = str(xbmc.getInfoLabel('System.CpuUsage'))
environment['mem_total'] = str(xbmc.getInfoLabel('System.Memory(total)')).replace('MB', '').replace('KB', '')
environment['mem_free'] = str(xbmc.getInfoLabel('System.Memory(free)')).replace('MB', '').replace('KB', '')
if not environment['mem_total'] or not environment['mem_free']:
try:
if environment['os_name'].lower() == 'windows':
kernel32 = ctypes.windll.kernel32
c_ulong = ctypes.c_ulong
c_ulonglong = ctypes.c_ulonglong
class MEMORYSTATUS(ctypes.Structure):
_fields_ = [
('dwLength', c_ulong),
('dwMemoryLoad', c_ulong),
('dwTotalPhys', c_ulonglong),
('dwAvailPhys', c_ulonglong),
('dwTotalPageFile', c_ulonglong),
('dwAvailPageFile', c_ulonglong),
('dwTotalVirtual', c_ulonglong),
('dwAvailVirtual', c_ulonglong),
('availExtendedVirtual', c_ulonglong)
]
memoryStatus = MEMORYSTATUS()
memoryStatus.dwLength = ctypes.sizeof(MEMORYSTATUS)
kernel32.GlobalMemoryStatus(ctypes.byref(memoryStatus))
environment['mem_total'] = str(old_div(int(memoryStatus.dwTotalPhys), (1024 ** 2)))
environment['mem_free'] = str(old_div(int(memoryStatus.dwAvailPhys), (1024 ** 2)))
else:
with open('/proc/meminfo') as f:
meminfo = f.read()
environment['mem_total'] = str(
old_div(int(re.search(r'MemTotal:\s+(\d+)', meminfo).groups()[0]), 1024))
environment['mem_free'] = str(
old_div(int(re.search(r'MemAvailable:\s+(\d+)', meminfo).groups()[0]), 1024))
except:
environment['mem_total'] = ''
environment['mem_free'] = ''
try:
environment['kodi_buffer'] = '20'
environment['kodi_bmode'] = '0'
environment['kodi_rfactor'] = '4.0'
if filetools.exists(filetools.join(xbmc.translatePath("special://userdata"), "advancedsettings.xml")):
advancedsettings = filetools.read(filetools.join(xbmc.translatePath("special://userdata"),
"advancedsettings.xml")).split('\n')
for label_a in advancedsettings:
if 'memorysize' in label_a:
environment['kodi_buffer'] = str(old_div(int(scrapertools.find_single_match
(label_a, '>(\d+)<\/')), 1024 ** 2))
if 'buffermode' in label_a:
environment['kodi_bmode'] = str(scrapertools.find_single_match
(label_a, '>(\d+)<\/'))
if 'readfactor' in label_a:
environment['kodi_rfactor'] = str(scrapertools.find_single_match
(label_a, '>(.*?)<\/'))
except:
pass
environment['userdata_path'] = str(xbmc.translatePath(config.get_data_path()))
try:
if environment['os_name'].lower() == 'windows':
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(environment['userdata_path']),
None, None, ctypes.pointer(free_bytes))
environment['userdata_free'] = str(round(float(free_bytes.value) / (1024 ** 3), 3))
else:
disk_space = os.statvfs(environment['userdata_path'])
if not disk_space.f_frsize: disk_space.f_frsize = disk_space.f_frsize.f_bsize
environment['userdata_free'] = str(round((float(disk_space.f_bavail) / \
(1024 ** 3)) * float(disk_space.f_frsize), 3))
except:
environment['userdata_free'] = '?'
try:
environment['videolab_series'] = '?'
environment['videolab_episodios'] = '?'
environment['videolab_pelis'] = '?'
environment['videolab_path'] = str(xbmc.translatePath(config.get_videolibrary_path()))
if filetools.exists(filetools.join(environment['videolab_path'], \
config.get_setting("folder_tvshows"))):
environment['videolab_series'] = str(len(filetools.listdir(filetools.join(environment['videolab_path'], \
config.get_setting(
"folder_tvshows")))))
counter = 0
for root, folders, files in filetools.walk(filetools.join(environment['videolab_path'], \
config.get_setting("folder_tvshows"))):
for file in files:
if file.endswith('.strm'): counter += 1
environment['videolab_episodios'] = str(counter)
if filetools.exists(filetools.join(environment['videolab_path'], \
config.get_setting("folder_movies"))):
environment['videolab_pelis'] = str(len(filetools.listdir(filetools.join(environment['videolab_path'], \
config.get_setting(
"folder_movies")))))
except:
pass
try:
video_updates = ['No', 'Inicio', 'Una vez', 'Inicio+Una vez']
environment['videolab_update'] = str(video_updates[config.get_setting("update", "videolibrary")])
except:
environment['videolab_update'] = '?'
try:
if environment['os_name'].lower() == 'windows':
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(environment['videolab_path']),
None, None, ctypes.pointer(free_bytes))
environment['videolab_free'] = str(round(float(free_bytes.value) / (1024 ** 3), 3))
else:
disk_space = os.statvfs(environment['videolab_path'])
if not disk_space.f_frsize: disk_space.f_frsize = disk_space.f_frsize.f_bsize
environment['videolab_free'] = str(round((float(disk_space.f_bavail) / \
(1024 ** 3)) * float(disk_space.f_frsize), 3))
except:
environment['videolab_free'] = '?'
environment['torrent_list'] = []
environment['torrentcli_option'] = ''
environment['torrent_error'] = ''
environment['torrentcli_rar'] = config.get_setting("mct_rar_unpack", server="torrent", default=True)
environment['torrentcli_backgr'] = config.get_setting("mct_background_download", server="torrent", default=True)
environment['torrentcli_lib_path'] = config.get_setting("libtorrent_path", server="torrent", default="")
if environment['torrentcli_lib_path']:
lib_path = 'Activo'
else:
lib_path = 'Inactivo'
environment['torrentcli_unrar'] = config.get_setting("unrar_path", server="torrent", default="")
if environment['torrentcli_unrar']:
if xbmc.getCondVisibility("system.platform.Android"):
unrar = 'Android'
else:
unrar, bin = filetools.split(environment['torrentcli_unrar'])
unrar = unrar.replace('\\', '/')
if not unrar.endswith('/'):
unrar = unrar + '/'
unrar = scrapertools.find_single_match(unrar, '\/([^\/]+)\/$').capitalize()
else:
unrar = 'Inactivo'
torrent_id = config.get_setting("torrent_client", server="torrent", default=0)
environment['torrentcli_option'] = str(torrent_id)
torrent_options = platformtools.torrent_client_installed()
if lib_path == 'Activo':
torrent_options = ['MCT'] + torrent_options
torrent_options = ['BT'] + torrent_options
environment['torrent_list'].append({'Torrent_opt': str(torrent_id), 'Libtorrent': lib_path, \
'RAR_Auto': str(environment['torrentcli_rar']), \
'RAR_backgr': str(environment['torrentcli_backgr']), \
'UnRAR': unrar})
environment['torrent_error'] = config.get_setting("libtorrent_error", server="torrent", default="")
if environment['torrent_error']:
environment['torrent_list'].append({'Libtorrent_error': environment['torrent_error']})
for torrent_option in torrent_options:
cliente = dict()
cliente['D_load_Path'] = ''
cliente['Libre'] = '?'
cliente['Plug_in'] = torrent_option.replace('Plugin externo: ', '')
if cliente['Plug_in'] == 'BT':
cliente['D_load_Path'] = str(config.get_setting("bt_download_path", server="torrent", default=''))
if not cliente['D_load_Path']: continue
cliente['Buffer'] = str(config.get_setting("bt_buffer", server="torrent", default=50))
elif cliente['Plug_in'] == 'MCT':
cliente['D_load_Path'] = str(config.get_setting("mct_download_path", server="torrent", default=''))
if not cliente['D_load_Path']: continue
cliente['Buffer'] = str(config.get_setting("mct_buffer", server="torrent", default=50))
elif xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % cliente['Plug_in']):
__settings__ = xbmcaddon.Addon(id="plugin.video.%s" % cliente['Plug_in'])
cliente['Plug_in'] = cliente['Plug_in'].capitalize()
if cliente['Plug_in'] == 'Torrenter':
cliente['D_load_Path'] = str(xbmc.translatePath(__settings__.getSetting('storage')))
if not cliente['D_load_Path']:
cliente['D_load_Path'] = str(filetools.join(xbmc.translatePath("special://home/"), \
"cache", "xbmcup", "plugin.video.torrenter",
"Torrenter"))
cliente['Buffer'] = str(__settings__.getSetting('pre_buffer_bytes'))
else:
cliente['D_load_Path'] = str(xbmc.translatePath(__settings__.getSetting('download_path')))
cliente['Buffer'] = str(__settings__.getSetting('buffer_size'))
if __settings__.getSetting('download_storage') == '1' and __settings__.getSetting('memory_size'):
cliente['Memoria'] = str(__settings__.getSetting('memory_size'))
if cliente['D_load_Path']:
try:
if environment['os_name'].lower() == 'windows':
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(cliente['D_load_Path']),
None, None, ctypes.pointer(free_bytes))
cliente['Libre'] = str(round(float(free_bytes.value) / \
(1024 ** 3), 3)).replace('.', ',')
else:
disk_space = os.statvfs(cliente['D_load_Path'])
if not disk_space.f_frsize: disk_space.f_frsize = disk_space.f_frsize.f_bsize
cliente['Libre'] = str(round((float(disk_space.f_bavail) / \
(1024 ** 3)) * float(disk_space.f_frsize), 3)).replace('.', ',')
except:
pass
environment['torrent_list'].append(cliente)
environment['proxy_active'] = ''
try:
proxy_channel_bloqued_str = base64.b64decode(config.get_setting('proxy_channel_bloqued')).decode('utf-8')
proxy_channel_bloqued = dict()
proxy_channel_bloqued = ast.literal_eval(proxy_channel_bloqued_str)
for channel_bloqued, proxy_active in list(proxy_channel_bloqued.items()):
if proxy_active != 'OFF':
environment['proxy_active'] += channel_bloqued + ', '
except:
pass
if not environment['proxy_active']: environment['proxy_active'] = 'OFF'
environment['proxy_active'] = environment['proxy_active'].rstrip(', ')
for root, folders, files in filetools.walk(xbmc.translatePath("special://logpath/")):
for file in files:
if file.lower() in ['kodi.log', 'jarvis.log', 'spmc.log', 'cemc.log', \
'mygica.log', 'wonderbox.log', 'leiapp,log', \
'leianmc.log', 'kodiapp.log', 'anmc.log', \
'latin-anmc.log']:
environment['log_path'] = str(filetools.join(root, file))
break
else:
environment['log_path'] = ''
break
if environment['log_path']:
environment['log_size_bytes'] = str(filetools.getsize(environment['log_path']))
environment['log_size'] = str(round(float(environment['log_size_bytes']) / \
(1024 * 1024), 3))
else:
environment['log_size_bytes'] = ''
environment['log_size'] = ''
environment['debug'] = str(config.get_setting('debug'))
environment['addon_version'] = str(config.get_addon_version())
except:
logger.error(traceback.format_exc())
environment = {}
environment['log_size'] = ''
environment['cpu_usage'] = ''
environment['python_version'] = ''
environment['log_path'] = ''
environment['userdata_free'] = ''
environment['mem_total'] = ''
environment['machine'] = ''
environment['platform'] = ''
environment['videolab_path'] = ''
environment['num_version'] = ''
environment['os_name'] = ''
environment['video_db'] = ''
environment['userdata_path'] = ''
environment['log_size_bytes'] = ''
environment['name_version'] = ''
environment['language'] = ''
environment['mem_free'] = ''
environment['prod_model'] = ''
environment['proxy_active'] = ''
environment['architecture'] = ''
environment['os_release'] = ''
environment['videolab_free'] = ''
environment['kodi_buffer'] = ''
environment['kodi_bmode'] = ''
environment['kodi_rfactor'] = ''
environment['videolab_series'] = ''
environment['videolab_episodios'] = ''
environment['videolab_pelis'] = ''
environment['videolab_update'] = ''
environment['debug'] = ''
environment['addon_version'] = ''
environment['torrent_list'] = []
environment['torrentcli_option'] = ''
environment['torrentcli_rar'] = ''
environment['torrentcli_lib_path'] = ''
environment['torrentcli_unrar'] = ''
environment['torrent_error'] = ''
return environment
def list_env(environment={}):
if not environment:
environment = get_environment()
if environment['debug'] == 'False':
logger.log_enable(True)
logger.info('----------------------------------------------')
logger.info('Variables de entorno Alfa: ' + environment['addon_version'] +
' Debug: ' + environment['debug'])
logger.info("----------------------------------------------")
logger.info(environment['os_name'] + ' ' + environment['prod_model'] + ' ' +
environment['os_release'] + ' ' + environment['machine'] + ' ' +
environment['architecture'] + ' ' + environment['language'])
logger.info('Kodi ' + environment['num_version'] + ', Vídeo: ' +
environment['video_db'] + ', Python ' + environment['python_version'])
if environment['cpu_usage']:
logger.info('CPU: ' + environment['cpu_usage'])
if environment['mem_total'] or environment['mem_free']:
logger.info('Memoria: Total: ' + environment['mem_total'] + ' MB / Disp.: ' +
environment['mem_free'] + ' MB / Buffers: ' +
str(int(environment['kodi_buffer']) * 3) + ' MB / Buffermode: ' +
environment['kodi_bmode'] + ' / Readfactor: ' +
environment['kodi_rfactor'])
logger.info('Userdata: ' + environment['userdata_path'] + ' - Libre: ' +
environment['userdata_free'].replace('.', ',') + ' GB')
logger.info('Videoteca: Series/Epis: ' + environment['videolab_series'] + '/' +
environment['videolab_episodios'] + ' - Pelis: ' +
environment['videolab_pelis'] + ' - Upd: ' +
environment['videolab_update'] + ' - Path: ' +
environment['videolab_path'] + ' - Libre: ' +
environment['videolab_free'].replace('.', ',') + ' GB')
if environment['torrent_list']:
for x, cliente in enumerate(environment['torrent_list']):
if x == 0:
cliente_alt = cliente.copy()
del cliente_alt['Torrent_opt']
logger.info('Torrent: Opt: %s, %s' % (str(cliente['Torrent_opt']), \
str(cliente_alt).replace('{', '').replace('}', '') \
.replace("'", '').replace('_', ' ')))
elif x == 1 and environment['torrent_error']:
logger.info('- ' + str(cliente).replace('{', '').replace('}', '') \
.replace("'", '').replace('_', ' '))
else:
cliente_alt = cliente.copy()
del cliente_alt['Plug_in']
cliente_alt['Libre'] = cliente_alt['Libre'].replace('.', ',') + ' GB'
logger.info('- %s: %s' % (str(cliente['Plug_in']), str(cliente_alt) \
.replace('{', '').replace('}', '').replace("'", '') \
.replace('\\\\', '\\')))
logger.info('Proxy: ' + environment['proxy_active'])
logger.info('TAMAÑO del LOG: ' + environment['log_size'].replace('.', ',') + ' MB')
logger.info("----------------------------------------------")
if environment['debug'] == 'False':
logger.log_enable(False)
return environment
def paint_env(item, environment={}):
from core.item import Item
from channelselector import get_thumb
if not environment:
environment = get_environment()
environment = list_env(environment)
itemlist = []
thumb = get_thumb("setting_0.png")
cabecera = """\
Muestra las [COLOR yellow]variables[/COLOR] del ecosistema de Kodi que puden ser relevantes para el diagnóstico de problema en Alfa:
- Versión de Alfa con Fix
- Debug Alfa: True/False
"""
plataform = """\
Muestra los datos especificos de la [COLOR yellow]plataforma[/COLOR] en la que está alojado Kodi:
- Sistema Operativo
- Modelo (opt)
- Versión SO
- Procesador
- Aquitectura
- Idioma de Kodi
"""
kodi = """\
Muestra los datos especificos de la instalación de [COLOR yellow]Kodi[/COLOR]:
- Versión de Kodi
- Base de Datos de Vídeo
- Versión de Python
"""
cpu = """\
Muestra los datos consumo actual de [COLOR yellow]CPU(s)[/COLOR]
"""
memoria = """\
Muestra los datos del uso de [COLOR yellow]Memoria[/COLOR] del sistema:
- Memoria total
- Memoria disponible
- en [COLOR yellow]Advancedsettings.xml[/COLOR]
- Buffer de memoria
configurado:
para Kodi: 3 x valor de
<memorysize>
- Buffermode: cachea:
* Internet (0, 2)
* También local (1)
* No Buffer (3)
- Readfactor: readfactor *
avg bitrate vídeo
"""
userdata = """\
Muestra los datos del "path" de [COLOR yellow]Userdata[/COLOR]:
- Path
- Espacio disponible
"""
videoteca = """\
Muestra los datos de la [COLOR yellow]Videoteca[/COLOR]:
- de Series y Episodios
- de Películas
- Tipo de actulización
- Path
- Espacio disponible
"""
torrent = """\
Muestra los datos generales del estado de [COLOR yellow]Torrent[/COLOR]:
- ID del cliente seleccionado
- Descompresión automática de archivos RAR?
- Está activo Libtorrent?
- Se descomprimen los RARs en background?
- Está operativo el módulo UnRAR? Qué plataforma?
"""
torrent_error = """\
Muestra los datos del error de importación de [COLOR yellow]Libtorrent[/COLOR]
"""
torrent_cliente = """\
Muestra los datos de los [COLOR yellow]Clientes Torrent[/COLOR]:
- Nombre del Cliente
- Tamaño de buffer inicial
- Path de descargas
- Tamaño de buffer en Memoria
(opt, si no disco)
- Espacio disponible
"""
proxy = """\
Muestra las direcciones de canales o servidores que necesitan [COLOR yellow]Proxy[/COLOR]
"""
log = """\
Muestra el tamaño actual del [COLOR yellow]Log[/COLOR]
"""
reporte = """\
Enlaza con la utilidad que permite el [COLOR yellow]envío del Log[/COLOR] de Kodi a través de un servicio Pastebin
"""
itemlist.append(Item(channel=item.channel, title="[COLOR orange][B]Variables " +
"de entorno Alfa: %s Debug: %s[/B][/COLOR]" %
(environment['addon_version'], environment['debug']),
action="", plot=cabecera, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]%s[/COLOR]' %
environment['os_name'] + ' ' + environment['prod_model'] + ' ' +
environment['os_release'] + ' ' + environment['machine'] + ' ' +
environment['architecture'] + ' ' + environment['language'],
action="", plot=plataform, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Kodi [/COLOR]' +
environment['num_version'] + ', Vídeo: ' + environment[
'video_db'] +
', Python ' + environment['python_version'], action="",
plot=kodi, thumbnail=thumb, folder=False))
if environment['cpu_usage']:
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]CPU: [/COLOR]' +
environment['cpu_usage'], action="", plot=cpu, thumbnail=thumb,
folder=False))
if environment['mem_total'] or environment['mem_free']:
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Memoria: [/COLOR]Total: ' +
environment['mem_total'] + ' MB / Disp.: ' +
environment['mem_free'] + ' MB / Buffers: ' +
str(int(
environment['kodi_buffer']) * 3) + ' MB / Buffermode: ' +
environment['kodi_bmode'] + ' / Readfactor: ' +
environment['kodi_rfactor'],
action="", plot=memoria, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Userdata: [/COLOR]' +
environment['userdata_path'] + ' - Free: ' + environment[
'userdata_free'].replace('.', ',') +
' GB', action="", plot=userdata, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Videoteca: [/COLOR]Series/Epis: ' +
environment['videolab_series'] + '/' + environment[
'videolab_episodios'] +
' - Pelis: ' + environment['videolab_pelis'] + ' - Upd: ' +
environment['videolab_update'] + ' - Path: ' +
environment['videolab_path'] + ' - Free: ' + environment[
'videolab_free'].replace('.', ',') +
' GB', action="", plot=videoteca, thumbnail=thumb, folder=False))
if environment['torrent_list']:
for x, cliente in enumerate(environment['torrent_list']):
if x == 0:
cliente_alt = cliente.copy()
del cliente_alt['Torrent_opt']
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Torrent: [/COLOR]Opt: %s, %s' \
% (str(cliente['Torrent_opt']),
str(cliente_alt).replace('{', '').replace('}', '') \
.replace("'", '').replace('_', ' ')), action="",
plot=torrent, thumbnail=thumb,
folder=False))
elif x == 1 and environment['torrent_error']:
itemlist.append(Item(channel=item.channel,
title='[COLOR magenta]- %s[/COLOR]' % str(cliente).replace('{', '').replace('}',
'') \
.replace("'", '').replace('_', ' '), action="", plot=torrent_error,
thumbnail=thumb,
folder=False))
else:
cliente_alt = cliente.copy()
del cliente_alt['Plug_in']
cliente_alt['Libre'] = cliente_alt['Libre'].replace('.', ',') + ' GB'
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]- %s: [/COLOR]: %s' %
(str(cliente['Plug_in']),
str(cliente_alt).replace('{', '').replace('}', '') \
.replace("'", '').replace('\\\\', '\\')), action="",
plot=torrent_cliente,
thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Proxy: [/COLOR]' +
environment['proxy_active'], action="", plot=proxy,
thumbnail=thumb,
folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]TAMAÑO del LOG: [/COLOR]' +
environment['log_size'].replace('.', ',') + ' MB', action="",
plot=log, thumbnail=thumb,
folder=False))
itemlist.append(Item(title="[COLOR hotpink][B]==> Reportar un fallo[/B][/COLOR]",
channel="setting", action="report_menu", category='Configuración',
unify=False, plot=reporte, thumbnail=get_thumb("error.png")))
return (itemlist, environment)
+6 -1
View File
@@ -1,5 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from builtins import map
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
from threading import Timer from threading import Timer
import xbmc import xbmc
@@ -113,7 +118,7 @@ class Main(xbmcgui.WindowXMLDialog):
if config.get_platform(True)['num_version'] < 18: if config.get_platform(True)['num_version'] < 18:
self.setCoordinateResolution(2) self.setCoordinateResolution(2)
for menuentry in MAIN_MENU.keys(): for menuentry in list(MAIN_MENU.keys()):
item = xbmcgui.ListItem(MAIN_MENU[menuentry]["label"]) item = xbmcgui.ListItem(MAIN_MENU[menuentry]["label"])
item.setProperty("thumb", str(MAIN_MENU[menuentry]["icon"])) item.setProperty("thumb", str(MAIN_MENU[menuentry]["icon"]))
item.setProperty("identifier", str(menuentry)) item.setProperty("identifier", str(menuentry))
+60 -37
View File
@@ -3,24 +3,33 @@
# XBMC Launcher (xbmc / kodi) # XBMC Launcher (xbmc / kodi)
# ------------------------------------------------------------ # ------------------------------------------------------------
#from future import standard_library
#standard_library.install_aliases()
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
import urllib.error as urllib2 # Es muy lento en PY2. En PY3 es nativo
else:
import urllib2 # Usamos el nativo de PY2 que es más rápido
import os import os
import sys import sys
import time
import urllib2
from core import channeltools from core import channeltools
from core import scrapertools from core import scrapertools
from core import servertools from core import servertools
from core import trakt_tools
from core import videolibrarytools from core import videolibrarytools
from core import trakt_tools
from core.item import Item from core.item import Item
from platformcode import config, logger from platformcode import config, logger
from platformcode import platformtools from platformcode import platformtools
from platformcode.logger import WebErrorException from platformcode.logger import WebErrorException
def start(): def start():
""" Primera funcion que se ejecuta al entrar en el plugin. """ Primera funcion que se ejecuta al entrar en el plugin.
Dentro de esta funcion deberian ir todas las llamadas a las Dentro de esta funcion deberian ir todas las llamadas a las
@@ -30,18 +39,14 @@ def start():
#config.set_setting('show_once', True) #config.set_setting('show_once', True)
# Test if all the required directories are created # Test if all the required directories are created
config.verify_directories_created() config.verify_directories_created()
# controlla se l'utente ha qualche problema di connessione # controlla se l'utente ha qualche problema di connessione
# se lo ha: non lo fa entrare nell'addon # se lo ha: non lo fa entrare nell'addon
# se ha problemi di DNS avvia ma lascia entrare # se ha problemi di DNS avvia ma lascia entrare
# se tutto ok: entra nell'addon # se tutto ok: entra nell'addon
from specials import resolverdns
from specials.checkhost import test_conn from specials.checkhost import test_conn
import threading import threading
threading.Thread(target=test_conn, args=(True, not config.get_setting('resolver_dns'), True, [], [], True)).start() threading.Thread(target=test_conn, args=(True, not config.get_setting('resolver_dns'), True, [], [], True)).start()
# check_adsl = test_conn(is_exit = True, check_dns = True, view_msg = True,
# lst_urls = [], lst_site_check_dns = [], in_addon = True)
def run(item=None): def run(item=None):
logger.info() logger.info()
@@ -78,9 +83,6 @@ def run(item=None):
else: else:
item = Item(channel="channelselector", action="getmainlist", viewmode="movie") item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
if not config.get_setting('show_once'): if not config.get_setting('show_once'):
if not config.dev_mode():
from platformcode import updater
updater.calcCurrHash()
from platformcode import xbmc_videolibrary from platformcode import xbmc_videolibrary
xbmc_videolibrary.ask_set_content(1, config.get_setting('videolibrary_kodi_force')) xbmc_videolibrary.ask_set_content(1, config.get_setting('videolibrary_kodi_force'))
config.set_setting('show_once', True) config.set_setting('show_once', True)
@@ -88,9 +90,12 @@ def run(item=None):
logger.info(item.tostring()) logger.info(item.tostring())
try: try:
if not config.get_setting('tmdb_active'):
config.set_setting('tmdb_active', True)
# If item has no action, stops here # If item has no action, stops here
if item.action == "": if item.action == "":
logger.info("Item sin accion") logger.info("Item without action")
return return
# Action for main menu in channelselector # Action for main menu in channelselector
@@ -145,8 +150,12 @@ def run(item=None):
if xbmc.getCondVisibility('system.platform.linux') and xbmc.getCondVisibility('system.platform.android'): # android if xbmc.getCondVisibility('system.platform.linux') and xbmc.getCondVisibility('system.platform.android'): # android
xbmc.executebuiltin('StartAndroidActivity("", "android.intent.action.VIEW", "", "%s")' % (item.url)) xbmc.executebuiltin('StartAndroidActivity("", "android.intent.action.VIEW", "", "%s")' % (item.url))
else: else:
short = urllib2.urlopen( try:
'https://u.nu/api.php?action=shorturl&format=simple&url=' + item.url).read() import urllib.request as urllib
except ImportError:
import urllib
short = urllib.urlopen(
'https://u.nu/api.php?action=shorturl&format=simple&url=' + item.url).read().decode('utf-8')
platformtools.dialog_ok(config.get_localized_string(20000), platformtools.dialog_ok(config.get_localized_string(20000),
config.get_localized_string(70740) % short) config.get_localized_string(70740) % short)
# Action in certain channel specified in "action" and "channel" parameters # Action in certain channel specified in "action" and "channel" parameters
@@ -169,28 +178,28 @@ def run(item=None):
# Checks if channel exists # Checks if channel exists
if os.path.isfile(os.path.join(config.get_runtime_path(), 'channels', item.channel + ".py")): if os.path.isfile(os.path.join(config.get_runtime_path(), 'channels', item.channel + ".py")):
CHANNELS = 'channels' CHANNELS = 'channels'
elif os.path.isfile(os.path.join(config.get_runtime_path(), 'channels', 'porn', item.channel + ".py")): elif os.path.isfile(os.path.join(config.get_runtime_path(), 'channels', 'porn', item.channel + ".py")):
CHANNELS = 'channels.porn' CHANNELS = 'channels.porn'
else: else:
CHANNELS ='specials' CHANNELS = 'specials'
if CHANNELS != 'channels.porn': if CHANNELS != 'channels.porn':
channel_file = os.path.join(config.get_runtime_path(), CHANNELS, item.channel + ".py") channel_file = os.path.join(config.get_runtime_path(), CHANNELS, item.channel + ".py")
else: else:
channel_file = os.path.join(config.get_runtime_path(), 'channels', 'porn', item.channel + ".py") channel_file = os.path.join(config.get_runtime_path(), 'channels', 'porn',
item.channel + ".py")
logger.info("channel_file= " + channel_file + ' - ' + CHANNELS +' - ' + item.channel) logger.info("channel_file= " + channel_file + ' - ' + CHANNELS + ' - ' + item.channel)
channel = None channel = None
if os.path.exists(channel_file): if os.path.exists(channel_file):
try: try:
channel = __import__(CHANNELS + item.channel, None, None, [CHANNELS + item.channel]) channel = __import__('%s.%s' % (CHANNELS, item.channel), None,
None, ['%s.%s' % (CHANNELS, item.channel)])
except ImportError: except ImportError:
importer = "import " + CHANNELS + "." + item.channel + " as channel " exec("import " + CHANNELS + "." + item.channel + " as channel")
exec(importer)
logger.info("Running channel %s | %s" % (channel.__name__, channel.__file__)) logger.info("Running channel %s | %s" % (channel.__name__, channel.__file__))
@@ -270,14 +279,22 @@ def run(item=None):
# Special action for searching, first asks for the words then call the "search" function # Special action for searching, first asks for the words then call the "search" function
elif item.action == "search": elif item.action == "search":
logger.info("item.action=%s" % item.action.upper()) logger.info("item.action=%s" % item.action.upper())
if channeltools.get_channel_setting('last_search', 'search'):
last_search = channeltools.get_channel_setting('Last_searched', 'search', '') # last_search = ""
else: # last_search_active = config.get_setting("last_search", "search")
last_search = '' # if last_search_active:
# try:
# current_saved_searches_list = list(config.get_setting("saved_searches_list", "search"))
# last_search = current_saved_searches_list[0]
# except:
# pass
last_search = channeltools.get_channel_setting('Last_searched', 'search', '')
tecleado = platformtools.dialog_input(last_search) tecleado = platformtools.dialog_input(last_search)
if tecleado is not None: if tecleado is not None:
channeltools.set_channel_setting('Last_searched', tecleado, 'search') channeltools.set_channel_setting('Last_searched', tecleado, 'search')
if 'search' in dir(channel): if 'search' in dir(channel):
itemlist = channel.search(item, tecleado) itemlist = channel.search(item, tecleado)
else: else:
@@ -308,26 +325,26 @@ def run(item=None):
platformtools.render_items(itemlist, item) platformtools.render_items(itemlist, item)
except urllib2.URLError, e: except urllib2.URLError as e:
import traceback import traceback
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# Grab inner and third party errors # Grab inner and third party errors
if hasattr(e, 'reason'): if hasattr(e, 'reason'):
logger.error("Razon del error, codigo: %s | Razon: %s" % (str(e.reason[0]), str(e.reason[1]))) logger.error("Reason for the error, code: %s | Reason: %s" % (str(e.reason[0]), str(e.reason[1])))
texto = config.get_localized_string(30050) # "No se puede conectar con el sitio web" texto = config.get_localized_string(30050) # "No se puede conectar con el sitio web"
platformtools.dialog_ok(config.get_localized_string(20000), texto) platformtools.dialog_ok(config.get_localized_string(20000), texto)
# Grab server response errors # Grab server response errors
elif hasattr(e, 'code'): elif hasattr(e, 'code'):
logger.error("Codigo de error HTTP : %d" % e.code) logger.error("HTTP error code: %d" % e.code)
# "El sitio web no funciona correctamente (error http %d)" # "El sitio web no funciona correctamente (error http %d)"
platformtools.dialog_ok(config.get_localized_string(20000), config.get_localized_string(30051) % e.code) platformtools.dialog_ok(config.get_localized_string(20000), config.get_localized_string(30051) % e.code)
except WebErrorException, e: except WebErrorException as e:
import traceback import traceback
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
patron = 'File "' + os.path.join(config.get_runtime_path(), CHANNELS, "").replace("\\", "\\\\") + '([^.]+)\.py"' patron = 'File "' + os.path.join(config.get_runtime_path(), "channels", "").replace("\\", "\\\\") + '([^.]+)\.py"'
canal = scrapertools.find_single_match(traceback.format_exc(), patron) canal = scrapertools.find_single_match(traceback.format_exc(), patron)
platformtools.dialog_ok( platformtools.dialog_ok(
@@ -382,13 +399,19 @@ def reorder_itemlist(itemlist):
[config.get_localized_string(60336), '[D]']] [config.get_localized_string(60336), '[D]']]
for item in itemlist: for item in itemlist:
old_title = unicode(item.title, "utf8").lower().encode("utf8") if not PY3:
old_title = unicode(item.title, "utf8").lower().encode("utf8")
else:
old_title = item.title.lower()
for before, after in to_change: for before, after in to_change:
if before in item.title: if before in item.title:
item.title = item.title.replace(before, after) item.title = item.title.replace(before, after)
break break
new_title = unicode(item.title, "utf8").lower().encode("utf8") if not PY3:
new_title = unicode(item.title, "utf8").lower().encode("utf8")
else:
new_title = item.title.lower()
if old_title != new_title: if old_title != new_title:
mod_list.append(item) mod_list.append(item)
modified += 1 modified += 1
@@ -401,7 +424,7 @@ def reorder_itemlist(itemlist):
new_list.extend(mod_list) new_list.extend(mod_list)
new_list.extend(not_mod_list) new_list.extend(not_mod_list)
logger.info("Titulos modificados:%i | No modificados:%i" % (modified, not_modified)) logger.info("Modified Titles:%i |Unmodified:%i" % (modified, not_modified))
if len(new_list) == 0: if len(new_list) == 0:
new_list = itemlist new_list = itemlist
+23 -3
View File
@@ -6,9 +6,12 @@
import inspect import inspect
import xbmc import xbmc
from platformcode import config from platformcode import config
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
loggeractive = (config.get_setting("debug") == True) loggeractive = (config.get_setting("debug") == True)
@@ -18,14 +21,20 @@ def log_enable(active):
def encode_log(message=""): def encode_log(message=""):
# Unicode to utf8 # Unicode to utf8
if type(message) == unicode: if isinstance(message, unicode):
message = message.encode("utf8") message = message.encode("utf8")
if PY3: message = message.decode("utf8")
# All encodings to utf8 # All encodings to utf8
elif type(message) == str: elif not PY3 and isinstance(message, str):
message = unicode(message, "utf8", errors="replace").encode("utf8") message = unicode(message, "utf8", errors="replace").encode("utf8")
# Bytes encodings to utf8
elif PY3 and isinstance(message, bytes):
message = message.decode("utf8")
# Objects to string # Objects to string
else: else:
message = str(message) message = str(message)
@@ -34,6 +43,17 @@ def encode_log(message=""):
def get_caller(message=None): def get_caller(message=None):
if message and isinstance(message, unicode):
message = message.encode("utf8")
if PY3: message = message.decode("utf8")
elif message and PY3 and isinstance(message, bytes):
message = message.decode("utf8")
elif message and not PY3:
message = unicode(message, "utf8", errors="replace").encode("utf8")
elif message:
message = str(message)
module = inspect.getmodule(inspect.currentframe().f_back.f_back) module = inspect.getmodule(inspect.currentframe().f_back.f_back)
if module == None: if module == None:
+480 -236
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+16 -13
View File
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from builtins import range
import xbmcgui import xbmcgui
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from platformcode import config from platformcode import config
from platformcode import platformtools from platformcode import platformtools
lang = 'it'
class Recaptcha(xbmcgui.WindowXMLDialog): class Recaptcha(xbmcgui.WindowXMLDialog):
def Start(self, key, referer): def Start(self, key, referer):
@@ -14,9 +15,9 @@ class Recaptcha(xbmcgui.WindowXMLDialog):
self.key = key self.key = key
self.headers = {'Referer': self.referer} self.headers = {'Referer': self.referer}
api_js = httptools.downloadpage("http://www.google.com/recaptcha/api.js?hl=es").data api_js = httptools.downloadpage("https://www.google.com/recaptcha/api.js?hl=" + lang).data
version = scrapertools.find_single_match(api_js, 'po.src = \'(.*?)\';').split("/")[5] version = scrapertools.find_single_match(api_js, 'po.src\s*=\s*\'(.*?)\';').split("/")[5]
self.url = "http://www.google.com/recaptcha/api/fallback?k=%s&hl=es&v=%s&t=2&ff=true" % (self.key, version) self.url = "https://www.google.com/recaptcha/api/fallback?k=" + self.key + "&hl=" + lang + "&v=" + version + "&t=2&ff=true"
self.doModal() self.doModal()
# Reload # Reload
if self.result == {}: if self.result == {}:
@@ -27,10 +28,10 @@ class Recaptcha(xbmcgui.WindowXMLDialog):
def update_window(self): def update_window(self):
data = httptools.downloadpage(self.url, headers=self.headers).data data = httptools.downloadpage(self.url, headers=self.headers).data
self.message = scrapertools.find_single_match(data, self.message = scrapertools.find_single_match(data,
'<div class="rc-imageselect-desc-no-canonical">(.*?)(?:</label>|</div>)').replace( '<div class="rc-imageselect-desc[a-z-]*">(.*?)(?:</label>|</div>)').replace(
"<strong>", "[B]").replace("</strong>", "[/B]") "<strong>", "[B]").replace("</strong>", "[/B]")
self.token = scrapertools.find_single_match(data, 'name="c" value="([^"]+)"') self.token = scrapertools.find_single_match(data, 'name="c" value="([^"]+)"')
self.image = "http://www.google.com/recaptcha/api2/payload?k=%s&c=%s" % (self.key, self.token) self.image = "https://www.google.com/recaptcha/api2/payload?k=%s&c=%s" % (self.key, self.token)
self.result = {} self.result = {}
self.getControl(10020).setImage(self.image) self.getControl(10020).setImage(self.image)
self.getControl(10000).setText(self.message) self.getControl(10000).setText(self.message)
@@ -56,16 +57,18 @@ class Recaptcha(xbmcgui.WindowXMLDialog):
self.close() self.close()
elif control == 10002: elif control == 10002:
self.result = [int(k) for k in range(9) if self.result.get(k, False) == True] self.result = [int(k) for k in range(9) if self.result.get(k, False)]
post = "c=%s" % self.token post = {
"c": self.token,
"response": self.result
}
for r in self.result: data = httptools.downloadpage(self.url, post=post, headers=self.headers).data
post += "&response=%s" % r from platformcode import logger
logger.info(data)
data = httptools.downloadpage(self.url, post, headers=self.headers).data
self.result = scrapertools.find_single_match(data, '<div class="fbc-verification-token">.*?>([^<]+)<') self.result = scrapertools.find_single_match(data, '<div class="fbc-verification-token">.*?>([^<]+)<')
if self.result: if self.result:
platformtools.dialog_notification("Captcha Correcto", "La verificación ha concluido") platformtools.dialog_notification("Captcha corretto", "Verifica conclusa")
self.close() self.close()
else: else:
self.result = {} self.result = {}
+119 -35
View File
@@ -1,17 +1,34 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import print_function
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
#from future import standard_library
#standard_library.install_aliases()
import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo
else:
import urllib # Usamos el nativo de PY2 que es más rápido
import os import os
import re import re
import string import string
import urllib
from unicodedata import normalize from unicodedata import normalize
from core import filetools
from core import httptools
from core import jsontools
from core import scrapertools
import xbmc import xbmc
import xbmcgui import xbmcgui
from platformcode import config, logger from platformcode import config, logger
allchars = string.maketrans('', '') if PY3: allchars = str.maketrans('', '')
if not PY3: allchars = string.maketrans('', '')
deletechars = ',\\/:*"<>|?' deletechars = ',\\/:*"<>|?'
@@ -38,14 +55,14 @@ def regex_tvshow(compare, file, sub=""):
for regex in regex_expressions: for regex in regex_expressions:
response_file = re.findall(regex, file) response_file = re.findall(regex, file)
if len(response_file) > 0: if len(response_file) > 0:
print "Regex File Se: %s, Ep: %s," % (str(response_file[0][0]), str(response_file[0][1]),) print("Regex File Se: %s, Ep: %s," % (str(response_file[0][0]), str(response_file[0][1]),))
tvshow = 1 tvshow = 1
if not compare: if not compare:
title = re.split(regex, file)[0] title = re.split(regex, file)[0]
for char in ['[', ']', '_', '(', ')', '.', '-']: for char in ['[', ']', '_', '(', ')', '.', '-']:
title = title.replace(char, ' ') title = title.replace(char, ' ')
if title.endswith(" "): title = title.strip() if title.endswith(" "): title = title.strip()
print "title: %s" % title print("title: %s" % title)
return title, response_file[0][0], response_file[0][1] return title, response_file[0][0], response_file[0][1]
else: else:
break break
@@ -74,7 +91,7 @@ def set_Subtitle():
logger.info() logger.info()
exts = [".srt", ".sub", ".txt", ".smi", ".ssa", ".ass"] exts = [".srt", ".sub", ".txt", ".smi", ".ssa", ".ass"]
subtitle_folder_path = os.path.join(config.get_data_path(), "subtitles") subtitle_folder_path = filetools.join(config.get_data_path(), "subtitles")
subtitle_type = config.get_setting("subtitle_type") subtitle_type = config.get_setting("subtitle_type")
@@ -90,9 +107,9 @@ def set_Subtitle():
config.set_setting("subtitlepath_folder", subtitle_path) config.set_setting("subtitlepath_folder", subtitle_path)
else: else:
subtitle_path = config.get_setting("subtitlepath_keyboard") subtitle_path = config.get_setting("subtitlepath_keyboard")
long = len(subtitle_path) long_v = len(subtitle_path)
if long > 0: if long_v > 0:
if subtitle_path.startswith("http") or subtitle_path[long - 4, long] in exts: if subtitle_path.startswith("http") or subtitle_path[long_v - 4, long] in exts:
logger.info("Con subtitulo : " + subtitle_path) logger.info("Con subtitulo : " + subtitle_path)
xbmc.Player().setSubtitles(subtitle_path) xbmc.Player().setSubtitles(subtitle_path)
return return
@@ -106,13 +123,13 @@ def set_Subtitle():
tvshow_title, season, episode = regex_tvshow(False, subtitle_name) tvshow_title, season, episode = regex_tvshow(False, subtitle_name)
try: try:
if episode != "": if episode != "":
Subnames = glob.glob(os.path.join(subtitle_path, "Tvshows", tvshow_title, Subnames = glob.glob(filetools.join(subtitle_path, "Tvshows", tvshow_title,
"%s %sx%s" % (tvshow_title, season, episode) + "*.??.???")) "%s %sx%s" % (tvshow_title, season, episode) + "*.??.???"))
else: else:
Subnames = glob.glob(os.path.join(subtitle_path, "Movies", subtitle_name + "*.??.???")) Subnames = glob.glob(filetools.join(subtitle_path, "Movies", subtitle_name + "*.??.???"))
for Subname in Subnames: for Subname in Subnames:
if os.path.splitext(Subname)[1] in exts: if os.path.splitext(Subname)[1] in exts:
logger.info("Con subtitulo : " + os.path.split(Subname)[1]) logger.info("Con subtitulo : " + filetools.split(Subname)[1])
xbmc.Player().setSubtitles((Subname)) xbmc.Player().setSubtitles((Subname))
except: except:
logger.error("error al cargar subtitulos") logger.error("error al cargar subtitulos")
@@ -147,13 +164,13 @@ def searchSubtitle(item):
if config.get_setting("subtitle_type") == 0: if config.get_setting("subtitle_type") == 0:
subtitlepath = config.get_setting("subtitlepath_folder") subtitlepath = config.get_setting("subtitlepath_folder")
if subtitlepath == "": if subtitlepath == "":
subtitlepath = os.path.join(config.get_data_path(), "subtitles") subtitlepath = filetools.join(config.get_data_path(), "subtitles")
config.set_setting("subtitlepath_folder", subtitlepath) config.set_setting("subtitlepath_folder", subtitlepath)
elif config.get_setting("subtitle_type") == 1: elif config.get_setting("subtitle_type") == 1:
subtitlepath = config.get_setting("subtitlepath_keyboard") subtitlepath = config.get_setting("subtitlepath_keyboard")
if subtitlepath == "": if subtitlepath == "":
subtitlepath = os.path.join(config.get_data_path(), "subtitles") subtitlepath = filetools.join(config.get_data_path(), "subtitles")
config.set_setting("subtitlepathkeyboard", subtitlepath) config.set_setting("subtitlepathkeyboard", subtitlepath)
elif subtitlepath.startswith("http"): elif subtitlepath.startswith("http"):
subtitlepath = config.get_setting("subtitlepath_folder") subtitlepath = config.get_setting("subtitlepath_folder")
@@ -161,27 +178,27 @@ def searchSubtitle(item):
else: else:
subtitlepath = config.get_setting("subtitlepath_folder") subtitlepath = config.get_setting("subtitlepath_folder")
if subtitlepath == "": if subtitlepath == "":
subtitlepath = os.path.join(config.get_data_path(), "subtitles") subtitlepath = filetools.join(config.get_data_path(), "subtitles")
config.set_setting("subtitlepath_folder", subtitlepath) config.set_setting("subtitlepath_folder", subtitlepath)
if not os.path.exists(subtitlepath): if not filetools.exists(subtitlepath):
try: try:
os.mkdir(subtitlepath) filetools.mkdir(subtitlepath)
except: except:
logger.error("error no se pudo crear path subtitulos") logger.error("error no se pudo crear path subtitulos")
return return
path_movie_subt = xbmc.translatePath(os.path.join(subtitlepath, "Movies")) path_movie_subt = xbmc.translatePath(filetools.join(subtitlepath, "Movies"))
if not os.path.exists(path_movie_subt): if not filetools.exists(path_movie_subt):
try: try:
os.mkdir(path_movie_subt) filetools.mkdir(path_movie_subt)
except: except:
logger.error("error no se pudo crear el path Movies") logger.error("error no se pudo crear el path Movies")
return return
full_path_tvshow = "" full_path_tvshow = ""
path_tvshow_subt = xbmc.translatePath(os.path.join(subtitlepath, "Tvshows")) path_tvshow_subt = xbmc.translatePath(filetools.join(subtitlepath, "Tvshows"))
if not os.path.exists(path_tvshow_subt): if not filetools.exists(path_tvshow_subt):
try: try:
os.mkdir(path_tvshow_subt) filetools.mkdir(path_tvshow_subt)
except: except:
logger.error("error no pudo crear el path Tvshows") logger.error("error no pudo crear el path Tvshows")
return return
@@ -189,20 +206,20 @@ def searchSubtitle(item):
title_new = title = urllib.unquote_plus(item.title) title_new = title = urllib.unquote_plus(item.title)
else: else:
title_new = title = urllib.unquote_plus(item.show + " - " + item.title) title_new = title = urllib.unquote_plus(item.show + " - " + item.title)
path_video_temp = xbmc.translatePath(os.path.join(config.get_runtime_path(), "resources", "subtitle.mp4")) path_video_temp = xbmc.translatePath(filetools.join(config.get_runtime_path(), "resources", "subtitle.mp4"))
if not os.path.exists(path_video_temp): if not filetools.exists(path_video_temp):
logger.error("error : no existe el video temporal de subtitulos") logger.error("error : no existe el video temporal de subtitulos")
return return
# path_video_temp = xbmc.translatePath(os.path.join( ,video_temp + ".mp4" )) # path_video_temp = xbmc.translatePath(filetools.join( ,video_temp + ".mp4" ))
title_new = _normalize(title_new) title_new = _normalize(title_new)
tvshow_title, season, episode = regex_tvshow(False, title_new) tvshow_title, season, episode = regex_tvshow(False, title_new)
if episode != "": if episode != "":
full_path_tvshow = xbmc.translatePath(os.path.join(path_tvshow_subt, tvshow_title)) full_path_tvshow = xbmc.translatePath(filetools.join(path_tvshow_subt, tvshow_title))
if not os.path.exists(full_path_tvshow): if not filetools.exists(full_path_tvshow):
os.mkdir(full_path_tvshow) # title_new + ".mp4" filetools.mkdir(full_path_tvshow) # title_new + ".mp4"
full_path_video_new = xbmc.translatePath( full_path_video_new = xbmc.translatePath(
os.path.join(full_path_tvshow, "%s %sx%s.mp4" % (tvshow_title, season, episode))) filetools.join(full_path_tvshow, "%s %sx%s.mp4" % (tvshow_title, season, episode)))
logger.info(full_path_video_new) logger.info(full_path_video_new)
listitem = xbmcgui.ListItem(title_new, iconImage="DefaultVideo.png", thumbnailImage="") listitem = xbmcgui.ListItem(title_new, iconImage="DefaultVideo.png", thumbnailImage="")
listitem.setInfo("video", listitem.setInfo("video",
@@ -210,14 +227,14 @@ def searchSubtitle(item):
"tvshowtitle": tvshow_title}) "tvshowtitle": tvshow_title})
else: else:
full_path_video_new = xbmc.translatePath(os.path.join(path_movie_subt, title_new + ".mp4")) full_path_video_new = xbmc.translatePath(filetools.join(path_movie_subt, title_new + ".mp4"))
listitem = xbmcgui.ListItem(title, iconImage="DefaultVideo.png", thumbnailImage="") listitem = xbmcgui.ListItem(title, iconImage="DefaultVideo.png", thumbnailImage="")
listitem.setInfo("video", {"Title": title_new, "Genre": "Movies"}) listitem.setInfo("video", {"Title": title_new, "Genre": "Movies"})
import shutil, time import time
try: try:
shutil.copy(path_video_temp, full_path_video_new) filetools.copy(path_video_temp, full_path_video_new)
copy = True copy = True
logger.info("nuevo path =" + full_path_video_new) logger.info("nuevo path =" + full_path_video_new)
time.sleep(2) time.sleep(2)
@@ -242,10 +259,10 @@ def searchSubtitle(item):
continue continue
time.sleep(1) time.sleep(1)
os.remove(full_path_video_new) filetools.remove(full_path_video_new)
try: try:
if full_path_tvshow != "": if full_path_tvshow != "":
os.rmdir(full_path_tvshow) filetools.rmdir(full_path_tvshow)
except OSError: except OSError:
pass pass
@@ -267,3 +284,70 @@ def saveSubtitleName(item):
else: else:
config.set_setting("subtitle_name", title) config.set_setting("subtitle_name", title)
return return
def get_from_subdivx(sub_url):
"""
:param sub_url: Url de descarga del subtitulo alojado en suvdivx.com
Por Ejemplo: http://www.subdivx.com/bajar.php?id=573942&u=8
:return: La ruta al subtitulo descomprimido
"""
logger.info()
sub = ''
sub_dir = os.path.join(config.get_data_path(), 'temp_subs')
if os.path.exists(sub_dir):
for sub_file in os.listdir(sub_dir):
old_sub = os.path.join(sub_dir, sub_file)
os.remove(old_sub)
else:
os.mkdir(sub_dir)
sub_url = sub_url.replace("&amp;", "&")
sub_data = httptools.downloadpage(sub_url, follow_redirects=False)
if 'x-frame-options' not in sub_data.headers:
sub_url = '%s' % sub_data.headers['location']
ext = sub_url[-4::]
file_id = "subtitle%s" % ext
filename = os.path.join(sub_dir, file_id)
try:
data_dl = httptools.downloadpage(sub_url).data
filetools.write(filename, data_dl)
sub = extract_file_online(sub_dir, filename)
except:
logger.info('sub no valido')
else:
logger.info('sub no valido')
return sub
def extract_file_online(path, filename):
"""
:param path: Ruta donde se encuentra el archivo comprimido
:param filename: Nombre del archivo comprimido
:return: Devuelve la ruta al subtitulo descomprimido
"""
logger.info()
url = "http://online.b1.org/rest/online/upload"
data = httptools.downloadpage(url, file=filename).data
result = jsontools.load(scrapertools.find_single_match(data, "result.listing = ([^;]+);"))
compressed = result["name"]
extracted = result["children"][0]["name"]
dl_url = "http://online.b1.org/rest/online/download/%s/%s" % (compressed, extracted)
extracted_path = os.path.join(path, extracted)
data_dl = httptools.downloadpage(dl_url).data
filetools.write(extracted_path, data_dl)
return extracted_path
+313 -264
View File
@@ -6,189 +6,167 @@
# datos obtenidos de las paginas # datos obtenidos de las paginas
# ---------------------------------------------------------- # ----------------------------------------------------------
import re # from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import os
import unicodedata import unicodedata
import re
import config from platformcode import config
from core.item import Item
from core import scrapertools from core import scrapertools
from platformcode import logger from platformcode import logger
thumb_dict = { thumb_dict = {"movies": "https://s10.postimg.cc/fxtqzdog9/peliculas.png",
"numbers": "http://icons.iconarchive.com/icons/custom-icon-design/pretty-office-10/256/Numbers-icon.png", "tvshows": "https://s10.postimg.cc/kxvslawe1/series.png",
"a": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-A-black-icon.png", "on air": "https://i.postimg.cc/HLLJWMcr/en-emision.png",
"accion": "https://s14.postimg.cc/sqy3q2aht/action.png", "all": "https://s10.postimg.cc/h1igpgw0p/todas.png",
"actors": "https://i.postimg.cc/tC2HMhVV/actors.png", "genres": "https://s10.postimg.cc/6c4rx3x1l/generos.png",
"adolescente" : "https://s10.postimg.cc/inq7u4p61/teens.png", "search": "https://s10.postimg.cc/v985e2izd/buscar.png",
"adultos": "https://s10.postimg.cc/s8raxc51l/adultos.png", "quality": "https://s10.postimg.cc/9bbojsbjd/calidad.png",
"adults": "https://s10.postimg.cc/s8raxc51l/adultos.png", "audio": "https://s10.postimg.cc/b34nern7d/audio.png",
"alcinema": "http://icons.iconarchive.com/icons/chromatix/aerial/256/movie-icon.png", #"http://icons.iconarchive.com/icons/itzikgur/my-seven/256/Movies-Films-icon.png", "newest": "https://s10.postimg.cc/g1s5tf1bt/novedades.png",
"all": "https://s10.postimg.cc/h1igpgw0p/todas.png", "last": "https://s10.postimg.cc/i6ciuk0eh/ultimas.png",
"alphabet": "https://s10.postimg.cc/4dy3ytmgp/a-z.png", "hot": "https://s10.postimg.cc/yu40x8q2x/destacadas.png",
"animacion": "https://s14.postimg.cc/vl193mupd/animation.png", "year": "https://s10.postimg.cc/atzrqg921/a_o.png",
"anime" : "https://s10.postimg.cc/n9mc2ikzt/anime.png", "alphabet": "https://s10.postimg.cc/4dy3ytmgp/a-z.png",
"artes marciales" : "https://s10.postimg.cc/4u1v51tzt/martial_arts.png", "recomended": "https://s10.postimg.cc/7xk1oqccp/recomendadas.png",
"asiaticas" : "https://i.postimg.cc/Xq0HXD5d/asiaticas.png", "more watched": "https://s10.postimg.cc/c6orr5neh/masvistas.png",
"audio": "https://s10.postimg.cc/b34nern7d/audio.png", "more voted": "https://s10.postimg.cc/lwns2d015/masvotadas.png",
"aventura": "http://icons.iconarchive.com/icons/sirubico/movie-genre/256/Adventure-2-icon.png",#"https://s14.postimg.cc/ky7fy5he9/adventure.png", "favorites": "https://s10.postimg.cc/rtg147gih/favoritas.png",
"b": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-B-black-icon.png", "colections": "https://s10.postimg.cc/ywnwjvytl/colecciones.png",
"belico": "https://s14.postimg.cc/5e027lru9/war.png", "categories": "https://s10.postimg.cc/v0ako5lmh/categorias.png",
"biografia" : "https://s10.postimg.cc/jq0ecjxnt/biographic.png", "premieres": "https://s10.postimg.cc/sk8r9xdq1/estrenos.png",
"c": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-C-black-icon.png", "documentaries": "https://s10.postimg.cc/68aygmmcp/documentales.png",
"carreras": "https://s14.postimg.cc/yt5qgdr69/races.png", "language": "https://s10.postimg.cc/6wci189ft/idioma.png",
"cast": "https://i.postimg.cc/qvfP5Xvt/cast.png", "new episodes": "https://s10.postimg.cc/fu4iwpnqh/nuevoscapitulos.png",
"categories": "https://s10.postimg.cc/v0ako5lmh/categorias.png", "country": "https://s10.postimg.cc/yz0h81j15/pais.png",
"ciencia ficcion": "https://s14.postimg.cc/8kulr2jy9/scifi.png", "adults": "https://s10.postimg.cc/s8raxc51l/adultos.png",
"cine negro" : "https://s10.postimg.cc/6ym862qgp/noir.png", "recents": "https://s10.postimg.cc/649u24kp5/recents.png",
"colections": "https://s10.postimg.cc/ywnwjvytl/colecciones.png", "updated": "https://s10.postimg.cc/46m3h6h9l/updated.png",
"comedia": "https://s14.postimg.cc/9ym8moog1/comedy.png", "actors": "https://i.postimg.cc/tC2HMhVV/actors.png",
"cortometraje" : "https://s10.postimg.cc/qggvlxndl/shortfilm.png", "cast": "https://i.postimg.cc/qvfP5Xvt/cast.png",
"country": "https://s10.postimg.cc/yz0h81j15/pais.png", "lat": "https://i.postimg.cc/Gt8fMH0J/lat.png",
"crimen": "https://s14.postimg.cc/duzkipjq9/crime.png", "vose": "https://i.postimg.cc/kgmnbd8h/vose.png",
"d": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-D-black-icon.png", "accion": "https://s14.postimg.cc/sqy3q2aht/action.png",
"de la tv": "https://s10.postimg.cc/94gj0iwh5/image.png", "adolescente": "https://s10.postimg.cc/inq7u4p61/teens.png",
"deporte": "https://s14.postimg.cc/x1crlnnap/sports.png", "adultos": "https://s10.postimg.cc/s8raxc51l/adultos.png",
"destacadas": "https://s10.postimg.cc/yu40x8q2x/destacadas.png", "animacion": "https://s14.postimg.cc/vl193mupd/animation.png",
"documental": "https://s10.postimg.cc/68aygmmcp/documentales.png", "anime": "https://s10.postimg.cc/n9mc2ikzt/anime.png",
"documentaries": "https://s10.postimg.cc/68aygmmcp/documentales.png", "artes marciales": "https://s10.postimg.cc/4u1v51tzt/martial_arts.png",
"doramas":"https://s10.postimg.cc/h4dyr4nfd/doramas.png", "asiaticas": "https://i.postimg.cc/Xq0HXD5d/asiaticas.png",
"drama": "https://s14.postimg.cc/fzjxjtnxt/drama.png", "aventura": "https://s14.postimg.cc/ky7fy5he9/adventure.png",
"e": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-E-black-icon.png", "belico": "https://s14.postimg.cc/5e027lru9/war.png",
"erotica" : "https://s10.postimg.cc/dcbb9bfx5/erotic.png", "biografia": "https://s10.postimg.cc/jq0ecjxnt/biographic.png",
"espanolas" : "https://s10.postimg.cc/x1y6zikx5/spanish.png", "carreras": "https://s14.postimg.cc/yt5qgdr69/races.png",
"estrenos" : "https://s10.postimg.cc/sk8r9xdq1/estrenos.png", "ciencia ficcion": "https://s14.postimg.cc/8kulr2jy9/scifi.png",
"extranjera": "https://s10.postimg.cc/f44a4eerd/foreign.png", "cine negro": "https://s10.postimg.cc/6ym862qgp/noir.png",
"f": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-F-black-icon.png", "comedia": "https://s14.postimg.cc/9ym8moog1/comedy.png",
"familiar": "https://s14.postimg.cc/jj5v9ndsx/family.png", "cortometraje": "https://s10.postimg.cc/qggvlxndl/shortfilm.png",
"fantasia": "https://s14.postimg.cc/p7c60ksg1/fantasy.png", "crimen": "https://s14.postimg.cc/duzkipjq9/crime.png",
"fantastico" : "https://s10.postimg.cc/tedufx5eh/fantastic.png", "de la tv": "https://s10.postimg.cc/94gj0iwh5/image.png",
"favorites": "https://s10.postimg.cc/rtg147gih/favoritas.png", "deporte": "https://s14.postimg.cc/x1crlnnap/sports.png",
"g": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-G-black-icon.png", "destacadas": "https://s10.postimg.cc/yu40x8q2x/destacadas.png",
"genres": "https://s10.postimg.cc/6c4rx3x1l/generos.png", "documental": "https://s10.postimg.cc/68aygmmcp/documentales.png",
"h": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-H-black-icon.png", "doramas": "https://s10.postimg.cc/h4dyr4nfd/doramas.png",
"historica": "https://s10.postimg.cc/p1faxj6yh/historic.png", "drama": "https://s14.postimg.cc/fzjxjtnxt/drama.png",
"horror" : "https://s10.postimg.cc/8exqo6yih/horror2.png", "erotica": "https://s10.postimg.cc/dcbb9bfx5/erotic.png",
"hot": "https://s10.postimg.cc/yu40x8q2x/destacadas.png", "espanolas": "https://s10.postimg.cc/x1y6zikx5/spanish.png",
"i": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-I-black-icon.png", "estrenos": "https://s10.postimg.cc/sk8r9xdq1/estrenos.png",
"infantil": "https://s14.postimg.cc/4zyq842mp/childish.png", "extranjera": "https://s10.postimg.cc/f44a4eerd/foreign.png",
"intriga": "https://s14.postimg.cc/5qrgdimw1/intrigue.png", "familiar": "https://s14.postimg.cc/jj5v9ndsx/family.png",
"j": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-J-black-icon.png", "fantasia": "https://s14.postimg.cc/p7c60ksg1/fantasy.png",
"k": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-K-black-icon.png", "fantastico": "https://s10.postimg.cc/tedufx5eh/fantastic.png",
"l": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-L-black-icon.png", "historica": "https://s10.postimg.cc/p1faxj6yh/historic.png",
"language": "https://s10.postimg.cc/6wci189ft/idioma.png", "horror": "https://s10.postimg.cc/8exqo6yih/horror2.png",
"last": "https://s10.postimg.cc/i6ciuk0eh/ultimas.png", "infantil": "https://s14.postimg.cc/4zyq842mp/childish.png",
"lat": "https://i.postimg.cc/Gt8fMH0J/lat.png", "intriga": "https://s14.postimg.cc/5qrgdimw1/intrigue.png",
"latino" : "https://s10.postimg.cc/swip0b86h/latin.png", "latino": "https://s10.postimg.cc/swip0b86h/latin.png",
"m": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-M-black-icon.png", "mexicanas": "https://s10.postimg.cc/swip0b86h/latin.png",
"mexicanas" : "https://s10.postimg.cc/swip0b86h/latin.png", "misterio": "https://s14.postimg.cc/3m73cg8ep/mistery.png",
"misterio": "https://s14.postimg.cc/3m73cg8ep/mistery.png", "musical": "https://s10.postimg.cc/hy7fhtecp/musical.png",
"more voted": "https://s10.postimg.cc/lwns2d015/masvotadas.png", "peleas": "https://s10.postimg.cc/7a3ojbjwp/Fight.png",
"more watched": "https://s10.postimg.cc/c6orr5neh/masvistas.png", "policial": "https://s10.postimg.cc/wsw0wbgbd/cops.png",
"movies": "https://s10.postimg.cc/fxtqzdog9/peliculas.png", "recomendadas": "https://s10.postimg.cc/7xk1oqccp/recomendadas.png",
"musical": "https://s10.postimg.cc/hy7fhtecp/musical.png", "religion": "https://s10.postimg.cc/44j2skquh/religion.png",
"n": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-N-black-icon.png", "romance": "https://s10.postimg.cc/yn8vdll6x/romance.png",
"new episodes": "https://s10.postimg.cc/fu4iwpnqh/nuevoscapitulos.png", "romantica": "https://s14.postimg.cc/8xlzx7cht/romantic.png",
"newest": "http://icons.iconarchive.com/icons/laurent-baumann/creme/128/Location-News-icon.png", #"http://icons.iconarchive.com/icons/uiconstock/ios8-setting/128/news-icon.png", "suspenso": "https://s10.postimg.cc/7peybxdfd/suspense.png",
"nextpage": "http://icons.iconarchive.com/icons/custom-icon-design/pretty-office-5/256/navigate-right-icon.png", #"http://icons.iconarchive.com/icons/custom-icon-design/office/256/forward-icon.png", #"http://icons.iconarchive.com/icons/ahmadhania/spherical/128/forward-icon.png", "telenovelas": "https://i.postimg.cc/QCXZkyDM/telenovelas.png",
"o": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-O-black-icon.png", "terror": "https://s14.postimg.cc/thqtvl52p/horror.png",
"others": "http://icons.iconarchive.com/icons/limav/movie-genres-folder/128/Others-icon.png", "thriller": "https://s14.postimg.cc/uwsekl8td/thriller.png",
"p": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-P-black-icon.png", "western": "https://s10.postimg.cc/5wc1nokjt/western.png"
"peleas" : "https://s10.postimg.cc/7a3ojbjwp/Fight.png", }
"policial" : "https://s10.postimg.cc/wsw0wbgbd/cops.png",
"premieres": "https://s10.postimg.cc/sk8r9xdq1/estrenos.png",
"q": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-Q-black-icon.png",
"quality": "https://s10.postimg.cc/9bbojsbjd/calidad.png",
"r": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-R-black-icon.png",
"recents": "https://s10.postimg.cc/649u24kp5/recents.png",
"recomendadas": "https://s10.postimg.cc/7xk1oqccp/recomendadas.png",
"recomended": "https://s10.postimg.cc/7xk1oqccp/recomendadas.png",
"religion" : "https://s10.postimg.cc/44j2skquh/religion.png",
"romance" : "https://s10.postimg.cc/yn8vdll6x/romance.png",
"romantica": "https://s14.postimg.cc/8xlzx7cht/romantic.png",
"s": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-S-black-icon.png",
"search": "http://icons.iconarchive.com/icons/jamespeng/movie/256/database-icon.png",
"suspenso": "https://s10.postimg.cc/7peybxdfd/suspense.png",
"t": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-T-black-icon.png",
"telenovelas": "https://i.postimg.cc/QCXZkyDM/telenovelas.png",
"terror": "https://s14.postimg.cc/thqtvl52p/horror.png",
"thriller": "https://s14.postimg.cc/uwsekl8td/thriller.png",
"tvshows": "https://s10.postimg.cc/kxvslawe1/series.png",
"u": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-U-black-icon.png",
"ultimiarrivi" : "http://icons.iconarchive.com/icons/saki/snowish/128/Extras-internet-download-icon.png",
"updated" : "https://s10.postimg.cc/46m3h6h9l/updated.png",
"v": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-V-black-icon.png",
"vose": "https://i.postimg.cc/kgmnbd8h/vose.png",
"w": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-W-black-icon.png",
"western": "https://s10.postimg.cc/5wc1nokjt/western.png",
"x": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-X-black-icon.png",
"y": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-Y-black-icon.png",
"year": "https://s10.postimg.cc/atzrqg921/a_o.png",
"z": "http://icons.iconarchive.com/icons/hydrattz/multipurpose-alphabet/256/Letter-Z-black-icon.png"
}
def set_genre(string): def set_genre(string):
#logger.info() # logger.info()
genres_dict = {'accion':['azione'], genres_dict = {'accion': ['accion', 'action', 'accion y aventura', 'action & adventure'],
'adultos':['adulto','adulti'], 'adultos': ['adultos', 'adultos +', 'adulto'],
'animacion':['animazione'], 'animacion': ['animacion', 'animacion e infantil', 'dibujos animados'],
'adolescente':['adolescente', 'adolescenti'], 'adolescente': ['adolescente', 'adolescentes', 'adolescencia', 'adolecentes'],
'aventura':['avventura'], 'aventura': ['aventura', 'aventuras'],
'belico':['guerra','guerriglia'], 'belico': ['belico', 'belica', 'belicas', 'guerra', 'belico guerra'],
'biografia':['biografia', 'biografie', 'biografico'], 'biografia': ['biografia', 'biografias', 'biografica', 'biograficas', 'biografico'],
'ciencia ficcion':['ciencia ficcion', 'cienciaficcion', 'sci fi', 'c ficcion'], 'ciencia ficcion': ['ciencia ficcion', 'cienciaficcion', 'sci fi', 'c ficcion'],
'cine negro':['film noir'], 'cine negro': ['film noir', 'negro'],
'comedia':['commedia', 'commedie'], 'comedia': ['comedia', 'comedias'],
'cortometraje':['cortometraggio', 'corto', 'corti'], 'cortometraje': ['cortometraje', 'corto', 'cortos'],
'de la tv':['della tv', 'televisione', 'tv'], 'de la tv': ['de la tv', 'television', 'tv'],
'deporte':['deporte', 'deportes'], 'deporte': ['deporte', 'deportes'],
'destacadas':['destacada', 'destacadas'], 'destacadas': ['destacada', 'destacadas'],
'documental':['documentario', 'documentari'], 'documental': ['documental', 'documentales'],
'erotica':['erotica', 'erotica +', 'eroticas', 'eroticas +', 'erotico', 'erotico +'], 'erotica': ['erotica', 'erotica +', 'eroticas', 'eroticas +', 'erotico', 'erotico +'],
'estrenos':['estrenos', 'estrenos'], 'estrenos': ['estrenos', 'estrenos'],
'extranjera':['extrajera', 'extrajeras', 'foreign'], 'extranjera': ['extrajera', 'extrajeras', 'foreign'],
'familiar':['familiare', 'famiglia'], 'familiar': ['familiar', 'familia'],
'fantastico':['fantastico', 'fantastica', 'fantastici'], 'fantastico': ['fantastico', 'fantastica', 'fantasticas'],
'historica':['storico', 'storia'], 'historica': ['historica', 'historicas', 'historico', 'historia'],
'infantil':['bambini', 'infanzia'], 'infantil': ['infantil', 'kids'],
'musical':['musicale', 'musical', 'musica'], 'musical': ['musical', 'musicales', 'musica'],
'numbers': ['0','1','2','3','4','5','6','7','8','9'], 'policial': ['policial', 'policiaco', 'policiaca'],
'policial':['politico', 'politici', 'politica'], 'recomendadas': ['recomedada', 'recomendadas'],
'recomendadas':['raccomandato', 'raccomandati'], 'religion': ['religion', 'religiosa', 'religiosas'],
'religion':['religione', 'religioso', 'religiosa','religiosi'], 'romantica': ['romantica', 'romanticas', 'romantico'],
'romantica':['romantica', 'romantico', 'romantici'], 'suspenso': ['suspenso', 'suspense'],
'suspenso':['suspenso', 'suspense'], 'thriller': ['thriller', 'thrillers'],
'thriller':['thriller', 'thrillers'], 'western': ['western', 'westerns', 'oeste western']
'western':['western', 'westerns']
} }
string = re.sub(r'peliculas de |pelicula de la |peli |cine ','', string) string = re.sub(r'peliculas de |pelicula de la |peli |cine ', '', string)
for genre, variants in genres_dict.items(): for genre, variants in list(genres_dict.items()):
if string in variants: if string in variants:
string = genre string = genre
return string return string
def remove_format(string): def remove_format(string):
#logger.info() # logger.info()
#logger.debug('entra en remove: %s' % string) # logger.debug('entra en remove: %s' % string)
string = string.rstrip() string = string.rstrip()
string = re.sub(r'(\[|\[\/)(?:color|COLOR|b|B|i|I).*?\]|\[|\]|\(|\)|\:|\.', '', string) string = re.sub(r'(\[|\[\/)(?:color|COLOR|b|B|i|I).*?\]|\[|\]|\(|\)|\:|\.', '', string)
#logger.debug('sale de remove: %s' % string) # logger.debug('sale de remove: %s' % string)
return string return string
def normalize(string): def normalize(string):
string = string.decode('utf-8') if not PY3 and isinstance(string, str):
string = string.decode('utf-8')
normal = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn')) normal = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
return normal return normal
def simplify(string): def simplify(string):
# logger.info()
#logger.info() # logger.debug('entra en simplify: %s'%string)
#logger.debug('entra en simplify: %s'%string)
string = remove_format(string) string = remove_format(string)
string = string.replace('-',' ').replace('_',' ') string = string.replace('-', ' ').replace('_', ' ')
string = re.sub(r'\d+','', string) string = re.sub(r'\d+', '', string)
string = string.strip() string = string.strip()
notilde = normalize(string) notilde = normalize(string)
@@ -197,12 +175,13 @@ def simplify(string):
except: except:
pass pass
string = string.lower() string = string.lower()
#logger.debug('sale de simplify: %s' % string) # logger.debug('sale de simplify: %s' % string)
return string return string
def add_languages(title, languages): def add_languages(title, languages):
#logger.info() # logger.info()
if isinstance(languages, list): if isinstance(languages, list):
for language in languages: for language in languages:
@@ -211,14 +190,55 @@ def add_languages(title, languages):
title = '%s %s' % (title, set_color(languages, languages)) title = '%s %s' % (title, set_color(languages, languages))
return title return title
def add_info_plot(plot, languages, quality):
# logger.info()
last = '[/I][/B]\n'
if languages:
l_part = '[COLOR yellowgreen][B][I]Idiomas:[/COLOR] '
mid = ''
if isinstance(languages, list):
for language in languages:
mid += '%s ' % (set_color(language, language))
else:
mid = '%s ' % (set_color(languages, languages))
p_lang = '%s%s%s' % (l_part, mid, last)
if quality:
q_part = '[COLOR yellowgreen][B][I]Calidad:[/COLOR] '
p_quality = '%s%s%s' % (q_part, quality, last)
if languages and quality:
plot_ = '%s%s\n%s' % (p_lang, p_quality, plot)
elif languages:
plot_ = '%s\n%s' % (p_lang, plot)
elif quality:
plot_ = '%s\n%s' % (p_quality, plot)
else:
plot_ = plot
return plot_
def set_color(title, category): def set_color(title, category):
#logger.info() # logger.info()
from core import jsontools
styles_path = os.path.join(config.get_runtime_path(), 'resources', 'color_styles.json')
preset = config.get_setting("preset_style", default="Estilo 1")
color_setting = jsontools.load((open(styles_path, "r").read()))[preset]
color_scheme = {'otro': 'white', 'dual': 'white'} color_scheme = {'otro': 'white', 'dual': 'white'}
#logger.debug('category antes de remove: %s' % category) # logger.debug('category antes de remove: %s' % category)
category = remove_format(category).lower() category = remove_format(category).lower()
#logger.debug('category despues de remove: %s' % category) # logger.debug('category despues de remove: %s' % category)
# Lista de elementos posibles en el titulo # Lista de elementos posibles en el titulo
color_list = ['movie', 'tvshow', 'year', 'rating_1', 'rating_2', 'rating_3', 'quality', 'cast', 'lat', 'vose', color_list = ['movie', 'tvshow', 'year', 'rating_1', 'rating_2', 'rating_3', 'quality', 'cast', 'lat', 'vose',
'vos', 'vo', 'server', 'library', 'update', 'no_update'] 'vos', 'vo', 'server', 'library', 'update', 'no_update']
@@ -234,46 +254,45 @@ def set_color(title, category):
if custom_colors: if custom_colors:
color_scheme[element] = remove_format(config.get_setting('%s_color' % element)) color_scheme[element] = remove_format(config.get_setting('%s_color' % element))
else: else:
color_scheme[element] = 'white' color_scheme[element] = remove_format(color_setting.get(element, 'white'))
# color_scheme[element] = 'white'
if category in ['update', 'no_update']: if category in ['update', 'no_update']:
#logger.debug('title antes de updates: %s' % title) # logger.debug('title antes de updates: %s' % title)
title= re.sub(r'\[COLOR .*?\]','[COLOR %s]' % color_scheme[category],title) title = re.sub(r'\[COLOR .*?\]', '[COLOR %s]' % color_scheme[category], title)
else: else:
if category not in ['movie', 'tvshow', 'library', 'otro']: if category not in ['movie', 'tvshow', 'library', 'otro']:
title = "[COLOR %s][%s][/COLOR]"%(color_scheme[category], title) title = "[COLOR %s][%s][/COLOR]" % (color_scheme[category], title)
else: else:
title = "[COLOR %s]%s[/COLOR]" % (color_scheme[category], title) title = "[COLOR %s]%s[/COLOR]" % (color_scheme[category], title)
return title return title
def set_lang(language):
#logger.info()
cast =['castellano','espanol','cast','esp','espaol', 'es','zc', 'spa', 'spanish', 'vc'] def set_lang(language):
ita =['italiano','italian','ita','it'] # logger.info()
lat=['latino','lat','la', 'espanol latino', 'espaol latino', 'zl', 'mx', 'co', 'vl']
vose=['subtitulado','subtitulada','sub','sub espanol','vose','espsub','su','subs castellano', cast = ['castellano', 'español', 'espanol', 'cast', 'esp', 'espaol', 'es', 'zc', 'spa', 'spanish', 'vc']
'sub: español', 'vs', 'zs', 'vs', 'english-spanish subs', 'ingles sub espanol'] ita = ['italiano', 'italian', 'ita', 'it']
sub_ita=['sottotitolato','sottotitolata','sub','sub ita','subs italiano', lat = ['latino', 'lat', 'la', 'español latino', 'espanol latino', 'espaol latino', 'zl', 'mx', 'co', 'vl']
'sub: italiano', 'inglese sottotitolato'] vose = ['subtitulado', 'subtitulada', 'sub', 'sub espanol', 'vose', 'espsub', 'su', 'subs castellano',
vos=['vos', 'sub ingles', 'engsub','ingles subtitulado', 'sub: ingles'] 'sub: español', 'vs', 'zs', 'vs', 'english-spanish subs', 'ingles sub espanol', 'ingles sub español']
vo=['ingles', 'en','vo', 'ovos', 'eng','v.o', 'english'] vos = ['vos', 'sub ingles', 'engsub', 'vosi', 'ingles subtitulado', 'sub: ingles']
dual=['dual'] vo = ['ingles', 'en', 'vo', 'ovos', 'eng', 'v.o', 'english']
dual = ['dual']
language = scrapertools.decodeHtmlentities(language) language = scrapertools.decodeHtmlentities(language)
old_lang = language old_lang = language
language = simplify(language) language = simplify(language)
#logger.debug('language before simplify: %s' % language) # logger.debug('language before simplify: %s' % language)
#logger.debug('old language: %s' % old_lang) # logger.debug('old language: %s' % old_lang)
if language in cast: if language in cast:
language = 'cast' language = 'cast'
elif language in lat: elif language in lat:
language = 'lat' language = 'lat'
elif language in ita: elif language in ita:
language = 'ita' language = 'ita'
elif language in sub_ita:
language = 'sub-ita'
elif language in vose: elif language in vose:
language = 'vose' language = 'vose'
elif language in vos: elif language in vos:
@@ -285,67 +304,67 @@ def set_lang(language):
else: else:
language = 'otro' language = 'otro'
#logger.debug('language after simplify: %s' % language) # logger.debug('language after simplify: %s' % language)
return language return language
def title_format(item): def title_format(item):
#logger.info() # logger.info()
lang = False lang = False
valid = True valid = True
language_color = 'otro' language_color = 'otro'
simple_language = ''
#logger.debug('item.title antes de formatear: %s' % item.title.lower()) # logger.debug('item.title antes de formatear: %s' % item.title.lower())
# TODO se deberia quitar cualquier elemento que no sea un enlace de la lista de findvideos para quitar esto # TODO se deberia quitar cualquier elemento que no sea un enlace de la lista de findvideos para quitar esto
#Palabras "prohibidas" en los titulos (cualquier titulo que contengas estas no se procesara en unify) # Palabras "prohibidas" en los titulos (cualquier titulo que contengas estas no se procesara en unify)
excluded_words = ['online', 'descarga', 'downloads', 'trailer', 'videoteca', 'gb', 'autoplay'] excluded_words = ['online', 'descarga', 'downloads', 'trailer', 'videoteca', 'gb', 'autoplay']
# Actions excluidos, (se define canal y action) los titulos que contengan ambos valores no se procesaran en unify # Actions excluidos, (se define canal y action) los titulos que contengan ambos valores no se procesaran en unify
excluded_actions = [('videolibrary','get_episodes')] excluded_actions = [('videolibrary', 'get_episodes')]
# Verifica si hay marca de visto de trakt # Verifica el item sea valido para ser formateado por unify
visto = False
#logger.debug('titlo con visto? %s' % item.title)
if '[[I]v[/I]]' in item.title or '[COLOR limegreen][v][/COLOR]' in item.title:
visto = True
# Se elimina cualquier formato previo en el titulo
if item.action != '' and item.action !='mainlist':
item.title = remove_format(item.title)
#logger.debug('visto? %s' % visto)
# Evita que aparezcan los idiomas en los mainlist de cada canal
if item.action == 'mainlist':
item.language =''
info = item.infoLabels
#logger.debug('item antes de formatear: %s'%item)
if hasattr(item,'text_color'):
item.text_color=''
#Verifica el item sea valido para ser formateado por unify
if item.channel == 'trailertools' or (item.channel.lower(), item.action.lower()) in excluded_actions or \ if item.channel == 'trailertools' or (item.channel.lower(), item.action.lower()) in excluded_actions or \
item.action=='': item.action == '':
valid = False valid = False
else: else:
for word in excluded_words: for word in excluded_words:
if word in item.title.lower(): if word in item.title.lower():
valid = False valid = False
break break
if not valid:
return item
if valid and item.unify!=False: # Verifica si hay marca de visto de trakt
visto = False
# logger.debug('titlo con visto? %s' % item.title)
if '[[I]v[/I]]' in item.title or '[COLOR limegreen][v][/COLOR]' in item.title:
visto = True
# Se elimina cualquier formato previo en el titulo
if item.action != '' and item.action != 'mainlist' and item.unify:
item.title = remove_format(item.title)
# logger.debug('visto? %s' % visto)
# Evita que aparezcan los idiomas en los mainlist de cada canal
if item.action == 'mainlist':
item.language = ''
info = item.infoLabels
# logger.debug('item antes de formatear: %s'%item)
if hasattr(item, 'text_color'):
item.text_color = ''
if valid and item.unify != False:
# Formamos el titulo para serie, se debe definir contentSerieName # Formamos el titulo para serie, se debe definir contentSerieName
# o show en el item para que esto funcione. # o show en el item para que esto funcione.
@@ -354,25 +373,26 @@ def title_format(item):
# Si se tiene la informacion en infolabels se utiliza # Si se tiene la informacion en infolabels se utiliza
if item.contentType == 'episode' and info['episode'] != '': if item.contentType == 'episode' and info['episode'] != '':
if info['title'] == '': if info['title'] == '':
info['title'] = '%s - Episodio %s'% (info['tvshowtitle'], info['episode']) info['title'] = '%s - Episodio %s' % (info['tvshowtitle'], info['episode'])
elif 'Episode' in info['title']: elif 'Episode' in info['title']:
episode = info['title'].lower().replace('episode', 'episodio') episode = info['title'].lower().replace('episode', 'episodio')
info['title'] = '%s - %s' % (info['tvshowtitle'], episode.capitalize()) info['title'] = '%s - %s' % (info['tvshowtitle'], episode.capitalize())
elif info['episodio_titulo']!='': elif info['episodio_titulo'] != '':
#logger.debug('info[episode_titulo]: %s' % info['episodio_titulo']) # logger.debug('info[episode_titulo]: %s' % info['episodio_titulo'])
if 'episode' in info['episodio_titulo'].lower(): if 'episode' in info['episodio_titulo'].lower():
episode = info['episodio_titulo'].lower().replace('episode', 'episodio') episode = info['episodio_titulo'].lower().replace('episode', 'episodio')
item.title = '%sx%s - %s' % (info['season'],info['episode'], episode.capitalize()) item.title = '%sx%s - %s' % (info['season'], info['episode'], episode.capitalize())
else: else:
item.title = '%sx%s - %s' % (info['season'], info['episode'], info['episodio_titulo'].capitalize()) item.title = '%sx%s - %s' % (
info['season'], info['episode'], info['episodio_titulo'].capitalize())
else: else:
item.title = '%sx%s - %s' % (info['season'],info['episode'], info['title']) item.title = '%sx%s - %s' % (info['season'], info['episode'], info['title'])
item.title = set_color(item.title, 'tvshow') item.title = set_color(item.title, 'tvshow')
else: else:
# En caso contrario se utiliza el titulo proporcionado por el canal # En caso contrario se utiliza el titulo proporcionado por el canal
#logger.debug ('color_scheme[tvshow]: %s' % color_scheme['tvshow']) # logger.debug ('color_scheme[tvshow]: %s' % color_scheme['tvshow'])
item.title = '%s' % set_color(item.title, 'tvshow') item.title = '%s' % set_color(item.title, 'tvshow')
elif item.contentTitle: elif item.contentTitle:
@@ -386,27 +406,27 @@ def title_format(item):
item.title = '%s [V.Extend.]' % set_color(item.contentTitle, 'movie') item.title = '%s [V.Extend.]' % set_color(item.contentTitle, 'movie')
else: else:
item.title = '%s' % set_color(item.contentTitle, 'movie') item.title = '%s' % set_color(item.contentTitle, 'movie')
if item.contentType=='movie': if item.contentType == 'movie':
if item.context: if item.context:
if isinstance(item.context, list): if isinstance(item.context, list):
item.context.append('Buscar esta pelicula en otros canales') item.context.append('Buscar esta pelicula en otros canales')
if 'Novedades' in item.category and item.from_channel=='news': if ('Novedades' in item.category and item.from_channel == 'news'):
#logger.debug('novedades') # logger.debug('novedades')
item.title = '%s [%s]'%(item.title, item.channel) item.title = '%s [%s]' % (item.title, item.channel)
# Verificamos si item.language es una lista, si lo es se toma # Verificamos si item.language es una lista, si lo es se toma
# cada valor y se normaliza formado una nueva lista # cada valor y se normaliza formado una nueva lista
if hasattr(item,'language') and item.language !='': if hasattr(item, 'language') and item.language != '':
#logger.debug('tiene language: %s'%item.language) # logger.debug('tiene language: %s'%item.language)
if isinstance(item.language, list): if isinstance(item.language, list):
language_list =[] language_list = []
for language in item.language: for language in item.language:
if language != '': if language != '':
lang = True lang = True
language_list.append(set_lang(remove_format(language)).upper()) language_list.append(set_lang(remove_format(language)).upper())
#logger.debug('language_list: %s' % language_list) # logger.debug('language_list: %s' % language_list)
simple_language = language_list simple_language = language_list
else: else:
# Si item.language es un string se normaliza # Si item.language es un string se normaliza
@@ -416,19 +436,19 @@ def title_format(item):
else: else:
simple_language = '' simple_language = ''
#item.language = simple_language # item.language = simple_language
# Damos formato al año si existiera y lo agregamos # Damos formato al año si existiera y lo agregamos
# al titulo excepto que sea un episodio # al titulo excepto que sea un episodio
if info and info.get("year", "") not in [""," "] and item.contentType != 'episode' and not info['season']: if info and info.get("year", "") not in ["", " "] and item.contentType != 'episode' and not info['season']:
try: try:
year = '%s' % set_color(info['year'], 'year') year = '%s' % set_color(info['year'], 'year')
item.title = item.title = '%s %s' % (item.title, year) item.title = item.title = '%s %s' % (item.title, year)
except: except:
logger.debug('infoLabels: %s'%info) logger.debug('infoLabels: %s' % info)
# Damos formato al puntaje si existiera y lo agregamos al titulo # Damos formato al puntaje si existiera y lo agregamos al titulo
if info and info['rating'] and info['rating']!='0.0' and not info['season']: if info and info['rating'] and info['rating'] != '0.0' and not info['season']:
# Se normaliza el puntaje del rating # Se normaliza el puntaje del rating
@@ -454,13 +474,29 @@ def title_format(item):
# Damos formato a la calidad si existiera y lo agregamos al titulo # Damos formato a la calidad si existiera y lo agregamos al titulo
if item.quality and isinstance(item.quality, str): if item.quality and isinstance(item.quality, str):
quality = item.quality.strip() quality = item.quality.strip()
item.title = '%s %s' % (item.title, set_color(quality, 'quality'))
else: else:
quality = '' quality = ''
# Damos formato al idioma si existiera y lo agregamos al titulo # Damos formato al idioma-calidad si existieran y los agregamos al plot
if lang: quality_ = set_color(quality, 'quality')
item.title = add_languages(item.title, simple_language)
if (lang or quality) and item.action == "play":
if hasattr(item, "clean_plot"):
item.contentPlot = item.clear_plot
if lang: item.title = add_languages(item.title, simple_language)
if quality: item.title = '%s %s' % (item.title, quality_)
elif (lang or quality) and item.action != "play":
if item.contentPlot:
item.clean_plot = item.contentPlot
plot_ = add_info_plot(item.contentPlot, simple_language, quality_)
item.contentPlot = plot_
else:
item.clean_plot = None
plot_ = add_info_plot('', simple_language, quality_)
item.contentPlot = plot_
# Para las busquedas por canal # Para las busquedas por canal
if item.from_channel != '': if item.from_channel != '':
@@ -469,17 +505,16 @@ def title_format(item):
logger.debug(channel_parameters) logger.debug(channel_parameters)
item.title = '%s [%s]' % (item.title, channel_parameters['title']) item.title = '%s [%s]' % (item.title, channel_parameters['title'])
# Formato para actualizaciones de series en la videoteca sobreescribe los colores anteriores # Formato para actualizaciones de series en la videoteca sobreescribe los colores anteriores
if item.channel=='videolibrary' and item.context!='': if item.channel == 'videolibrary' and item.context != '':
if item.action=='get_seasons': if item.action == 'get_seasons':
if 'Desactivar' in item.context[1]['title']: if 'Desactivar' in item.context[1]['title']:
item.title= '%s' % (set_color(item.title, 'update')) item.title = '%s' % (set_color(item.title, 'update'))
if 'Activar' in item.context[1]['title']: if 'Activar' in item.context[1]['title']:
item.title= '%s' % (set_color(item.title, 'no_update')) item.title = '%s' % (set_color(item.title, 'no_update'))
#logger.debug('Despues del formato: %s' % item) # logger.debug('Despues del formato: %s' % item)
# Damos formato al servidor si existiera # Damos formato al servidor si existiera
if item.server: if item.server:
server = '%s' % set_color(item.server.strip().capitalize(), 'server') server = '%s' % set_color(item.server.strip().capitalize(), 'server')
@@ -487,18 +522,28 @@ def title_format(item):
# Compureba si estamos en findvideos, y si hay server, si es asi no se muestra el # 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. # titulo sino el server, en caso contrario se muestra el titulo normalmente.
#logger.debug('item.title antes de server: %s'%item.title) # logger.debug('item.title antes de server: %s'%item.title)
if item.action != 'play' and item.server: if item.action != 'play' and item.server:
item.title ='%s %s'%(item.title, server.strip()) item.title = '%s %s' % (item.title, server.strip())
elif item.action == 'play' and item.server: elif item.action == 'play' and item.server:
if hasattr(item, "clean_plot"):
item.contentPlot = item.clean_plot
if item.quality == 'default': if item.quality == 'default':
quality = '' quality = ''
#logger.debug('language_color: %s'%language_color) # logger.debug('language_color: %s'%language_color)
item.title = '%s %s' % (server, set_color(quality,'quality')) item.title = '%s %s' % (server, set_color(quality, 'quality'))
if lang: if lang:
item.title = add_languages(item.title, simple_language) item.title = add_languages(item.title, simple_language)
#logger.debug('item.title: %s' % item.title) # logger.debug('item.title: %s' % item.title)
# Torrent_info
if item.server == 'torrent' and item.torrent_info != '':
item.title = '%s [%s]' % (item.title, item.torrent_info)
if item.channel == 'videolibrary':
item.title += ' [%s]' % item.contentChannel
# si hay verificacion de enlaces # si hay verificacion de enlaces
if item.alive != '': if item.alive != '':
if item.alive.lower() == 'no': if item.alive.lower() == 'no':
@@ -507,29 +552,33 @@ def title_format(item):
item.title = '[[COLOR yellow][B]?[/B][/COLOR]] %s' % item.title item.title = '[[COLOR yellow][B]?[/B][/COLOR]] %s' % item.title
else: else:
item.title = '%s' % item.title item.title = '%s' % item.title
#logger.debug('item.title despues de server: %s' % item.title)
# logger.debug('item.title despues de server: %s' % item.title)
elif 'library' in item.action: elif 'library' in item.action:
item.title = '%s' % set_color(item.title, 'library') item.title = '%s' % set_color(item.title, 'library')
elif item.action == '' and item.title !='': elif item.action == '' and item.title != '':
item.title='**- %s -**'%item.title item.title = '**- %s -**' % item.title
else: elif item.unify:
item.title = '%s' % set_color(item.title, 'otro') item.title = '%s' % set_color(item.title, 'otro')
#logger.debug('antes de salir %s' % item.title) # logger.debug('antes de salir %s' % item.title)
if visto: if visto:
try: try:
check = u'\u221a' check = u'\u221a'
title = '[B][COLOR limegreen][%s][/COLOR][/B] %s' % (check, item.title.decode('utf-8')) title = '[B][COLOR limegreen][%s][/COLOR][/B] %s' % (check, item.title.decode('utf-8'))
item.title = title.encode('utf-8') item.title = title.encode('utf-8')
if PY3: item.title = item.title.decode('utf-8')
except: except:
check = 'v' check = 'v'
title = '[B][COLOR limegreen][%s][/COLOR][/B] %s' % (check, item.title.decode('utf-8')) title = '[B][COLOR limegreen][%s][/COLOR][/B] %s' % (check, item.title.decode('utf-8'))
item.title = title.encode('utf-8') item.title = title.encode('utf-8')
if PY3: item.title = item.title.decode('utf-8')
return item return item
def thumbnail_type(item): def thumbnail_type(item):
#logger.info() # logger.info()
# Se comprueba que tipo de thumbnail se utilizara en findvideos, # Se comprueba que tipo de thumbnail se utilizara en findvideos,
# Poster o Logo del servidor # Poster o Logo del servidor
@@ -539,7 +588,7 @@ def thumbnail_type(item):
item.contentThumbnail = item.thumbnail item.contentThumbnail = item.thumbnail
if info: if info:
if info['thumbnail'] !='': if info['thumbnail'] != '':
item.contentThumbnail = info['thumbnail'] item.contentThumbnail = info['thumbnail']
if item.action == 'play': if item.action == 'play':
@@ -548,7 +597,7 @@ def thumbnail_type(item):
item.thumbnail = info['thumbnail'] item.thumbnail = info['thumbnail']
elif thumb_type == 1: elif thumb_type == 1:
from core.servertools import get_server_parameters from core.servertools import get_server_parameters
#logger.debug('item.server: %s'%item.server) # logger.debug('item.server: %s'%item.server)
server_parameters = get_server_parameters(item.server.lower()) server_parameters = get_server_parameters(item.server.lower())
item.thumbnail = server_parameters.get("thumbnail", item.contentThumbnail) item.thumbnail = server_parameters.get("thumbnail", item.contentThumbnail)
@@ -574,7 +623,7 @@ def check_rating(rating):
try: try:
# convertimos los deciamles p.e. 7.1 # convertimos los deciamles p.e. 7.1
return "%.1f" % round(_rating, 1) return "%.1f" % round(_rating, 1)
except Exception, ex_dl: except Exception as ex_dl:
template = "An exception of type %s occured. Arguments:\n%r" template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex_dl).__name__, ex_dl.args) message = template % (type(ex_dl).__name__, ex_dl.args)
logger.error(message) logger.error(message)
@@ -601,18 +650,18 @@ def check_rating(rating):
def convert_float(_rating): def convert_float(_rating):
try: try:
return float(_rating) return float(_rating)
except ValueError, ex_ve: except ValueError as ex_ve:
template = "An exception of type %s occured. Arguments:\n%r" template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex_ve).__name__, ex_ve.args) message = template % (type(ex_ve).__name__, ex_ve.args)
logger.error(message) logger.error(message)
return None return None
if type(rating) != float: if not isinstance(rating, float):
# logger.debug("no soy float") # logger.debug("no soy float")
if type(rating) == int: if isinstance(rating, int):
# logger.debug("soy int") # logger.debug("soy int")
rating = convert_float(rating) rating = convert_float(rating)
elif type(rating) == str: elif isinstance(rating, str):
# logger.debug("soy str") # logger.debug("soy str")
rating = rating.replace("<", "") rating = rating.replace("<", "")
+22 -13
View File
@@ -2,7 +2,7 @@
import io import io
import os import os
import shutil import shutil
from cStringIO import StringIO from lib.six import BytesIO
from core import filetools from core import filetools
from platformcode import logger, platformtools from platformcode import logger, platformtools
@@ -15,7 +15,9 @@ try:
import urllib.request as urllib import urllib.request as urllib
except ImportError: except ImportError:
import urllib import urllib
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
addon = xbmcaddon.Addon('plugin.video.kod') addon = xbmcaddon.Addon('plugin.video.kod')
_hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@.*") _hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@.*")
@@ -33,7 +35,7 @@ def loadCommits(page=1):
apiLink = 'https://api.github.com/repos/' + user + '/' + repo + '/commits?sha=' + branch + "&page=" + str(page) apiLink = 'https://api.github.com/repos/' + user + '/' + repo + '/commits?sha=' + branch + "&page=" + str(page)
logger.info(apiLink) logger.info(apiLink)
# riprova ogni secondo finchè non riesce (ad esempio per mancanza di connessione) # riprova ogni secondo finchè non riesce (ad esempio per mancanza di connessione)
for n in xrange(10): for n in range(10):
try: try:
commitsLink = urllib.urlopen(apiLink).read() commitsLink = urllib.urlopen(apiLink).read()
ret = json.loads(commitsLink) ret = json.loads(commitsLink)
@@ -112,24 +114,26 @@ def check(background=False):
if 'patch' in file: if 'patch' in file:
text = "" text = ""
try: try:
localFile = open(addonDir + file["filename"], 'r+') localFile = io.open(addonDir + file["filename"], 'r+', encoding="utf8")
text = localFile.read() text = localFile.read()
if not PY3:
text = text.decode('utf-8')
except IOError: # nuovo file except IOError: # nuovo file
# crea le cartelle se non esistono # crea le cartelle se non esistono
dirname = os.path.dirname(addonDir + file["filename"]) dirname = os.path.dirname(addonDir + file["filename"])
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.makedirs(dirname) os.makedirs(dirname)
localFile = open(addonDir + file["filename"], 'w') localFile = io.open(addonDir + file["filename"], 'w', encoding="utf8")
patched = apply_patch(text, (file['patch']+'\n').encode('utf-8')) patched = apply_patch(text, (file['patch']+'\n').encode('utf-8'))
if patched != text: # non eseguo se già applicata (es. scaricato zip da github) if patched != text: # non eseguo se già applicata (es. scaricato zip da github)
alreadyApplied = False
if getShaStr(patched) == file['sha']: if getShaStr(patched) == file['sha']:
localFile.seek(0) localFile.seek(0)
localFile.truncate() localFile.truncate()
localFile.writelines(patched) localFile.writelines(patched)
localFile.close() localFile.close()
alreadyApplied = False
else: # nel caso ci siano stati problemi else: # nel caso ci siano stati problemi
logger.info('lo sha non corrisponde, scarico il file') logger.info('lo sha non corrisponde, scarico il file')
localFile.close() localFile.close()
@@ -250,7 +254,7 @@ def apply_patch(s,patch,revert=False):
def getSha(path): def getSha(path):
try: try:
f = open(path, 'rb') f = io.open(path, 'rb', encoding="utf8")
except: except:
return '' return ''
size = len(f.read()) size = len(f.read())
@@ -259,7 +263,11 @@ def getSha(path):
def getShaStr(str): def getShaStr(str):
return githash.blob_hash(StringIO(str), len(str)).hexdigest() if PY3:
return githash.blob_hash(BytesIO(str.encode('utf-8')), len(str.encode('utf-8'))).hexdigest()
else:
return githash.blob_hash(BytesIO(str), len(str)).hexdigest()
def updateFromZip(message='Installazione in corso...'): def updateFromZip(message='Installazione in corso...'):
@@ -267,7 +275,7 @@ def updateFromZip(message='Installazione in corso...'):
dp.update(0) dp.update(0)
remotefilename = 'https://github.com/' + user + "/" + repo + "/archive/" + branch + ".zip" remotefilename = 'https://github.com/' + user + "/" + repo + "/archive/" + branch + ".zip"
localfilename = os.path.join(xbmc.translatePath("special://home/addons/"), "plugin.video.kod.update.zip").encode('utf-8') localfilename = filetools.join(xbmc.translatePath("special://home/addons/"), "plugin.video.kod.update.zip")
destpathname = xbmc.translatePath("special://home/addons/") destpathname = xbmc.translatePath("special://home/addons/")
logger.info("remotefilename=%s" % remotefilename) logger.info("remotefilename=%s" % remotefilename)
@@ -306,7 +314,7 @@ def updateFromZip(message='Installazione in corso...'):
for member in zip.infolist(): for member in zip.infolist():
zip.extract(member, destpathname) zip.extract(member, destpathname)
cur_size += member.file_size cur_size += member.file_size
dp.update(80 + cur_size * 19 / size) dp.update(int(90 + cur_size * 9 / size))
except Exception as e: except Exception as e:
logger.info('Non sono riuscito ad estrarre il file zip') logger.info('Non sono riuscito ad estrarre il file zip')
@@ -417,13 +425,14 @@ def fOpen(file, mode = 'r'):
logger.info('android, uso FileIO per leggere') logger.info('android, uso FileIO per leggere')
return io.FileIO(file, mode) return io.FileIO(file, mode)
else: else:
return open(file, mode) return io.open(file, mode)
def _pbhook(numblocks, blocksize, filesize, url, dp): def _pbhook(numblocks, blocksize, filesize, url, dp):
try: try:
percent = min((numblocks*blocksize*90)/filesize, 100) percent = min((numblocks*blocksize*90)/filesize, 100)
dp.update(percent) dp.update(int(percent))
except: except Exception as e:
logger.error(e)
percent = 90 percent = 90
dp.update(percent) dp.update(percent)
+39 -31
View File
@@ -3,13 +3,21 @@
# XBMC Config Menu # XBMC Config Menu
# ------------------------------------------------------------ # ------------------------------------------------------------
from __future__ import division
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
from builtins import range
from past.utils import old_div
import inspect import inspect
import os import os
import xbmcgui import xbmcgui
from core import channeltools from core import channeltools
from core import servertools from core import servertools, scrapertools
from platformcode import config, logger from platformcode import config, logger
@@ -161,7 +169,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
self.callback = callback self.callback = callback
self.item = item self.item = item
if type(custom_button) == dict: if isinstance(custom_button, dict):
self.custom_button = {} self.custom_button = {}
self.custom_button["label"] = custom_button.get("label", "") self.custom_button["label"] = custom_button.get("label", "")
self.custom_button["function"] = custom_button.get("function", "") self.custom_button["function"] = custom_button.get("function", "")
@@ -245,12 +253,16 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
def evaluate(self, index, cond): def evaluate(self, index, cond):
import re import re
ok = False
# Si la condicion es True o False, no hay mas que evaluar, ese es el valor # Si la condicion es True o False, no hay mas que evaluar, ese es el valor
if type(cond) == bool: if isinstance(cond, bool):
return cond return cond
# Obtenemos las condiciones # Obtenemos las condiciones
conditions = re.compile("(!?eq|!?gt|!?lt)?\(([^,]+),[\"|']?([^)|'|\"]*)['|\"]?\)[ ]*([+||])?").findall(cond) # conditions = re.compile("(!?eq|!?gt|!?lt)?\(([^,]+),[\"|']?([^)|'|\"]*)['|\"]?\)[ ]*([+||])?").findall(cond)
conditions = re.compile(r'''(!?eq|!?gt|!?lt)?\s*\(\s*([^, ]+)\s*,\s*["']?([^"'\)]+)["']?\)([+|])?''').findall(cond)
# conditions = scrapertools.find_multiple_matches(cond, r"(!?eq|!?gt|!?lt)?\(([^,]+),[\"|']?([^)|'|\"]*)['|\"]?\)[ ]*([+||])?")
for operator, id, value, next in conditions: for operator, id, value, next in conditions:
# El id tiene que ser un numero, sino, no es valido y devuelve False # El id tiene que ser un numero, sino, no es valido y devuelve False
try: try:
@@ -294,9 +306,9 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
pass pass
# valor bool # valor bool
if value.lower() == "true": if not isinstance(value, int) and value.lower() == "true":
value = True value = True
elif value.lower() == "false": elif not isinstance(value, int) and value.lower() == "false":
value = False value = False
# operacion "eq" "igual a" # operacion "eq" "igual a"
@@ -515,7 +527,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
continue continue
if c["type"] == "list" and "lvalues" not in c: if c["type"] == "list" and "lvalues" not in c:
continue continue
if c["type"] == "list" and not type(c["lvalues"]) == list: if c["type"] == "list" and not isinstance(c["lvalues"], list):
continue continue
if c["type"] == "list" and not len(c["lvalues"]) > 0: if c["type"] == "list" and not len(c["lvalues"]) > 0:
continue continue
@@ -590,7 +602,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
self.check_ok(self.values) self.check_ok(self.values)
def dispose_controls(self, index, focus=False, force=False): def dispose_controls(self, index, focus=False, force=False):
show_controls = self.controls_height / self.height_control - 1 show_controls = old_div(self.controls_height, self.height_control) - 1
visible_count = 0 visible_count = 0
@@ -609,7 +621,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
if index < 0: index = 0 if index < 0: index = 0
new_index = index new_index = index
if self.index <> new_index or force: if self.index != new_index or force:
for x, c in enumerate(self.visible_controls): for x, c in enumerate(self.visible_controls):
if x < new_index or visible_count > show_controls or not c["show"]: if x < new_index or visible_count > show_controls or not c["show"]:
self.set_visible(c, False) self.set_visible(c, False)
@@ -693,7 +705,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
else: else:
self.return_value = getattr(cb_channel, self.custom_button['function'])(self.item, self.values) self.return_value = getattr(cb_channel, self.custom_button['function'])(self.item, self.values)
if not self.custom_button["close"]: if not self.custom_button["close"]:
if isinstance(self.return_value, dict) and self.return_value.has_key("label"): if isinstance(self.return_value, dict) and "label" in self.return_value:
self.getControl(10006).setLabel(self.return_value['label']) self.getControl(10006).setLabel(self.return_value['label'])
for c in self.list_controls: for c in self.list_controls:
@@ -757,23 +769,23 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
# Controles de ajustes, si se cambia el valor de un ajuste, cambiamos el valor guardado en el diccionario de # Controles de ajustes, si se cambia el valor de un ajuste, cambiamos el valor guardado en el diccionario de
# valores # valores
# Obtenemos el control sobre el que se ha echo click # Obtenemos el control sobre el que se ha echo click
control = self.getControl(id) # control = self.getControl(id)
# Lo buscamos en el listado de controles # Lo buscamos en el listado de controles
for cont in self.list_controls: for cont in self.list_controls:
# Si el control es un "downBtn" o "upBtn" son los botones del "list" # Si el control es un "downBtn" o "upBtn" son los botones del "list"
# en este caso cambiamos el valor del list # en este caso cambiamos el valor del list
if cont["type"] == "list" and (cont["downBtn"] == control or cont["upBtn"] == control): if cont["type"] == "list" and (cont["downBtn"].getId() == id or cont["upBtn"].getId() == id):
# Para bajar una posicion # Para bajar una posicion
if cont["downBtn"] == control: if cont["downBtn"].getId() == id:
index = cont["lvalues"].index(cont["label"].getLabel()) index = cont["lvalues"].index(cont["label"].getLabel())
if index > 0: if index > 0:
cont["label"].setLabel(cont["lvalues"][index - 1]) cont["label"].setLabel(cont["lvalues"][index - 1])
# Para subir una posicion # Para subir una posicion
elif cont["upBtn"] == control: elif cont["upBtn"].getId() == id:
index = cont["lvalues"].index(cont["label"].getLabel()) index = cont["lvalues"].index(cont["label"].getLabel())
if index < len(cont["lvalues"]) - 1: if index < len(cont["lvalues"]) - 1:
cont["label"].setLabel(cont["lvalues"][index + 1]) cont["label"].setLabel(cont["lvalues"][index + 1])
@@ -782,11 +794,11 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel()) self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
# Si esl control es un "bool", guardamos el nuevo valor True/False # Si esl control es un "bool", guardamos el nuevo valor True/False
if cont["type"] == "bool" and cont["control"] == control: if cont["type"] == "bool" and cont["control"].getId() == id:
self.values[cont["id"]] = bool(cont["control"].isSelected()) self.values[cont["id"]] = bool(cont["control"].isSelected())
# Si esl control es un "text", guardamos el nuevo valor # Si esl control es un "text", guardamos el nuevo valor
if cont["type"] == "text" and cont["control"] == control: if cont["type"] == "text" and cont["control"].getId() == id:
# Versiones antiguas requieren abrir el teclado manualmente # Versiones antiguas requieren abrir el teclado manualmente
if xbmcgui.ControlEdit == ControlEdit: if xbmcgui.ControlEdit == ControlEdit:
import xbmc import xbmc
@@ -817,9 +829,9 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
if action == 1: if action == 1:
# Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor # Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor
if focus not in [10004, 10005, 10006]: if focus not in [10004, 10005, 10006]:
control = self.getFocus() control = self.getFocus().getId()
for cont in self.list_controls: for cont in self.list_controls:
if cont["type"] == "list" and cont["control"] == control: if cont["type"] == "list" and cont["control"].getId() == control:
index = cont["lvalues"].index(cont["label"].getLabel()) index = cont["lvalues"].index(cont["label"].getLabel())
if index > 0: if index > 0:
cont["label"].setLabel(cont["lvalues"][index - 1]) cont["label"].setLabel(cont["lvalues"][index - 1])
@@ -843,9 +855,9 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
elif action == 2: elif action == 2:
# Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor # Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor
if focus not in [10004, 10005, 10006]: if focus not in [10004, 10005, 10006]:
control = self.getFocus() control = self.getFocus().getId()
for cont in self.list_controls: for cont in self.list_controls:
if cont["type"] == "list" and cont["control"] == control: if cont["type"] == "list" and cont["control"].getId() == control:
index = cont["lvalues"].index(cont["label"].getLabel()) index = cont["lvalues"].index(cont["label"].getLabel())
if index < len(cont["lvalues"]) - 1: if index < len(cont["lvalues"]) - 1:
cont["label"].setLabel(cont["lvalues"][index + 1]) cont["label"].setLabel(cont["lvalues"][index + 1])
@@ -870,11 +882,9 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
# Si el foco no está en ninguno de los tres botones inferiores, bajamos el foco en los controles de ajustes # Si el foco no está en ninguno de los tres botones inferiores, bajamos el foco en los controles de ajustes
if focus not in [10004, 10005, 10006]: if focus not in [10004, 10005, 10006]:
try: try:
focus_control = \ focus_control = [self.visible_controls.index(c) for c in self.visible_controls if c["control"].getId() == self.getFocus().getId()][0]
[self.visible_controls.index(c) for c in self.visible_controls if
c["control"] == self.getFocus()][
0]
focus_control += 1 focus_control += 1
except: except:
focus_control = 0 focus_control = 0
@@ -895,9 +905,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
if focus not in [10003, 10004, 10005, 10006]: if focus not in [10003, 10004, 10005, 10006]:
try: try:
focus_control = \ focus_control = \
[self.visible_controls.index(c) for c in self.visible_controls if [self.visible_controls.index(c) for c in self.visible_controls if c["control"].getId() == self.getFocus().getId()][0]
c["control"] == self.getFocus()][
0]
focus_control -= 1 focus_control -= 1
while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not
@@ -936,11 +944,11 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
elif action == 504: elif action == 504:
if self.xx > raw_action.getAmount2(): if self.xx > raw_action.getAmount2():
if (self.xx - int(raw_action.getAmount2())) / self.height_control: if old_div((self.xx - int(raw_action.getAmount2())), self.height_control):
self.xx -= self.height_control self.xx -= self.height_control
self.dispose_controls(self.index + 1) self.dispose_controls(self.index + 1)
else: else:
if (int(raw_action.getAmount2()) - self.xx) / self.height_control: if old_div((int(raw_action.getAmount2()) - self.xx), self.height_control):
self.xx += self.height_control self.xx += self.height_control
self.dispose_controls(self.index - 1) self.dispose_controls(self.index - 1)
return return
@@ -981,7 +989,7 @@ class ControlEdit(xbmcgui.ControlButton):
def setWidth(self, w): def setWidth(self, w):
xbmcgui.ControlButton.setWidth(self, w) xbmcgui.ControlButton.setWidth(self, w)
self.textControl.setWidth(w / 2) self.textControl.setWidth(old_div(w, 2))
def setHeight(self, w): def setHeight(self, w):
xbmcgui.ControlButton.setHeight(self, w) xbmcgui.ControlButton.setHeight(self, w)
@@ -992,7 +1000,7 @@ class ControlEdit(xbmcgui.ControlButton):
if xbmcgui.__version__ == "1.2": if xbmcgui.__version__ == "1.2":
self.textControl.setPosition(x + self.getWidth(), y) self.textControl.setPosition(x + self.getWidth(), y)
else: else:
self.textControl.setPosition(x + self.getWidth() / 2, y) self.textControl.setPosition(x + old_div(self.getWidth(), 2), y)
def setText(self, text): def setText(self, text):
self.text = text self.text = text
+1 -2
View File
@@ -91,8 +91,7 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
En caso de peliculas: En caso de peliculas:
Coge el titulo de los siguientes campos (en este orden) Coge el titulo de los siguientes campos (en este orden)
1. contentTitle (este tiene prioridad 1) 1. contentTitle (este tiene prioridad 1)
2. fulltitle (este tiene prioridad 2) 2. title (este tiene prioridad 2)
3. title (este tiene prioridad 3)
El primero que contenga "algo" lo interpreta como el titulo (es importante asegurarse que el titulo este en El primero que contenga "algo" lo interpreta como el titulo (es importante asegurarse que el titulo este en
su sitio) su sitio)
+38 -20
View File
@@ -3,17 +3,24 @@
# XBMC Library Tools # XBMC Library Tools
# ------------------------------------------------------------ # ------------------------------------------------------------
from future import standard_library
standard_library.install_aliases()
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import os import os
import threading import threading
import time import time
import re
import urllib2
import xbmc import xbmc
from core import filetools from core import filetools
from core import jsontools from core import jsontools
from platformcode import config, logger from platformcode import config, logger
from platformcode import platformtools from platformcode import platformtools
from core import scrapertools
def mark_auto_as_watched(item): def mark_auto_as_watched(item):
@@ -83,7 +90,6 @@ def sync_trakt_addon(path_folder):
"special://home/addons/script.trakt/"] "special://home/addons/script.trakt/"]
for path in paths: for path in paths:
import sys
sys.path.append(xbmc.translatePath(path)) sys.path.append(xbmc.translatePath(path))
# se obtiene las series vistas # se obtiene las series vistas
@@ -94,10 +100,9 @@ def sync_trakt_addon(path_folder):
return return
shows = traktapi.getShowsWatched({}) shows = traktapi.getShowsWatched({})
shows = shows.items() shows = list(shows.items())
# obtenemos el id de la serie para comparar # obtenemos el id de la serie para comparar
import re
_id = re.findall("\[(.*?)\]", path_folder, flags=re.DOTALL)[0] _id = re.findall("\[(.*?)\]", path_folder, flags=re.DOTALL)[0]
logger.debug("el id es %s" % _id) logger.debug("el id es %s" % _id)
@@ -329,9 +334,7 @@ def mark_season_as_watched_on_kodi(item, value=1):
def mark_content_as_watched_on_alfa(path): def mark_content_as_watched_on_alfa(path):
from specials import videolibrary from specials import videolibrary
from core import videolibrarytools from core import videolibrarytools
from core import scrapertools
from core import filetools
import re
""" """
marca toda la serie o película como vista o no vista en la Videoteca de Alfa basado en su estado en la Videoteca de Kodi marca toda la serie o película como vista o no vista en la Videoteca de Alfa basado en su estado en la Videoteca de Kodi
@type str: path @type str: path
@@ -361,6 +364,9 @@ def mark_content_as_watched_on_alfa(path):
if "\\" in path: if "\\" in path:
path = path.replace("/", "\\") path = path.replace("/", "\\")
head_nfo, item = videolibrarytools.read_nfo(path) #Leo el .nfo del contenido head_nfo, item = videolibrarytools.read_nfo(path) #Leo el .nfo del contenido
if not item:
logger.error('.NFO no encontrado: ' + path)
return
if FOLDER_TVSHOWS in path: #Compruebo si es CINE o SERIE if FOLDER_TVSHOWS in path: #Compruebo si es CINE o SERIE
contentType = "episode_view" #Marco la tabla de BBDD de Kodi Video contentType = "episode_view" #Marco la tabla de BBDD de Kodi Video
@@ -379,7 +385,7 @@ def mark_content_as_watched_on_alfa(path):
nfo_name = scrapertools.find_single_match(path2, '\]\/(.*?)$') #Construyo el nombre del .nfo nfo_name = scrapertools.find_single_match(path2, '\]\/(.*?)$') #Construyo el nombre del .nfo
path1 = path1.replace(nfo_name, '') #para la SQL solo necesito la carpeta path1 = path1.replace(nfo_name, '') #para la SQL solo necesito la carpeta
path2 = path2.replace(nfo_name, '') #para la SQL solo necesito la carpeta path2 = path2.replace(nfo_name, '') #para la SQL solo necesito la carpeta
path2 = filetools.remove_smb_credential(path2) #Si el archivo está en un servidor SMB, quiamos las credenciales path2 = filetools.remove_smb_credential(path2) #Si el archivo está en un servidor SMB, quitamos las credenciales
#Ejecutmos la sentencia SQL #Ejecutmos la sentencia SQL
sql = 'select strFileName, playCount from %s where (strPath like "%s" or strPath like "%s")' % (contentType, path1, path2) sql = 'select strFileName, playCount from %s where (strPath like "%s" or strPath like "%s")' % (contentType, path1, path2)
@@ -399,7 +405,11 @@ def mark_content_as_watched_on_alfa(path):
playCount_final = 0 playCount_final = 0
elif playCount >= 1: elif playCount >= 1:
playCount_final = 1 playCount_final = 1
title_plain = title_plain.decode("utf-8").encode("utf-8") #Hacemos esto porque si no genera esto: u'title_plain'
elif not PY3 and isinstance(title_plain, (str, unicode)):
title_plain = title_plain.decode("utf-8").encode("utf-8") #Hacemos esto porque si no genera esto: u'title_plain'
elif PY3 and isinstance(var, bytes):
title_plain = title_plain.decode('utf-8')
item.library_playcounts.update({title_plain: playCount_final}) #actualizamos el playCount del .nfo item.library_playcounts.update({title_plain: playCount_final}) #actualizamos el playCount del .nfo
if item.infoLabels['mediatype'] == "tvshow": #Actualizamos los playCounts de temporadas y Serie if item.infoLabels['mediatype'] == "tvshow": #Actualizamos los playCounts de temporadas y Serie
@@ -420,6 +430,7 @@ def get_data(payload):
@param payload: data @param payload: data
:return: :return:
""" """
import urllib.request, urllib.error
logger.info("payload: %s" % payload) logger.info("payload: %s" % payload)
# Required header for XBMC JSON-RPC calls, otherwise you'll get a 415 HTTP response code - Unsupported media type # Required header for XBMC JSON-RPC calls, otherwise you'll get a 415 HTTP response code - Unsupported media type
headers = {'content-type': 'application/json'} headers = {'content-type': 'application/json'}
@@ -433,14 +444,14 @@ def get_data(payload):
xbmc_json_rpc_url = "http://" + config.get_setting("xbmc_host", "videolibrary") + ":" + str( xbmc_json_rpc_url = "http://" + config.get_setting("xbmc_host", "videolibrary") + ":" + str(
xbmc_port) + "/jsonrpc" xbmc_port) + "/jsonrpc"
req = urllib2.Request(xbmc_json_rpc_url, data=jsontools.dump(payload), headers=headers) req = urllib.request.Request(xbmc_json_rpc_url, data=jsontools.dump(payload), headers=headers)
f = urllib2.urlopen(req) f = urllib.request.urlopen(req)
response = f.read() response = f.read()
f.close() f.close()
logger.info("get_data: response %s" % response) logger.info("get_data: response %s" % response)
data = jsontools.load(response) data = jsontools.load(response)
except Exception, ex: except Exception as ex:
template = "An exception of type %s occured. Arguments:\n%r" template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args) message = template % (type(ex).__name__, ex.args)
logger.error("error en xbmc_json_rpc_url: %s" % message) logger.error("error en xbmc_json_rpc_url: %s" % message)
@@ -448,7 +459,7 @@ def get_data(payload):
else: else:
try: try:
data = jsontools.load(xbmc.executeJSONRPC(jsontools.dump(payload))) data = jsontools.load(xbmc.executeJSONRPC(jsontools.dump(payload)))
except Exception, ex: except Exception as ex:
template = "An exception of type %s occured. Arguments:\n%r" template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args) message = template % (type(ex).__name__, ex.args)
logger.error("error en xbmc.executeJSONRPC: %s" % message) logger.error("error en xbmc.executeJSONRPC: %s" % message)
@@ -477,6 +488,7 @@ def update(folder_content=config.get_setting("folder_tvshows"), folder=""):
} }
if folder: if folder:
folder = str(folder)
videolibrarypath = config.get_videolibrary_config_path() videolibrarypath = config.get_videolibrary_config_path()
if folder.endswith('/') or folder.endswith('\\'): if folder.endswith('/') or folder.endswith('\\'):
@@ -489,9 +501,10 @@ def update(folder_content=config.get_setting("folder_tvshows"), folder=""):
videolibrarypath = videolibrarypath[:-1] videolibrarypath = videolibrarypath[:-1]
update_path = videolibrarypath + "/" + folder_content + "/" + folder + "/" update_path = videolibrarypath + "/" + folder_content + "/" + folder + "/"
else: else:
update_path = filetools.join(videolibrarypath, folder_content, folder) + "/" #update_path = filetools.join(videolibrarypath, folder_content, folder) + "/" # Problemas de encode en "folder"
update_path = filetools.join(videolibrarypath, folder_content, ' ').rstrip()
if not update_path.startswith("smb://"): if not scrapertools.find_single_match(update_path, '(^\w+:\/\/)'):
payload["params"] = {"directory": update_path} payload["params"] = {"directory": update_path}
while xbmc.getCondVisibility('Library.IsScanningVideo()'): while xbmc.getCondVisibility('Library.IsScanningVideo()'):
@@ -663,7 +676,7 @@ def set_content(content_type, silent=False):
if sql_videolibrarypath.startswith("special://"): if sql_videolibrarypath.startswith("special://"):
sql_videolibrarypath = sql_videolibrarypath.replace('/profile/', '/%/').replace('/home/userdata/', '/%/') sql_videolibrarypath = sql_videolibrarypath.replace('/profile/', '/%/').replace('/home/userdata/', '/%/')
sep = '/' sep = '/'
elif sql_videolibrarypath.startswith("smb://"): elif scrapertools.find_single_match(sql_videolibrarypath, '(^\w+:\/\/)'):
sep = '/' sep = '/'
else: else:
sep = os.sep sep = os.sep
@@ -881,7 +894,7 @@ def add_sources(path):
# Nodo <name> # Nodo <name>
nodo_name = xmldoc.createElement("name") nodo_name = xmldoc.createElement("name")
sep = os.sep sep = os.sep
if path.startswith("special://") or path.startswith("smb://"): if path.startswith("special://") or scrapertools.find_single_match(path, '(^\w+:\/\/)'):
sep = "/" sep = "/"
name = path name = path
if path.endswith(sep): if path.endswith(sep):
@@ -904,8 +917,13 @@ def add_sources(path):
nodo_video.appendChild(nodo_source) nodo_video.appendChild(nodo_source)
# Guardamos los cambios # Guardamos los cambios
filetools.write(SOURCES_PATH, if not PY3:
'\n'.join([x for x in xmldoc.toprettyxml().encode("utf-8").splitlines() if x.strip()])) filetools.write(SOURCES_PATH,
'\n'.join([x for x in xmldoc.toprettyxml().encode("utf-8").splitlines() if x.strip()]))
else:
filetools.write(SOURCES_PATH,
b'\n'.join([x for x in xmldoc.toprettyxml().encode("utf-8").splitlines() if x.strip()]),
vfs=False)
def ask_set_content(flag, silent=False): def ask_set_content(flag, silent=False):
+85 -1
View File
@@ -245,6 +245,10 @@ msgctxt "#30137"
msgid "Direct" msgid "Direct"
msgstr "" msgstr ""
msgctxt "#30138"
msgid "Live"
msgstr ""
msgctxt "#30151" msgctxt "#30151"
msgid "Watch the video" msgid "Watch the video"
msgstr "" msgstr ""
@@ -5734,7 +5738,19 @@ msgid "Playback"
msgstr "" msgstr ""
msgctxt "#70754" msgctxt "#70754"
msgid "Compact mode" msgid "Display mode"
msgstr ""
msgctxt "#70755"
msgid "Default"
msgstr ""
msgctxt "#70756"
msgid "Extended"
msgstr ""
msgctxt "#70757"
msgid "Compact"
msgstr "" msgstr ""
# DNS start [ settings and declaration ] # DNS start [ settings and declaration ]
@@ -5805,3 +5821,71 @@ msgstr ""
msgctxt "#707417" msgctxt "#707417"
msgid "Favourite quality" msgid "Favourite quality"
msgstr "" msgstr ""
msgctxt "#707418"
msgid "Follow the steps below:"
msgstr ""
msgctxt "#707419"
msgid "%s) click here to enable debug logging"
msgstr ""
msgctxt "#707420"
msgid "%s) repeat what you did to cause the error"
msgstr ""
msgctxt "#707421"
msgid "%s) click here to create the report"
msgstr ""
msgctxt "#707422"
msgid "%s) click here to disable debug logging"
msgstr ""
msgctxt "#707423"
msgid "Explain the issue and share this link:"
msgstr ""
msgctxt "#707424"
msgid "Service not available. Try again later"
msgstr ""
msgctxt "#707425"
msgid "Debug should be active"
msgstr ""
msgctxt "#707426"
msgid "before generating the report"
msgstr ""
msgctxt "#707427"
msgid "Unable to read kodi log"
msgstr ""
msgctxt "#707428"
msgid "Failed to upload report"
msgstr ""
msgctxt "#707429"
msgid "Report an issue"
msgstr ""
msgctxt "#707430"
msgid "Debug logging"
msgstr ""
msgctxt "#707431"
msgid "Enabled"
msgstr ""
msgctxt "#707432"
msgid "Disabled"
msgstr ""
msgctxt "#707433"
msgid "Delete"
msgstr ""
msgctxt "#707434"
msgid "reCaptcha verification failed"
msgstr ""
+86 -2
View File
@@ -245,6 +245,10 @@ msgctxt "#30137"
msgid "Direct" msgid "Direct"
msgstr "Diretto" msgstr "Diretto"
msgctxt "#30138"
msgid "Live"
msgstr "Live"
msgctxt "#30151" msgctxt "#30151"
msgid "Watch the video" msgid "Watch the video"
msgstr "Guarda il video" msgstr "Guarda il video"
@@ -5738,8 +5742,20 @@ msgid "Playback"
msgstr "Riproduzione" msgstr "Riproduzione"
msgctxt "#70754" msgctxt "#70754"
msgid "Compact mode" msgid "Display mode"
msgstr "Modalità compatta" msgstr "Modalità visualizzazione"
msgctxt "#70755"
msgid "Default"
msgstr "Predefinita"
msgctxt "#70756"
msgid "Extended"
msgstr "Estesa"
msgctxt "#70757"
msgid "Compact"
msgstr "Compatta"
# DNS start [ settings and declaration ] # DNS start [ settings and declaration ]
msgctxt "#707401" msgctxt "#707401"
@@ -5809,3 +5825,71 @@ msgstr "Ogni quanto vuoi che vengano controllati? (ore)"
msgctxt "#707417" msgctxt "#707417"
msgid "Favourite quality" msgid "Favourite quality"
msgstr "Qualità preferita" msgstr "Qualità preferita"
msgctxt "#707418"
msgid "Follow the steps below:"
msgstr "Segui i seguenti passi:"
msgctxt "#707419"
msgid "%s) click here to enable debug logging"
msgstr "%s) clicca qui per attivare il logging di debug"
msgctxt "#707420"
msgid "%s) repeat what you did to cause the error"
msgstr "%s) ripeti ciò che hai fatto per causare l'errore"
msgctxt "#707421"
msgid "%s) click here to create the report"
msgstr "%s) clicca qui per creare il report"
msgctxt "#707422"
msgid "%s) click here to disable debug logging"
msgstr "%s) clicca qui per disattivare il logging di debug"
msgctxt "#707423"
msgid "Explain the issue and share this link:"
msgstr "Spiega il problema e condividi questo link:"
msgctxt "#707424"
msgid "Service not available. Try again later"
msgstr "Servizio non disponibile Riprova più tardi"
msgctxt "#707425"
msgid "Debug should be active"
msgstr "Il debug dovrebbe essere attivo"
msgctxt "#707426"
msgid "before generating the report"
msgstr "prima di generare il report"
msgctxt "#707427"
msgid "Unable to read kodi log"
msgstr "Impossibile leggere il log di kodi"
msgctxt "#707428"
msgid "Failed to upload report"
msgstr "Impossibile caricare il report"
msgctxt "#707429"
msgid "Report an issue"
msgstr "Segnala un problema"
msgctxt "#707430"
msgid "Debug logging"
msgstr "Logging di debug"
msgctxt "#707431"
msgid "Enabled"
msgstr "Attivato"
msgctxt "#707432"
msgid "Disabed"
msgstr "Disattivato"
msgctxt "#707433"
msgid "Delete"
msgstr "Cancella"
msgctxt "#707434"
msgid "reCaptcha verification failed"
msgstr "Verifica reCaptcha fallita"
+2 -1
View File
@@ -59,7 +59,8 @@
<setting id="videolibrary_kodi" type="bool" label="70120" enable="lt(-1,2)+eq(0,false)" default="false"/> <setting id="videolibrary_kodi" type="bool" label="70120" enable="lt(-1,2)+eq(0,false)" default="false"/>
<setting id="videolibrary_max_quality" type="bool" label="70729" default="false" visible="true"/> <setting id="videolibrary_max_quality" type="bool" label="70729" default="false" visible="true"/>
<setting id="next_ep" type="enum" label="70746" lvalues="70752|70747|70748" default="0"/> <setting id="next_ep" type="enum" label="70746" lvalues="70752|70747|70748" default="0"/>
<setting id="next_ep_type" type="bool" label="70754" default="false" visible="eq(-1,2)"/> <setting id="next_ep_type" type="select" label="70754" lvalues="70755|70756|70757" default="0" visible="eq(-1,2)"/>
<!-- <setting id="next_ep_type" type="bool" label="70754" default="false" visible="eq(-1,2)"/> -->
<setting id="next_ep_seconds" type="enum" values="20|30|40|50|60|70|80|90|100|110|120" label="70749" default="2" visible="!eq(-2,0)"/> <setting id="next_ep_seconds" type="enum" values="20|30|40|50|60|70|80|90|100|110|120" label="70749" default="2" visible="!eq(-2,0)"/>
</category> </category>
@@ -26,7 +26,7 @@
<height>34</height> <height>34</height>
<width>725</width> <width>725</width>
<font>font12_title</font> <font>font12_title</font>
<textcolor>0xFFFFFFFF</textcolor> <textcolor>FFFFFFFF</textcolor>
<align>center</align> <align>center</align>
<aligny>center</aligny> <aligny>center</aligny>
<label>$ADDON[plugin.video.kod 70000]</label> <label>$ADDON[plugin.video.kod 70000]</label>
@@ -45,6 +45,7 @@
<width>200</width> <width>200</width>
<height>50</height> <height>50</height>
<textwidth>110</textwidth> <textwidth>110</textwidth>
<textcolor>FFFFFFFF</textcolor>
<texturefocus>Controls/KeyboardKey.png</texturefocus> <texturefocus>Controls/KeyboardKey.png</texturefocus>
<texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus>
<align>center</align> <align>center</align>
@@ -57,6 +58,7 @@
<width>200</width> <width>200</width>
<height>50</height> <height>50</height>
<textwidth>110</textwidth> <textwidth>110</textwidth>
<textcolor>FFFFFFFF</textcolor>
<texturefocus>Controls/KeyboardKey.png</texturefocus> <texturefocus>Controls/KeyboardKey.png</texturefocus>
<texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus>
<align>center</align> <align>center</align>
@@ -69,6 +71,7 @@
<width>200</width> <width>200</width>
<height>50</height> <height>50</height>
<textwidth>110</textwidth> <textwidth>110</textwidth>
<textcolor>FFFFFFFF</textcolor>
<texturefocus>Controls/KeyboardKey.png</texturefocus> <texturefocus>Controls/KeyboardKey.png</texturefocus>
<texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus>
<align>center</align> <align>center</align>
@@ -86,7 +89,7 @@
<width>745</width> <width>745</width>
<height>300</height> <height>300</height>
<font>font16</font> <font>font16</font>
<textcolor>0xFFFFFFFF</textcolor> <textcolor>FFFFFFFF</textcolor>
<align>center</align> <align>center</align>
<aligny>center</aligny> <aligny>center</aligny>
<label>$ADDON[plugin.video.kod 70004]</label> <label>$ADDON[plugin.video.kod 70004]</label>
@@ -97,7 +100,7 @@
<posx>780</posx> <posx>780</posx>
<width>10</width> <width>10</width>
<height>300</height> <height>300</height>
<textcolor>0xFFFFFFFF</textcolor> <textcolor>FFFFFFFF</textcolor>
<texture>Controls/ScrollBack.png</texture> <texture>Controls/ScrollBack.png</texture>
</control> </control>
<control type="image" id="10009"> <control type="image" id="10009">
+1 -1
View File
@@ -42,7 +42,7 @@
<itemgap>0</itemgap> <itemgap>0</itemgap>
<align>right</align> <align>right</align>
<control type="button" id="11"> <control type="button" id="11">
<label>$ADDON[plugin.video.kod 70750] $INFO[Player.TimeRemaining(mm:ss)]</label> <label>$INFO[Window.Property(title)] | $INFO[Window.Property(ep_title)] | $INFO[Player.TimeRemaining(secs),,]</label>
<onclick>SendClick(3012)</onclick> <onclick>SendClick(3012)</onclick>
<height>40</height> <height>40</height>
<width min="50">auto</width> <width min="50">auto</width>
@@ -42,7 +42,7 @@
<itemgap>0</itemgap> <itemgap>0</itemgap>
<align>right</align> <align>right</align>
<control type="button" id="11"> <control type="button" id="11">
<label>[B]$INFO[Player.TimeRemaining(mm:ss)][/B]</label> <label>[B]$INFO[Player.TimeRemaining(secs),,][/B]</label>
<onclick>SendClick(3012)</onclick> <onclick>SendClick(3012)</onclick>
<!-- <visible>!Integer.IsGreater(Player.TimeRemaining,59)</visible> --> <!-- <visible>!Integer.IsGreater(Player.TimeRemaining,59)</visible> -->
<height>40</height> <height>40</height>
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<window>
<defaultcontrol always="true">20</defaultcontrol>
<onload>Dialog.Close(fullscreeninfo,true)</onload>
<onload>Dialog.Close(videoosd,true)</onload>
<controls>
<control type="group">
<animation type="WindowOpen" reversible="false">
<effect type="fade" start="0" end="100" time="600" />
<effect type="slide" start="115,0" end="0,0" time="600" />
</animation>
<animation type="WindowClose" reversible="false">
<effect type="fade" start="100" end="0" time="400" />
<effect type="slide" start="0,0" end="115,0" time="400" />
</animation>
<control type="group">
<right>30</right>
<bottom>30</bottom>
<height>220</height>
<width>326</width>
<!-- Background -->
<control type="image">
<top>0</top>
<right>0</right>
<width>326</width>
<height>180</height>
<texture>$INFO[Window.Property(next_img)]</texture>
</control>
<control type="group">
<top>0</top>
<right>0</right>
<width>100%</width>
<!-- buttons -->
<control type="button" id="3012">
<left>-1000</left>
<top>-1000</top>
<height>1</height>
<width>1</width>
</control>
<control type="grouplist" id="20">
<orientation>vertical</orientation>
<height>220</height>
<width>326</width>
<itemgap>0</itemgap>
<right>0</right>
<control type="button" id="11">
<label></label>
<onclick>SendClick(3012)</onclick>
<height>180</height>
<width>326</width>
<right>0</right>
<font>font30_title</font>
<textoffsety>20</textoffsety>
<textcolor>FFFFFFFF</textcolor>
<focusedcolor>FFFFFFFF</focusedcolor>
<selectedcolor>FFFFFFFF</selectedcolor>
<shadowcolor>22000000</shadowcolor>
<aligny>top</aligny>
<align>center</align>
<texturefocus border="10">NextDialog/background-play.png</texturefocus>
<texturenofocus border="10" colordiffuse="00232323">NextDialog/background-diffuse.png</texturenofocus>
<pulseonselect>no</pulseonselect>
</control>
<control type="button" id="3013">
<label>$ADDON[plugin.video.kod 60396]</label>
<height>40</height>
<width>326</width>
<font>font30_title</font>
<textoffsetx>20</textoffsetx>
<textcolor>80FFFFFF</textcolor>
<focusedcolor>FFFFFFFF</focusedcolor>
<selectedcolor>80FFFFFF</selectedcolor>
<shadowcolor>22000000</shadowcolor>
<aligny>center</aligny>
<align>center</align>
<texturefocus border="10" colordiffuse="88232323">NextDialog/background-diffuse.png</texturefocus>
<texturenofocus border="10" colordiffuse="88232323">NextDialog/background-diffuse.png</texturenofocus>
<pulseonselect>no</pulseonselect>
</control>
</control>
<control type="label">
<bottom>60</bottom>
<height>40</height>
<aligny>center</aligny>
<visible>true</visible>
<align>center</align>
<scroll>true</scroll>
<scrollspeed>50</scrollspeed>
<textcolor>FFFFFFFF</textcolor>
<shadowcolor>ff000000</shadowcolor>
<info>Window.Property(title)</info>
<font>font30_title</font>
</control>
<control type="label">
<bottom>40</bottom>
<height>40</height>
<aligny>center</aligny>
<visible>true</visible>
<align>center</align>
<scroll>true</scroll>
<scrollspeed>50</scrollspeed>
<textcolor>FFFFFFFF</textcolor>
<shadowcolor>ff000000</shadowcolor>
<info>Window.Property(ep_title)</info>
<font>font20_title</font>
</control>
<control type="label">
<top>20</top>
<right>25</right>
<height>auto</height>
<aligny>top</aligny>
<visible>true</visible>
<align>right</align>
<scroll>true</scroll>
<scrollspeed>50</scrollspeed>
<textcolor>FFFFFFFF</textcolor>
<shadowcolor>ff000000</shadowcolor>
<info>Player.TimeRemaining(secs),,</info>
<font>font30_title</font>
</control>
</control>
</control>
</control>
</controls>
</window>
+134 -119
View File
@@ -1,206 +1,221 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<window> <window>
<allowoverlays>false</allowoverlays> <allowoverlays>false</allowoverlays>
<animation type="WindowOpen" reversible="false">
<effect type="zoom" start="80" end="100" center="640,225" delay="160" tween="back" time="240" />
<effect type="fade" delay="160" end="100" time="240" />
</animation>
<animation type="WindowClose" reversible="false">
<effect type="zoom" start="100" end="80" center="640,225" easing="in" tween="back" time="240" />
<effect type="fade" start="100" end="0" time="240" />
</animation>
<controls> <controls>
<control type="group" id="10001"> <control type="group" id="10001">
<posx>250</posx> <top>40</top>
<posy>60</posy> <left>390</left>
<width>700</width> <width>600</width>
<height>600</height> <height>640</height>
<control type="image"> <control type="image">
<width>700</width> <width>510</width>
<height>600</height> <height>640</height>
<texture>Windows/DialogBack.png</texture> <left>45</left>
<texture>Shortcut/dialog-bg-solid.png</texture>
</control> </control>
<control type="textbox" id="10000"> <control type="textbox" id="10000">
<posy>20</posy> <top>30</top>
<posx>30</posx> <left>20</left>
<height>60</height> <height>60</height>
<width>630</width> <width>560</width>
<textcolor>0xFFFFA500</textcolor> <textcolor>FFFFFFFF</textcolor>
<wrapmultiline>true</wrapmultiline> <wrapmultiline>true</wrapmultiline>
<align>center</align> <align>center</align>
<label></label> <label></label>
</control> </control>
<control type="button" id="10002"> <control type="button" id="10002">
<posy>540</posy> <top>565</top>
<posx>110</posx> <left>75</left>
<width>140</width> <width>150</width>
<height>30</height> <height>50</height>
<textwidth>100</textwidth> <textwidth>110</textwidth>
<textcolor>FFFFFFFF</textcolor>
<focusedcolor>FFFFFFFF</focusedcolor>
<texturefocus>Controls/KeyboardKey.png</texturefocus> <texturefocus>Controls/KeyboardKey.png</texturefocus>
<texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus>
<align>center</align> <align>center</align>
<aligny>center</aligny> <aligny>center</aligny>
<label>Aceptar</label> <label>$ADDON[plugin.video.kod 70007]</label>
<onup>10013</onup> <onup>10011</onup>
<ondown>10003</ondown> <ondown>10005</ondown>
<onleft>10013</onleft> <onleft>10004</onleft>
<onright>10003</onright> <onright>10003</onright>
</control> </control>
<control type="button" id="10003"> <control type="button" id="10003">
<posy>540</posy> <top>565</top>
<posx>280</posx> <left>225</left>
<width>140</width> <width>150</width>
<height>30</height> <height>50</height>
<textwidth>100</textwidth> <textwidth>110</textwidth>
<textcolor>FFFFFFFF</textcolor>
<focusedcolor>FFFFFFFF</focusedcolor>
<texturefocus>Controls/KeyboardKey.png</texturefocus> <texturefocus>Controls/KeyboardKey.png</texturefocus>
<texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus>
<align>center</align> <align>center</align>
<aligny>center</aligny> <aligny>center</aligny>
<label>Cancelar</label> <label>$ADDON[plugin.video.kod 707433]</label>
<onup>10002</onup> <onup>10012</onup>
<ondown>10004</ondown> <ondown>10006</ondown>
<onleft>10002</onleft> <onleft>10002</onleft>
<onright>10004</onright> <onright>10004</onright>
</control> </control>
<control type="button" id="10004"> <control type="button" id="10004">
<posy>540</posy> <top>565</top>
<posx>450</posx> <left>375</left>
<width>140</width> <width>150</width>
<height>30</height> <height>50</height>
<textwidth>100</textwidth> <textwidth>110</textwidth>
<textcolor>FFFFFFFF</textcolor>
<focusedcolor>FFFFFFFF</focusedcolor>
<texturefocus>Controls/KeyboardKey.png</texturefocus> <texturefocus>Controls/KeyboardKey.png</texturefocus>
<texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus>Controls/KeyboardKeyNF.png</texturenofocus>
<align>center</align> <align>center</align>
<aligny>center</aligny> <aligny>center</aligny>
<label>Recargar</label> <label>$ADDON[plugin.video.kod 70008]</label>
<onup>10003</onup> <onup>10013</onup>
<ondown>10005</ondown> <ondown>10007</ondown>
<onleft>10003</onleft> <onleft>10003</onleft>
<onright>10005</onright> <onright>10002</onright>
</control> </control>
<control type="image" id="10020"> <control type="image" id="10020">
<posy>80</posy> <top>90</top>
<posx>120</posx> <left>75</left>
<width>450</width> <width>450</width>
<height>450</height> <height>450</height>
</control> </control>
<control type="togglebutton" id="10005"> <control type="togglebutton" id="10005">
<posy>80</posy> <top>90</top>
<posx>120</posx> <left>75</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10004</onup> <onup>10002</onup>
<ondown>10006</ondown> <ondown>10008</ondown>
<onleft>10004</onleft> <onleft>10007</onleft>
<onright>10006</onright> <onright>10006</onright>
</control> </control>
<control type="togglebutton" id="10006"> <control type="togglebutton" id="10006">
<posy>80</posy> <top>90</top>
<posx>270</posx> <left>225</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10005</onup> <onup>10003</onup>
<ondown>10007</ondown> <ondown>10009</ondown>
<onleft>10005</onleft> <onleft>10005</onleft>
<onright>10007</onright> <onright>10007</onright>
</control> </control>
<control type="togglebutton" id="10007"> <control type="togglebutton" id="10007">
<posy>80</posy> <top>90</top>
<posx>420</posx> <left>375</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10006</onup> <onup>10004</onup>
<ondown>10008</ondown> <ondown>10010</ondown>
<onleft>10006</onleft> <onleft>10006</onleft>
<onright>10008</onright> <onright>10005</onright>
</control> </control>
<control type="togglebutton" id="10008"> <control type="togglebutton" id="10008">
<posy>230</posy> <top>240</top>
<posx>120</posx> <left>75</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10007</onup> <onup>10005</onup>
<ondown>10009</ondown> <ondown>10011</ondown>
<onleft>10007</onleft> <onleft>10010</onleft>
<onright>10009</onright> <onright>10009</onright>
</control> </control>
<control type="togglebutton" id="10009"> <control type="togglebutton" id="10009">
<posy>230</posy> <top>240</top>
<posx>270</posx> <left>225</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10008</onup> <onup>10006</onup>
<ondown>10010</ondown> <ondown>10012</ondown>
<onleft>10008</onleft> <onleft>10008</onleft>
<onright>10010</onright> <onright>10010</onright>
</control> </control>
<control type="togglebutton" id="10010"> <control type="togglebutton" id="10010">
<posy>230</posy> <top>240</top>
<posx>420</posx> <left>375</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10009</onup> <onup>10007</onup>
<ondown>10011</ondown> <ondown>10013</ondown>
<onleft>10009</onleft> <onleft>10009</onleft>
<onright>10011</onright> <onright>10008</onright>
</control> </control>
<control type="togglebutton" id="10011"> <control type="togglebutton" id="10011">
<posy>380</posy> <top>390</top>
<posx>120</posx> <left>75</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10010</onup> <onup>10008</onup>
<ondown>10012</ondown> <ondown>10002</ondown>
<onleft>10010</onleft> <onleft>10013</onleft>
<onright>10012</onright> <onright>10012</onright>
</control> </control>
<control type="togglebutton" id="10012"> <control type="togglebutton" id="10012">
<posy>380</posy> <top>390</top>
<posx>270</posx> <left>225</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10011</onup> <onup>10009</onup>
<ondown>10013</ondown> <ondown>10003</ondown>
<onleft>10011</onleft> <onleft>10011</onleft>
<onright>10013</onright> <onright>10013</onright>
</control> </control>
<control type="togglebutton" id="10013"> <control type="togglebutton" id="10013">
<posy>380</posy> <top>390</top>
<posx>420</posx> <left>375</left>
<width>150</width> <width>150</width>
<height>150</height> <height>150</height>
<texturefocus border="50" colordiffuse="50D8D8D8">Controls/KeyboardKeyWhite.png</texturefocus> <texturefocus colordiffuse="AA232323">Controls/background-diffuse.png</texturefocus>
<texturenofocus border="15" colordiffuse="FFA2B2E7">Controls/KeyboardKeyNF.png</texturenofocus> <texturenofocus colordiffuse="00232323">Controls/background-diffuse.png</texturenofocus>
<alttexturefocus>Controls/check_mark.png</alttexturefocus> <alttexturefocus colordiffuse="FF232323">Controls/check_mark.png</alttexturefocus>
<alttexturenofocus>Controls/check_mark.png</alttexturenofocus> <alttexturenofocus colordiffuse="FFFFFFFF">Controls/check_mark.png</alttexturenofocus>
<onup>10012</onup> <onup>10010</onup>
<ondown>10002</ondown> <ondown>10004</ondown>
<onleft>10012</onleft> <onleft>10012</onleft>
<onright>10002</onright> <onright>10011</onright>
</control> </control>
</control> </control>
</controls> </controls>
+10 -12
View File
@@ -69,10 +69,8 @@
<texture colordiffuse="60FFFFFF">Shortcut/black.png</texture> <texture colordiffuse="60FFFFFF">Shortcut/black.png</texture>
</control> </control>
<control type="image"> <control type="image">
<left>35</left> <width>220</width>
<top>35</top> <height>220</height>
<width>150</width>
<height>150</height>
<texture>$INFO[ListItem.Property(thumb)]</texture> <texture>$INFO[ListItem.Property(thumb)]</texture>
<aspectratio>keep</aspectratio> <aspectratio>keep</aspectratio>
<align>center</align> <align>center</align>
@@ -97,8 +95,9 @@
<animation effect="fade" start="100" end="0" time="0">Focus</animation> <animation effect="fade" start="100" end="0" time="0">Focus</animation>
</control> </control>
<control type="image"> <control type="image">
<top>1</top>
<width>220</width> <width>220</width>
<height>220</height> <height>218</height>
<texture colordiffuse="FF0082C2">Shortcut/button-fo.png</texture> <texture colordiffuse="FF0082C2">Shortcut/button-fo.png</texture>
<animation effect="fade" start="100" end="0" time="0">Unfocus</animation> <animation effect="fade" start="100" end="0" time="0">Unfocus</animation>
</control> </control>
@@ -118,20 +117,19 @@
<texture colordiffuse="60FFFFFF">Shortcut/black.png</texture> <texture colordiffuse="60FFFFFF">Shortcut/black.png</texture>
</control> </control>
<control type="image"> <control type="image">
<left>35</left> <width>220</width>
<top>35</top> <height>220</height>
<width>150</width>
<height>150</height>
<texture>$INFO[ListItem.Property(thumb)]</texture> <texture>$INFO[ListItem.Property(thumb)]</texture>
<aspectratio>keep</aspectratio> <aspectratio>keep</aspectratio>
<align>center</align> <align>center</align>
</control> </control>
<control type="textbox"> <control type="textbox">
<left>0</left> <left>0</left>
<top>146</top> <top>160</top>
<width>220</width> <width>220</width>
<height>74</height> <height>74</height>
<font>font12</font> <font>font12</font>
<textcolor>FFFFFFFF</textcolor>
<label>$INFO[ListItem.Label]</label> <label>$INFO[ListItem.Label]</label>
<align>center</align> <align>center</align>
<aligny>center</aligny> <aligny>center</aligny>
@@ -151,8 +149,8 @@
<textureslidernibfocus>-</textureslidernibfocus> <textureslidernibfocus>-</textureslidernibfocus>
<showonepage>false</showonepage> <showonepage>false</showonepage>
<orientation>horizontal</orientation> <orientation>horizontal</orientation>
<onleft>32500</onleft> <!-- <onleft>32500</onleft>
<onright>32500</onright> <onright>32500</onright> -->
<ondown>32500</ondown> <ondown>32500</ondown>
<onup>32500</onup> <onup>32500</onup>
<animation effect="slide" end="120,0" time="0" condition="!Control.IsVisible(5)">Conditional</animation> <animation effect="slide" end="120,0" time="0" condition="!Control.IsVisible(5)">Conditional</animation>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

@@ -8,17 +8,13 @@
"url": "\\1" "url": "\\1"
}, },
{ {
"pattern": "akvideo.stream\/video\/(?:embed-)?([a-zA-Z0-9]+)", "pattern": "akvideo.stream/(?:video/|video\\.php\\?file_code=)?(?:embed-)?([a-zA-Z0-9]+)",
"url": "http://akvideo.stream\/video\/\\1"
},
{
"pattern": "akvideo.stream/(?:embed-)?([a-zA-Z0-9]+)",
"url": "http://akvideo.stream/video/\\1" "url": "http://akvideo.stream/video/\\1"
} }
] ]
}, },
"free": true, "free": true,
"id": "akstream", "id": "akvideo",
"name": "Akvideo", "name": "Akvideo",
"settings": [ "settings": [
{ {
+19 -5
View File
@@ -1,24 +1,34 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# by DrZ3r0 # by DrZ3r0
import urllib import urllib, re
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from platformcode import logger, config from platformcode import logger, config
from core.support import dbg
def test_video_exists(page_url): def test_video_exists(page_url):
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
# page_url = re.sub('akvideo.stream/(?:video/|video\\.php\\?file_code=)?(?:embed-)?([a-zA-Z0-9]+)','akvideo.stream/video/\\1',page_url)
global data global data
# dbg()
page = httptools.downloadpage(page_url) page = httptools.downloadpage(page_url)
logger.info(page.data)
if 'embed_ak.php' in page_url: if 'embed_ak.php' in page_url:
code = scrapertools.find_single_match(page.url, '/embed-([0-9a-z]+)\.html') code = scrapertools.find_single_match(page.url, '/embed-([0-9a-z]+)\.html')
if code: if not code:
code = scrapertools.find_single_match(page.data, r"""input\D*id=(?:'|")[^'"]+(?:'|").*?value='([a-z0-9]+)""")
if code :
page = httptools.downloadpage('http://akvideo.stream/video/' + code) page = httptools.downloadpage('http://akvideo.stream/video/' + code)
else: else:
return False, config.get_localized_string(70449) % "Akvideo" return False, config.get_localized_string(70449) % "Akvideo"
data = page.data data = page.data
# ID, code = scrapertools.find_single_match(data, r"""input\D*id=(?:'|")([^'"]+)(?:'|").*?value='([a-z0-9]+)""")
# post = urllib.urlencode({ID: code})
# logger.info('PAGE DATA' + data)
if "File Not Found" in data: if "File Not Found" in data:
return False, config.get_localized_string(70449) % "Akvideo" return False, config.get_localized_string(70449) % "Akvideo"
return True, "" return True, ""
@@ -27,14 +37,20 @@ def test_video_exists(page_url):
def get_video_url(page_url, premium=False, user="", password="", video_password=""): def get_video_url(page_url, premium=False, user="", password="", video_password=""):
logger.info(" url=" + page_url) logger.info(" url=" + page_url)
video_urls = [] video_urls = []
# dbg()
global data global data
logger.info('PAGE DATA' + data)
vres = scrapertools.find_multiple_matches(data, 'nowrap[^>]+>([^,]+)') vres = scrapertools.find_multiple_matches(data, 'nowrap[^>]+>([^,]+)')
if not vres: vres = scrapertools.find_multiple_matches(data, '<td>(\d+x\d+)')
data_pack = scrapertools.find_single_match(data, "</div>\n\s*<script[^>]+>(eval.function.p,a,c,k,e,.*?)\s*</script>") data_pack = scrapertools.find_single_match(data, "</div>\n\s*<script[^>]+>(eval.function.p,a,c,k,e,.*?)\s*</script>")
if data_pack != "": if data_pack != "":
from lib import jsunpack from lib import jsunpack
data = jsunpack.unpack(data_pack) data = jsunpack.unpack(data_pack)
block = scrapertools.find_single_match(data, "sources:\s\[([^\]]+)\]")
data = block if block else data
# URL # URL
# logger.info(data) # logger.info(data)
matches = scrapertools.find_multiple_matches(data, '(http.*?\.mp4)') matches = scrapertools.find_multiple_matches(data, '(http.*?\.mp4)')
@@ -47,6 +63,4 @@ def get_video_url(page_url, premium=False, user="", password="", video_password=
video_urls.append([vres[i] + " mp4 [Akvideo] ", media_url.replace('https://', 'http://') + '|' + _headers]) video_urls.append([vres[i] + " mp4 [Akvideo] ", media_url.replace('https://', 'http://') + '|' + _headers])
i = i + 1 i = i + 1
for video_url in video_urls: return sorted(video_urls, key=lambda x: int(x[0].split('x')[0]))
logger.info(" %s - %s" % (video_url[0], video_url[1]))
return sorted(video_urls, key=lambda x: x[0].split('x')[1])
+2 -2
View File
@@ -5,14 +5,14 @@
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
response = httptools.downloadpage(page_url) response = httptools.downloadpage(page_url)
if not response.sucess or "Not Found" in response.data or "File was deleted" in response.data or "is no longer available" in response.data: if not response.sucess or "Not Found" in response.data or "File was deleted" in response.data or "is no longer available" in response.data:
return False, "[anonfile] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "anonfile"
return True, "" return True, ""
+2 -2
View File
@@ -5,14 +5,14 @@
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
response = httptools.downloadpage(page_url) response = httptools.downloadpage(page_url)
if "File was deleted" in response.data or "is no longer available" in response.data: if "File was deleted" in response.data or "is no longer available" in response.data:
return False, "[filepup] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "filepup"
return True, "" return True, ""
+2 -2
View File
@@ -3,7 +3,7 @@
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from lib import jsunpack from lib import jsunpack
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
@@ -12,7 +12,7 @@ def test_video_exists(page_url):
data = httptools.downloadpage(page_url).data data = httptools.downloadpage(page_url).data
if "Not Found" in data or "File was deleted" in data: if "Not Found" in data or "File was deleted" in data:
return False, "[Filevideo] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "Filevideo"
return True, "" return True, ""
+2 -2
View File
@@ -5,14 +5,14 @@
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
response = httptools.downloadpage(page_url) response = httptools.downloadpage(page_url)
if not response.sucess or "Not Found" in response.data or "File was deleted" in response.data or "is no longer available" in response.data: if not response.sucess or "Not Found" in response.data or "File was deleted" in response.data or "is no longer available" in response.data:
return False, "[nofile] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "nofile"
return True, "" return True, ""
+2 -2
View File
@@ -3,7 +3,7 @@
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from lib import jsunpack from lib import jsunpack
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
@@ -12,7 +12,7 @@ def test_video_exists(page_url):
response = httptools.downloadpage(page_url) response = httptools.downloadpage(page_url)
if not response.sucess or "Not Found" in response.data or "File was deleted" in response.data or "is no longer available" in response.data: if not response.sucess or "Not Found" in response.data or "File was deleted" in response.data or "is no longer available" in response.data:
return False, "[Userscloud] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "Userscloud"
return True, "" return True, ""
@@ -8,13 +8,13 @@
"url": "https://vcstream.to/embed/\\1/\\2" "url": "https://vcstream.to/embed/\\1/\\2"
}, },
{ {
"pattern": "vidcloud.co/(?:embed|f|v)/([a-z0-9A-Z]+)", "pattern": "vidcloud.ru/(?:embed|f|v)/([a-z0-9A-Z]+)",
"url": "https://vidcloud.co\/v\/\\1" "url": "https://vidcloud.ru\/v\/\\1"
} }
] ]
}, },
"free": true, "free": true,
"id": "vcstream", "id": "vidcloud",
"name": "Vidcloud", "name": "Vidcloud",
"settings": [ "settings": [
{ {
+4 -1
View File
@@ -2,7 +2,10 @@
# Icarus pv7 # Icarus pv7
# Fix dentaku65 # Fix dentaku65
import urlparse try:
import urlparse
except:
import urllib.parse as urlparse
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
+2 -2
View File
@@ -4,14 +4,14 @@ import urllib
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
data = httptools.downloadpage(page_url).data data = httptools.downloadpage(page_url).data
if "Not Found" in data: if "Not Found" in data:
return False, "[Vidup] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "Vidup"
return True, "" return True, ""
+2 -2
View File
@@ -3,14 +3,14 @@
from core import httptools from core import httptools
from core import scrapertools from core import scrapertools
from lib import jsunpack from lib import jsunpack
from platformcode import logger from platformcode import logger, config
def test_video_exists(page_url): def test_video_exists(page_url):
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
data = httptools.downloadpage(page_url).data data = httptools.downloadpage(page_url).data
if "Not Found" in data or "File was deleted" in data: if "Not Found" in data or "File was deleted" in data:
return False, "[Watchvideo] El fichero no existe o ha sido borrado" return False, config.get_localized_string(70449) % "Watchvideo"
return True, "" return True, ""
+24 -9
View File
@@ -9,34 +9,49 @@ except ImportError:
import urllib import urllib
from core import httptools, scrapertools from core import httptools, scrapertools
from platformcode import logger, config from platformcode import logger, config, platformtools
headers = [['User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0']] headers = [['User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0']]
def test_video_exists(page_url): def test_video_exists(page_url):
def int_bckup_method():
global data,headers
page_url = scrapertools.find_single_match(data, r"""<center><a href='(https?:\/\/wstream[^']+)'\s*title='bkg'""")
if page_url:
data = httptools.downloadpage(page_url, headers=headers, follow_redirects=True, post={'g-recaptcha-response': captcha}).data
logger.info("(page_url='%s')" % page_url) logger.info("(page_url='%s')" % page_url)
resp = httptools.downloadpage(page_url) resp = httptools.downloadpage(page_url)
global data global data
data = resp.data data = resp.data
sitekey = scrapertools.find_single_match(data, 'data-sitekey="([^"]+)')
captcha = platformtools.show_recaptcha(sitekey, page_url) if sitekey else ''
page_url = resp.url page_url = resp.url
if '/streaming.php' in page_url in page_url: if '/streaming.php' in page_url in page_url:
code = httptools.downloadpage(page_url, headers=headers, follow_redirects=False).headers['location'].split('/')[-1].replace('.html','') code = httptools.downloadpage(page_url, headers=headers, follow_redirects=False).headers['location'].split('/')[-1].replace('.html', '')
logger.info('WCODE='+code) logger.info('WCODE=' + code)
page_url = 'https://wstream.video/video.php?file_code=' + code page_url = 'https://wstream.video/video.php?file_code=' + code
data = httptools.downloadpage(page_url, headers=headers, follow_redirects=True).data data = httptools.downloadpage(page_url, headers=headers, follow_redirects=True).data
possibleParam = scrapertools.find_multiple_matches(data, r"""<input.*?(?:name=["']([^'"]+).*?value=["']([^'"]*)['"]>|>)""") possibleParam = scrapertools.find_multiple_matches(data, r"""<input.*?(?:name=["']([^'"]+).*?value=["']([^'"]*)['"]>|>)""")
if possibleParam: if possibleParam:
post = urllib.urlencode({param[0]: param[1] for param in possibleParam if param[0]}) post = {param[0]: param[1] for param in possibleParam if param[0]}
data = httptools.downloadpage(page_url, headers=headers, post=post, follow_redirects=True).data if captcha: post['g-recaptcha-response'] = captcha
if post:
data = httptools.downloadpage(page_url, headers=headers, post=post, follow_redirects=True).data
elif captcha:
int_bckup_method()
elif captcha:
int_bckup_method()
else: else:
page_url = scrapertools.find_single_match(data, r"""<center><a href='(https?:\/\/wstream[^']+)'\s*title='bkg'""") return False, config.get_localized_string(707434)
if page_url:
data = httptools.downloadpage(page_url, headers=headers, follow_redirects=True).data
if "Not Found" in data or "File was deleted" in data: if "Not Found" in data or "File was deleted" in data:
return False, config.get_localized_string(70449) % 'Wstream' return False, config.get_localized_string(70449) % 'Wstream'
return True, "" else:
return True, ""
# Returns an array of possible video url's from the page_url # Returns an array of possible video url's from the page_url

Some files were not shown because too many files have changed in this diff Show More