# Copyright (C) 2017 Lunatixz # # # This file is part of iSpot. # # iSpot is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # iSpot is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with iSpot. If not, see . # -*- coding: utf-8 -*- import os, re, sys, time, datetime, traceback import urllib, urllib2, base64, HTMLParser, socket, json import xbmc, xbmcgui, xbmcplugin, xbmcvfs, xbmcaddon from simplecache import use_cache, SimpleCache ## GLOBALS ## baseurl='https://www.ispot.tv/' TIMEOUT = 15 # Plugin Info ADDON_ID = 'plugin.video.ispot' REAL_SETTINGS = xbmcaddon.Addon(id=ADDON_ID) SETTINGS_LOC = REAL_SETTINGS.getAddonInfo('profile') ADDON_NAME = REAL_SETTINGS.getAddonInfo('name') ADDON_PATH = REAL_SETTINGS.getAddonInfo('path').decode('utf-8') ADDON_VERSION = REAL_SETTINGS.getAddonInfo('version') ICON = os.path.join(ADDON_PATH, 'icon.png') FANART = os.path.join(ADDON_PATH, 'fanart.jpg') # User Settings DEBUG = REAL_SETTINGS.getSetting('Enable_Debugging') == 'true' WEB = int(REAL_SETTINGS.getSetting('Preferred_WEB')) socket.setdefaulttimeout(TIMEOUT) def log(msg, level=xbmc.LOGDEBUG): msg = stringify(msg[:1000]) if DEBUG == False and level == xbmc.LOGDEBUG: return if level == xbmc.LOGERROR: msg += ' ,' + traceback.format_exc() xbmc.log(ADDON_ID + '-' + ADDON_VERSION + '-' + msg, level) def ascii(string): if isinstance(string, basestring): if isinstance(string, unicode): string = string.encode('ascii', 'ignore') return string def uni(string): if isinstance(string, basestring): if isinstance(string, unicode): string = string.encode('utf-8', 'ignore' ) else: string = ascii(string) return string def stringify(string): if isinstance(string, list): string = stringify(string[0]) elif isinstance(string, (int, float, long, complex, bool)): string = str(string) elif isinstance(string, (str, unicode)): string = uni(string) elif not isinstance(string, (str, unicode)): string = ascii(string) if isinstance(string, basestring): return string return '' def cleanString(string): newstr = uni(string) newstr = newstr.replace('&', '&') newstr = newstr.replace('>', '>') newstr = newstr.replace('<', '<') newstr = newstr.replace('"', '"') return uni(newstr) def uncleanString(string): newstr = uni(string) newstr = newstr.replace('&', '&') newstr = newstr.replace('>', '>') newstr = newstr.replace('<', '<') newstr = newstr.replace('"', '"') return uni(newstr) def chunks(l, n=25): """Yield successive n-sized chunks from l.""" for i in range(0, len(l), n): yield l[i:i + n] def get_params(): param=[] if len(sys.argv[2])>=2: params=sys.argv[2] cleanedparams=params.replace('?','') if (params[len(params)-1]=='/'): params=params[0:len(params)-2] pairsofparams=cleanedparams.split('&') param={} for i in range(len(pairsofparams)): splitparams={} splitparams=pairsofparams[i].split('=') if (len(splitparams))==2: param[splitparams[0]]=splitparams[1] return param class iSpot(): def __init__(self): log('__init__') self.PAGE = 25 self.cache = SimpleCache() def onInit(self): log('onInit') def open_url(self, url, userpass=None): try: request = urllib2.Request(url) if userpass: user, password = userpass.split(':') base64string = base64.encodestring('%s:%s' % (user, password)) request.add_header("Authorization", "Basic %s" % base64string) else: request.add_header('User-Agent','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11') page = urllib2.urlopen(request) return page except urllib2.HTTPError, e: log("open_url failed " + str(e)) ''' ispot is not updated regularly, no need to burden site. Cache for 14days ''' @use_cache(14) def read_url_cached(self, url, userpass=False): log("read_url_cached") return self.open_url(url, userpass).read() def fillMenu(self): self.addDir('Browse Commercials','http://www.ispot.tv/browse',1) self.addDir('Event Commercials','http://www.ispot.tv/events',1) def parseDuration(self, duration): duration = 'P'+duration """ Parse and prettify duration from youtube duration format """ DURATION_REGEX = r'P(?P[0-9]+D)?T(?P[0-9]+H)?(?P[0-9]+M)?(?P[0-9]+S)?' NON_DECIMAL = re.compile(r'[^\d]+') duration_dict = re.search(DURATION_REGEX, duration).groupdict() converted_dict = {} # convert all values to ints, remove nones for a, x in duration_dict.iteritems(): if x is not None: converted_dict[a] = int(NON_DECIMAL.sub('', x)) x = time.strptime(str(datetime.timedelta(**converted_dict)).split(',')[0],'%H:%M:%S') return int(datetime.timedelta(hours=x.tm_hour,minutes=x.tm_min,seconds=x.tm_sec).total_seconds()) def PageParse(self, url): log('PageParse, url = ' + url) link = self.read_url_cached(url) if url.startswith('http://www.ispot.tv/events'): catlink = re.compile('(.+?)').findall(link) else: catlink = re.compile('').findall(link) for i in range(len(catlink)): # filter unwanted elements if not catlink[i][1].startswith(('<','&','Request Trial','Terms &','Most Engaging Ads','Top Spenders')) and url != baseurl + catlink[i][0]: self.addDir(catlink[i][1],baseurl + catlink[i][0],1) self.PAGECNT = 0 self.LINKLST = [] self.titleLST =[] if url.startswith('http://www.ispot.tv/event'): self.LINKLST = re.compile('').findall(link) else: self.LINKLST = re.compile('').findall(link) #parse dir for links self.buildLink(self.LINKLST) def buildLink(self, lst, pg=False): log('buildLink') if pg == False: catlink = lst self.LinkParse(catlink, 0, len(lst)) else: self.PAGEMAX = len(lst) self.PAGECNT = self.PAGECNT + self.PAGE if self.PAGECNT >= self.PAGEMAX: self.PAGECNT = 0 return #page parse dir links in chunks catlink = (list(chunks(lst[self.PAGECNT:],self.PAGE))[0]) self.LinkParse(catlink, self.PAGECNT, self.PAGEMAX) def LinkParse(self, catlink, cnt, cntmax): log('LinkParse') for i in range(len(catlink)): try: link = catlink[i] link = link.split('"')[0] link = link.split('"')[0] link = baseurl + 'ad/' + link link = self.read_url_cached(link) if len(link) == 0: link = baseurl + 'events/' + link link = self.read_url_cached(link) try: if WEB == 1: url = ((re.compile("data-webm="'(.+?)"').findall(link))[0]).replace('https','http').replace('"','') else: url = ((re.compile("data-mp4="'(.+?)"').findall(link))[0]).replace('https','http').replace('"','') except: url = (re.compile("file: '(.+?)'").findall(link)) title = (re.compile('(.+?)').findall(link))[0] description = HTMLParser.HTMLParser().unescape((re.compile('').findall(link))[0]) thumburl = ((re.compile('').findall(link))[0]) ##adDict = (re.compile("data-ad='(.+?)}'").findall(link))[0] + '}' try: duration = self.parseDuration((re.compile('').findall(link))[0]) except: duration = 30 infoList = {} infoList['mediatype'] = 'video' infoList['Duration'] = int(duration) infoList['Title'] = uni(title) infoList['Plot'] = uni(description) infoArt = {} infoArt['thumb'] = (thumburl or ICON) infoArt['poster'] = (thumburl or ICON) infoArt['fanart'] = (thumburl or ICON) # Avoid duplicates if title not in self.titleLST: self.titleLST.append(title) self.addLink(title,url, 2, infoList, infoArt, len(catlink)) except Exception,e: log('PageParse, failed ' + str(e)) #todo pagination? # if cnt < cntmax: # self.addDir('Next Page','Next',1) def LinkPlay(self, name, url): listitem = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = uncleanString(name) log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo( type="Video", infoLabels={"label":name,"title":name} ) else: liz.setInfo(type="Video", infoLabels=infoList) if infoArt == False: liz.setArt({'thumb':ICON,'fanart':FANART}) else: liz.setArt(infoArt) u=sys.argv[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,totalItems=total) def addDir(self, name, u, mode, infoList=False, infoArt=False): name = uncleanString(name) log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) else: liz.setInfo(type="Video", infoLabels=infoList) if infoArt == False: liz.setArt({'thumb':ICON,'fanart':FANART}) else: liz.setArt(infoArt) u=sys.argv[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True) params=get_params() try: url=urllib.unquote_plus(params["url"]) except: url=None try: name=urllib.unquote_plus(params["name"]) except: name=None try: mode=int(params["mode"]) except: mode=None log("Mode: "+str(mode)) log("URL : "+str(url)) log("Name: "+str(name)) if mode==None: iSpot().PageParse('http://www.ispot.tv/browse') elif mode == 1: iSpot().PageParse(url) elif mode == 2: iSpot().LinkPlay(name, url) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_NONE ) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_LABEL ) xbmcplugin.endOfDirectory(int(sys.argv[1]),cacheToDisc=True)