class NewsOn(object): def __init__(self): log('__init__') self.cache = SimpleCache() self.stateMenu = self.getStates() def openURL(self, url): try: cacheResponce = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponce: request = urllib2.Request(url) request.add_header('Accept-encoding', 'gzip') request.add_header( 'User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' ) responce = urllib2.urlopen(request, timeout=TIMEOUT) log(responce.headers['content-type']) log(responce.headers['content-encoding']) if responce.info().get('content-encoding') == 'gzip': buf = StringIO(responce.read()) f = gzip.GzipFile(fileobj=buf) results = json.loads(f.read()) else: results = json.load(responce) responce.close() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, results, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) except urllib2.URLError, e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout, e: log("openURL Failed! " + str(e), xbmc.LOGERROR)
class Haystack(): def __init__(self): log('__init__') self.cache = SimpleCache() self.getCategories() def openURL(self, url): try: cacheResponce = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponce: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') responce = urllib2.urlopen(request, timeout = TIMEOUT) soup = BeautifulSoup(responce.read()) data = (soup.find('script').text).rstrip() data = (data.split('window.__INITIAL_STATE__ = ')[1]).replace(';','') results = json.loads(data) responce.close() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, results, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except urllib2.URLError, e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout, e: log("openURL Failed! " + str(e), xbmc.LOGERROR)
class News12(): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponse: request = urllib2.Request(url) request.add_header( 'User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' ) response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, response, expiration=datetime.timedelta(hours=6)) return self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) except urllib2.URLError, e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout, e: log("openURL Failed! " + str(e), xbmc.LOGERROR)
class GUI(xbmcgui.WindowXMLDialog): def __init__( self, *args, **kwargs ): self.isExiting = False self.cache = SimpleCache() self.baseAPI = BASE_API def log(self, msg, level=xbmc.LOGDEBUG): xbmc.log(ADDON_ID + '-' + ADDON_VERSION + '-' + msg, level) def onInit(self): self.winid = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId()) self.winid.setProperty('earth_animation', ANIMATION) self.winid.setProperty('earth_time', TIME) self.startRotation() def openURL(self, url): try: self.log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: cacheresponse = (urllib2.urlopen(urllib2.Request(url), timeout=15)).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(days=28)) return cacheresponse except Exception as e: self.log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(32001), ICON, 4000) return '' def setImage(self, id): try: results = json.loads(self.openURL('%s%s'%(BASE_URL,self.baseAPI))) self.getControl(id).setImage(results['photoUrl']) self.getControl(id+1).setLabel(('%s, %s'%(results.get('region',' '),results.get('country',''))).strip(' ,')) baseAPI = results['nextApi'] except: baseAPI = DEFAULT_JSON self.baseAPI = baseAPI def startRotation(self): self.currentID = IMG_CONTROLS[0] self.nextID = IMG_CONTROLS[1] self.setImage(self.currentID) while not KODI_MONITOR.abortRequested(): self.getControl(self.nextID).setVisible(False) self.getControl(self.currentID).setVisible(True) self.nextID = self.currentID self.currentID = CYC_CONTROL() self.setImage(self.currentID) if KODI_MONITOR.waitForAbort(TIMER) == True or self.isExiting == True: break REAL_SETTINGS.setSetting("Last",self.baseAPI) def onAction( self, action ): self.isExiting = True self.close()
class Region(object): def __init__(self, sysARG=sys.argv): self.cache = SimpleCache() self.token = (REAL_SETTINGS.getSetting('User_Token') or None) def buildHeader(self): header_dict = {} header_dict['Accept'] = 'application/json, text/javascript, */*; q=0.01' header_dict['Content-Type'] = 'application/json' header_dict['Connection'] = 'keep-alive' header_dict['Origin'] = BASE_URL header_dict['Referer'] = BASE_URL header_dict['Authorization'] = "Bearer %s" % self.token header_dict['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36' return header_dict def getURL(self, url, param={}, header={'Content-Type':'application/json'}, life=datetime.timedelta(minutes=5)): log('getURL, url = %s, header = %s'%(url, header)) cacheresponse = self.cache.get(ADDON_NAME + '.getURL, url = %s.%s.%s'%(url,param,header)) if not cacheresponse: try: req = requests.get(url, param, headers=header) try: cacheresponse = req.json() except: return {} req.close() self.cache.set(ADDON_NAME + '.getURL, url = %s.%s.%s'%(url,param,header), json.dumps(cacheresponse), expiration=life) return cacheresponse except Exception as e: log("getURL, Failed! %s"%(e), xbmc.LOGERROR) notificationDialog(LANGUAGE(30001)) return {} else: return json.loads(cacheresponse) def getCities(self): log("getCities") return self.getURL(BASE_API + '/dma',header=self.buildHeader()) def listRegions(self): log('listRegions') cities = sorted(self.getCities(), key=lambda k: k['name']) def getListItem(): for k in cities: if k.get('name') == 'Lab': continue listitem = xbmcgui.ListItem(k.get('name'),path=str(k.get('id'))) listitem.setArt({'thumb':k.get('imageLargeUrl')}) yield listitem listItems = list(getListItem()) pselect = ([idx for idx, dma in enumerate(listItems) if dma.getPath() == (REAL_SETTINGS.getSetting('User_Select_DMA') or REAL_SETTINGS.getSetting('User_DMA'))] or [-1])[0] select = selectDialog(listItems,header='%s %s'%(ADDON_NAME,LANGUAGE(49013)),preselect=pselect) REAL_SETTINGS.setSetting('User_Select_DMA' ,listItems[select].getPath()) REAL_SETTINGS.setSetting('User_Select_City',listItems[select].getLabel()) REAL_SETTINGS.openSettings()
class ALTPOSTERS(object): def __init__(self): self.cache = SimpleCache() title = (xbmc.getInfoLabel('ListItem.Title') or xbmc.getInfoLabel('ListItem.Label') or None) dbID = (xbmc.getInfoLabel('ListItem.DBID') or -1) if title is None or (xbmc.getInfoLabel('ListItem.DBTYPE') or '') != 'movie': xbmcgui.Dialog().notification(ADDON_NAME,LANGUAGE(30001), ICON, 4000) return self.searchTitle(title, dbID) def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') cacheresponse = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(days=28)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) return '' def searchTitle(self, title, dbID): log('searchTitle, title = ' + title) listItems = [] xbmc.executebuiltin('ActivateWindow(busydialog)') for idx in range(1,SEARCHDEPTH): try: SEARCHURL = 'http://www.alternativemovieposters.com/page/%d/?s=%s' soup = BeautifulSoup(self.openURL(SEARCHURL%(idx,urllib.quote_plus(title))), "html.parser") posters = soup('div' , {'class': 'fusion-image-wrapper'}) if not posters or len(posters) == 0: break for poster in posters: url = poster.find_all('img')[0].attrs['src'] listItems.append(xbmcgui.ListItem(poster.find_all('h4')[0].get_text(), thumbnailImage=url, path=url)) except: break xbmc.executebuiltin('Dialog.Close(busydialog)') if len(listItems) > 0: select = xbmcgui.Dialog().select(LANGUAGE(32001)%(title), listItems, useDetails=True) if select > -1: self.setImage(title, dbID, listItems[select].getPath()) else: xbmcgui.Dialog().notification(ADDON_NAME,LANGUAGE(32002)%(title), ICON, 4000) def setImage(self, title, dbID, url): log('setImage, url = ' + url) if not xbmcgui.Dialog().yesno(ADDON_NAME, LANGUAGE(32003)%(title)): return xbmcgui.Dialog().notification(ADDON_NAME,LANGUAGE(32004)%(title), ICON, 4000) json_query = ('{"jsonrpc":"2.0","method":"VideoLibrary.SetMovieDetails","params":{"movieid" : %s, "thumbnail" : "%s", "art" : %s},"id":1}' % (dbID,url,json.dumps({"poster":url}))) xbmc.executeJSONRPC(json_query) xbmc.executebuiltin("Container.Refresh")
class Newsy(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=6)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except urllib2.URLError, e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout, e: log("openURL Failed! " + str(e), xbmc.LOGERROR)
def retrieveCatalog(): try: cache = SimpleCache() catalog = cache.get(ADDON_NAME + '.catalog') if catalog: log("using cached catalog") if not catalog: log("downloading catalog") opener = FancyURLopener() f = opener.open(url) catalog = json.load(f) cache.set(ADDON_NAME + '.catalog', catalog, expiration=datetime.timedelta(hours=12)) return catalog except Exception as e: log("error retrieving catalog - " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30003), ICON, 4000) xbmc.executebuiltin('Action(PreviousMenu)') sys.exit(0)
class CBSN(object): def __init__(self, sysARG): log('__init__, sysARG = ' + str(sysARG)) self.sysARG = sysARG self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') cacheResponse = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheResponse, expiration=datetime.timedelta(minutes=5)) return cacheResponse except Exception, e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return ''
class NBC(object): def __init__(self): log('__init__') self.cache = SimpleCache() self.ydl = YoutubeDL() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponce = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponce: request = urllib2.Request(url) responce = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, responce, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) except urllib2.URLError, e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout, e: log("openURL Failed! " + str(e), xbmc.LOGERROR)
class CNN(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheresponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, response, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self): for item in MENU_ITEMS: self.addDir(item, JSON_URL % item.lower(), 1) self.addDir('Digital Shorts', SHORTS_URL, 2) self.addYoutube("Browse Youtube", 'plugin://plugin.video.youtube/user/CNN/') def browse(self, name, url): log('browse, ' + name) response = json.loads(self.openURL(url)) items = response['videos'] for item in items: try: runtime = item['duration'].split(':') if len(runtime) == 3: h, m, s = runtime duration = int(h) * 3600 + int(m) * 60 + int(s) else: m, s = runtime duration = int(m) * 60 + int(s) except: duration = item['duration'] label = item['headline'] thumb = 'http:%s' % item['fullsize_url'] infoLabels = { "mediatype": "episode", "label": label, "title": label, "duration": duration, "plot": item['description'], "genre": "News" } infoArt = { "thumb": thumb, "poster": thumb, "fanart": FANART, "icon": ICON, "logo": ICON } self.addLink(label, BASE_URL + item['clickback_url'], 9, infoLabels, infoArt, len(items)) def browseShorts(self, name, url): log('browseShorts, ' + name) soup = BeautifulSoup(self.openURL(url), "html.parser") videos = soup('article', {'class': 'cd'}) for video in videos: vid_url = video('h3', {'class': 'cd__headline'})[0].find('a')['href'] if not vid_url.startswith('http'): vid_url = BASE_URL + vid_url if vid_url.startswith('http://cnn.it'): continue if not '/video' in vid_url: continue thumb = (video('div', {'class': 'cd__wrapper'})[0]) try: thumb = 'http:' + (json.loads( (re.search('"large":(.*?)"},', str(thumb)).group(1) + '"}'))['uri']) except: thumb = ICON results = video('div', {'class': 'cd__content'})[0] try: title = results('div', {'class': 'cd__kicker'})[0].get_text() except: title = None subtitle = results('span', {'class': 'cd__headline-text'})[0].get_text() label = '%s - %s' % (title, subtitle) if title is not None else subtitle try: plot = results('div', {'class': 'cd__description'})[0].get_text() except: plot = subtitle duration = 0 infoLabels = { "mediatype": "episode", "label": label, "title": label, "duration": duration, "plot": plot } infoArt = { "thumb": thumb, "poster": thumb, "fanart": FANART, "icon": ICON, "logo": ICON } self.addLink(label, vid_url, 9, infoLabels, infoArt, len(videos)) def playVideo(self, name, url, liz=None): log('playVideo') info = getVideoInfo(url, QUALITY, True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([ x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en', '') if 'url' in x ]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label": name, "title": name}) liz.setArt({'thumb': ICON, 'fanart': FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=liz, isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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 = name.encode("utf-8") log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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)
class CNN(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self): for item in MENU_ITEMS: self.addDir(item, JSON_URL%item.lower(), 1) self.addDir('Digital Shorts', SHORTS_URL, 2) self.addYoutube("Browse Youtube" , 'plugin://plugin.video.youtube/user/CNN/') def browse(self, name, url): log('browse, ' + name) response = json.loads(self.openURL(url)) items = response['videos'] for item in items: try: runtime = item['duration'].split(':') if len(runtime) == 3: h, m, s = runtime duration = int(h) * 3600 + int(m) * 60 + int(s) else: m, s = runtime duration = int(m) * 60 + int(s) except: duration = item['duration'] label = item['headline'] thumb = 'http:%s'%item['fullsize_url'] infoLabels = {"mediatype":"episode","label":label ,"title":label,"duration":duration,"plot":item['description'],"genre":"News"} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, BASE_URL + item['clickback_url'], 9, infoLabels, infoArt, len(items)) def browseShorts(self, name, url): log('browseShorts, ' + name) soup = BeautifulSoup(self.openURL(url), "html.parser") videos = soup('article', {'class': 'cd'}) for video in videos: vid_url = video('h3', {'class': 'cd__headline'})[0].find('a')['href'] if not vid_url.startswith('http'): vid_url = BASE_URL + vid_url if vid_url.startswith('http://cnn.it'): continue if not '/video' in vid_url: continue thumb = (video('div', {'class': 'cd__wrapper'})[0]) try: thumb = 'http:' + (json.loads((re.search('"large":(.*?)"},', str(thumb)).group(1) + '"}'))['uri']) except: thumb = ICON results = video('div', {'class': 'cd__content'})[0] try: title = results('div', {'class': 'cd__kicker'})[0].get_text() except: title = None subtitle = results('span', {'class': 'cd__headline-text'})[0].get_text() label = '%s - %s'%(title, subtitle) if title is not None else subtitle try: plot = results('div', {'class': 'cd__description'})[0].get_text() except: plot = subtitle duration = 0 infoLabels = {"mediatype":"episode","label":label ,"title":label,"duration":duration,"plot":plot} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, vid_url, 9, infoLabels, infoArt, len(videos)) def playVideo(self, name, url, liz=None): log('playVideo') info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class Installer(object): def __init__(self): self.myMonitor = xbmc.Monitor() self.cache = SimpleCache() if not self.chkVersion(): return self.lastURL = (REAL_SETTINGS.getSetting("LastURL") or self.buildMain()) self.lastPath = REAL_SETTINGS.getSetting("LastPath") self.selectPath(self.lastURL) def disable(self, build): xbmcgui.Dialog().notification(ADDON_NAME, VERSION, ICON, 8000) if not xbmcgui.Dialog().yesno(ADDON_NAME, LANGUAGE(30011)%(build), LANGUAGE(30012)): return False xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method":"Addons.SetAddonEnabled","params":{"addonid":"%s","enabled":false}, "id": 1}'%(ADDON_ID)) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30009), ICON, 4000) return False def chkVersion(self): try: build = int(re.compile('Android (\d+)').findall(VERSION)[0]) except: build = MIN_VER if build >= MIN_VER: return True else: return self.disable(build) def openURL(self, url): if url is None: return log('openURL, url = ' + str(url)) try: cacheResponce = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponce: req = urllib.request.Request(url) with urllib.request.urlopen(req) as response: cacheResponce = response.read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheResponce, expiration=datetime.timedelta(minutes=5)) return BeautifulSoup(cacheResponce, "html.parser") except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return None def getItems(self, soup): try: #folders items = (soup.find_all('tr')) del items[0] except: #files items = (soup.find_all('a')) return [x.get_text() for x in items if x.get_text() is not None] def buildMain(self): tmpLST = [] for label in sorted(BUILD_OPT.keys()): tmpLST.append(xbmcgui.ListItem(label.title(), BUILD_OPT[label], DROID_URL%(label,PLATFORM))) select = selectDialog(ADDON_NAME, tmpLST) if select is None: return return tmpLST[select].getPath() def buildItems(self, url): soup = self.openURL(url) if soup is None: return for item in self.getItems(soup): try: #folders label, label2 = re.compile("(.*?)/-(.*)").match(item).groups() if label == PLATFORM: label2 = LANGUAGE(30014)%PLATFORM elif label.lower() == BRANCH.lower(): label2 = LANGUAGE(30022)%(BUILD.get('major',''),BUILD.get('minor',''),BUILD.get('revision','')) else: label2 = '' #Don't use time-stamp for folders yield (xbmcgui.ListItem(label.title(),label2,(url + label))) except: #files label, label2 = re.compile("(.*?)\s(.*)").match(item).groups() if '.apk' in label: yield (xbmcgui.ListItem('%s.apk'%label.split('.apk')[0],'%s %s'%(label.split('.apk')[1], label2.replace('MiB','MB ').strip()),'%s%s.apk'%(url,label.split('.apk')[0]))) def setLastPath(self, url, path): REAL_SETTINGS.setSetting("LastURL",url) REAL_SETTINGS.setSetting("LastPath",path) def selectPath(self, url, bypass=False): log('selectPath, url = ' + str(url)) newURL = url while not self.myMonitor.abortRequested(): items = list(self.buildItems(url)) if len(items) == 0: break elif len(items) == 2 and not bypass and items[0].getLabel().lower() == 'parent directory' and not items[1].getLabel().startswith('.apk'): select = 1 #If one folder bypass selection. else: select = selectDialog(url.replace(BASE_URL,'./').replace('//','/'), items) if select is None: return #return on cancel. label = items[select].getLabel() newURL = items[select].getPath() preURL = url.rsplit('/', 2)[0] + '/' if newURL.endswith('.apk'): dest = xbmc.translatePath(os.path.join(SETTINGS_LOC,label)) self.setLastPath(url,dest) return self.downloadAPK(newURL,dest) elif label.lower() == 'parent directory' and "android" in preURL.lower(): return self.selectPath(preURL, True) elif label.lower() == 'parent directory' and "android" not in preURL.lower(): return self.selectPath(self.buildMain(), False) url = newURL + '/' def fileExists(self, dest): if xbmcvfs.exists(dest): message = '%s %s' % (LANGUAGE(30004), dest.rsplit('/', 1)[-1]) if not xbmcgui.Dialog().yesno(ADDON_NAME, message, nolabel=LANGUAGE(30005), yeslabel=LANGUAGE(30006)): return True elif CLEAN and xbmcvfs.exists(self.lastPath): self.deleleAPK(self.lastPath) return False def deleleAPK(self, path): count = 0 #some file systems don't release the file lock instantly. while not self.myMonitor.abortRequested() and count < 3: count += 1 if self.myMonitor.waitForAbort(1): return try: if xbmcvfs.delete(path): return except BaseException: pass def downloadAPK(self, url, dest): if self.fileExists(dest): return self.installAPK(dest) start_time = time.time() dia = xbmcgui.DialogProgress() fle = dest.rsplit('/', 1)[1] dia.create(ADDON_NAME, LANGUAGE(30002)%fle) try: urlretrieve(url.rstrip('/'), dest, lambda nb, bs, fs: self.pbhook(nb, bs, fs, dia, start_time, fle)) except Exception as e: dia.close() xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) log("downloadAPK, Failed! (%s) %s"%(url,str(e)), xbmc.LOGERROR) return self.deleleAPK(dest) dia.close() return self.installAPK(dest) def pbhook(self, numblocks, blocksize, filesize, dia, start_time, fle): try: percent = min(numblocks * blocksize * 100 / filesize, 100) currently_downloaded = float(numblocks) * blocksize / (1024 * 1024) bps_speed = numblocks * blocksize / (time.time() - start_time) eta = (filesize - numblocks * blocksize) / bps_speed if bps_speed > 0 else 0 kbps_speed = bps_speed / 1024 if eta < 0: eta = divmod(0, 60) else: eta = divmod(eta, 60) total = (float(filesize) / (1024 * 1024)) message = '%.02f MB of %.02f MB | [B]Speed:[/B] %.02f KB/s | ' % (currently_downloaded, total, kbps_speed) message += '[B]ETA:[/B] %02d:%02d' % eta dia.update(int(percent), message) except Exception as e: log("pbhook failed! %s" + str(e), xbmc.LOGERROR) dia.update(100) if dia.iscanceled(): raise Exception def installAPK(self, apkfile): xbmc.executebuiltin('StartAndroidActivity(%s,,,"content://%s")'%(FMANAGER,apkfile))
class StudioLogos(): """Helper class for studio logo images""" def __init__(self, simplecache=None): """Initialize - optionaly provide simplecache object""" if not simplecache: from simplecache import SimpleCache self.cache = SimpleCache() else: self.cache = simplecache @use_cache(14) def get_studio_logo(self, studios, lookup_path): """get the studio logo for the given studio string(s)""" if not studios: return {} result = {} if not isinstance(studios, list): studios = studios.split(" / ") result["Studio"] = studios[0] result['Studios'] = "[CR]".join(studios) result['StudioLogo'] = self.match_studio_logo(studios, self.get_studio_logos(lookup_path)) return result def get_studio_logos(self, lookup_path): """get all studio logos""" cache_str = "SkinHelper.StudioLogos" cache = self.cache.get(cache_str, checksum=lookup_path) if cache: return cache # no cache - start lookup all_logos = {} if lookup_path.startswith("resource://"): all_logos = self.get_resource_addon_files(lookup_path) else: if not (lookup_path.endswith("/") or lookup_path.endswith("\\")): lookup_path = lookup_path + os.sep all_logos = self.list_files_in_path(lookup_path) # save in cache and return self.cache.set(cache_str, all_logos, expiration=timedelta(days=14), checksum=lookup_path) return all_logos @staticmethod def match_studio_logo(studios, studiologos): """try to find a matching studio logo""" studiologo = "" for studio in studios: if studiologo: break studio = studio.lower() # find logo normal if studio in studiologos: studiologo = studiologos[studio] if not studiologo: # find logo by substituting characters if " (" in studio: studio = studio.split(" (")[0] if studio in studiologos: studiologo = studiologos[studio] if not studiologo: # find logo by substituting characters for pvr channels if " HD" in studio: studio = studio.replace(" HD", "") elif " " in studio: studio = studio.replace(" ", "") if studio in studiologos: studiologo = studiologos[studio] return studiologo @use_cache(90) def get_resource_addon_files(self, resourcepath): """get listing of all files (eg studio logos) inside a resource image addonName read data from our permanent cache file to prevent that we have to query the resource addon""" return self.list_files_in_path(resourcepath) @staticmethod def list_files_in_path(filespath): """used for easy matching of studio logos""" all_files = {} dirs, files = xbmcvfs.listdir(filespath) if "/" in filespath: sep = "/" else: sep = "\\" for file in files: file = try_decode(file) name = file.split(".png")[0].lower() all_files[name] = filespath + file for directory in dirs: directory = try_decode(directory) files = xbmcvfs.listdir(os.path.join(filespath, directory) + sep)[1] for file in files: file = try_decode(file) name = directory + "/" + file.split(".png")[0].lower() all_files[name] = filespath + directory + sep + file # return the list return all_files
class Sky(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponse: request = urllib2.Request(url) request.add_header( 'User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' ) response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, response, expiration=datetime.timedelta(hours=6)) return self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMainMenu(self): self.addLink(LANGUAGE(30002), '', 0) self.addYoutube(LANGUAGE(30003), 'plugin://plugin.video.youtube/user/skynews/') def buildLiveLink(self): try: link = 'http:' + BeautifulSoup( self.openURL(LIVEURL), "html.parser")('div', { 'class': 'video-embed' })[0].find('iframe').get('src') print self.resolveYoutube(link) self.playVideo(LANGUAGE(30004), self.resolveYoutube(link)) except: self.playVideo(LANGUAGE(30004), YTURL + 'XOacA3RYrXk') def resolveYoutube(self, link): if len(re.findall('http[s]?://www.youtube.com/watch', link)) > 0: return YTURL + link.split('/watch?v=')[1] elif len(re.findall('http[s]?://www.youtube.com/embed', link)) > 0: return YTURL + link.split('/embed/')[1].split('?autoplay=1')[0] elif len(re.findall('http[s]?://youtu.be/', link)) > 0: return YTURL + link.split('/youtu.be/')[1] def playVideo(self, name, url, liz=None): log('playVideo') if not liz: liz = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, title, url): liz = xbmcgui.ListItem(title) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label": title, "title": title}) liz.setArt({'thumb': ICON, 'fanart': FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=liz, isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "label": name, "title": name, "genre": "News" }) 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)
class Tmdb(object): '''get metadata from tmdb''' api_key = None # public var to be set by the calling addon def __init__(self, simplecache=None, api_key=None): '''Initialize - optionaly provide simplecache object''' if not simplecache: from simplecache import SimpleCache self.cache = SimpleCache() else: self.cache = simplecache addon = xbmcaddon.Addon(id=ADDON_ID) # personal api key (preferred over provided api key) api_key = addon.getSetting("tmdb_apikey") if api_key: self.api_key = api_key del addon def search_movie(self, title, year="", manual_select=False, ignore_cache=False): ''' Search tmdb for a specific movie, returns full details of best match parameters: title: (required) the title of the movie to search for year: (optional) the year of the movie to search for (enhances search result if supplied) manual_select: (optional) if True will show select dialog with all results ''' details = self.select_best_match(self.search_movies(title, year), manual_select=manual_select) if details: details = self.get_movie_details(details["id"]) return details @use_cache(30) def search_movieset(self, title): '''search for movieset details providing the title of the set''' details = {} params = {"query": title, "language": KODI_LANGUAGE} result = self.get_data("search/collection", params) if result: set_id = result[0]["id"] details = self.get_movieset_details(set_id) return details @use_cache(4) def search_tvshow(self, title, year="", manual_select=False, ignore_cache=False): ''' Search tmdb for a specific movie, returns full details of best match parameters: title: (required) the title of the movie to search for year: (optional) the year of the movie to search for (enhances search result if supplied) manual_select: (optional) if True will show select dialog with all results ''' details = self.select_best_match(self.search_tvshows(title, year), manual_select=manual_select) if details: details = self.get_tvshow_details(details["id"]) return details @use_cache(4) def search_video(self, title, prefyear="", preftype="", manual_select=False, ignore_cache=False): ''' Search tmdb for a specific entry (can be movie or tvshow), returns full details of best match parameters: title: (required) the title of the movie/tvshow to search for prefyear: (optional) prefer result if year matches preftype: (optional) prefer result if type matches manual_select: (optional) if True will show select dialog with all results ''' results = self.search_videos(title) details = self.select_best_match(results, prefyear=prefyear, preftype=preftype, preftitle=title, manual_select=manual_select) if details and details["media_type"] == "movie": details = self.get_movie_details(details["id"]) elif details and "tv" in details["media_type"]: details = self.get_tvshow_details(details["id"]) return details @use_cache(4) def search_videos(self, title): ''' Search tmdb for a specific entry (can be movie or tvshow), parameters: title: (required) the title of the movie/tvshow to search for ''' results = [] page = 1 maxpages = 5 while page < maxpages: params = {"query": title, "language": KODI_LANGUAGE, "page": page} subresults = self.get_data("search/multi", params) page += 1 if subresults: for item in subresults: if item["media_type"] in ["movie", "tv"]: results.append(item) else: break return results @use_cache(4) def search_movies(self, title, year=""): ''' Search tmdb for a specific movie, returns a list of all closest matches parameters: title: (required) the title of the movie to search for year: (optional) the year of the movie to search for (enhances search result if supplied) ''' params = {"query": title, "language": KODI_LANGUAGE} if year: params["year"] = try_parse_int(year) return self.get_data("search/movie", params) @use_cache(4) def search_tvshows(self, title, year=""): ''' Search tmdb for a specific tvshow, returns a list of all closest matches parameters: title: (required) the title of the tvshow to search for year: (optional) the first air date year of the tvshow to search for (enhances search result if supplied) ''' params = {"query": title, "language": KODI_LANGUAGE} if year: params["first_air_date_year"] = try_parse_int(year) return self.get_data("search/tv", params) def get_actor(self, name): ''' Search tmdb for a specific actor/person, returns the best match as kodi compatible dict required parameter: name --> the name of the person ''' params = {"query": name, "language": KODI_LANGUAGE} result = self.get_data("search/person", params) if result: result = result[0] cast_thumb = "http://image.tmdb.org/t/p/original%s" % result[ "profile_path"] if result["profile_path"] else "" item = { "name": result["name"], "thumb": cast_thumb, "roles": [ item["title"] if item.get("title") else item["name"] for item in result["known_for"] ] } return item else: return {} def get_movie_details(self, movie_id): '''get all moviedetails''' params = { "append_to_response": "keywords,videos,credits,images", "include_image_language": "%s,en" % KODI_LANGUAGE, "language": KODI_LANGUAGE } return self.map_details(self.get_data("movie/%s" % movie_id, params), "movie") def get_movieset_details(self, movieset_id): '''get all moviesetdetails''' details = {"art": {}} params = {"language": KODI_LANGUAGE} result = self.get_data("collection/%s" % movieset_id, params) if result: details["title"] = result["name"] details["plot"] = result["overview"] details["tmdb_id"] = result["id"] details["art"][ "poster"] = "http://image.tmdb.org/t/p/original%s" % result[ "poster_path"] details["art"][ "fanart"] = "http://image.tmdb.org/t/p/original%s" % result[ "backdrop_path"] details["totalmovies"] = len(result["parts"]) return details def get_tvshow_details(self, tvshow_id): '''get all tvshowdetails''' params = { "append_to_response": "keywords,videos,external_ids,credits,images", "include_image_language": "%s,en" % KODI_LANGUAGE, "language": KODI_LANGUAGE } return self.map_details(self.get_data("tv/%s" % tvshow_id, params), "tvshow") def get_videodetails_by_externalid(self, extid, extid_type): '''get metadata by external ID (like imdbid)''' params = {"external_source": extid_type, "language": KODI_LANGUAGE} results = self.get_data("find/%s" % extid, params) if results and results["movie_results"]: return self.get_movie_details(results["movie_results"][0]["id"]) elif results and results["tv_results"]: return self.get_tvshow_details(results["tv_results"][0]["id"]) return {} def get_data(self, endpoint, params): '''helper method to get data from tmdb json API''' if self.api_key: # addon provided or personal api key params["api_key"] = self.api_key rate_limit = None expiration = datetime.timedelta(days=7) else: # fallback api key (rate limited !) params["api_key"] = "80246691939720672db3fc71c74e0ef2" # without personal (or addon specific) api key = rate limiting and older info from cache rate_limit = ("themoviedb.org", 10) expiration = datetime.timedelta(days=60) cachestr = "tmdb.%s" % params.itervalues() cache = self.cache.get(cachestr) if cache: # data obtained from cache result = cache else: # no cache, grab data from API url = u'http://api.themoviedb.org/3/%s' % endpoint result = get_json(url, params, ratelimit=rate_limit) # make sure that we have a plot value (if localized value fails, fallback to english) if result and "language" in params and "overview" in result: if not result["overview"] and params["language"] != "en": params["language"] = "en" result2 = get_json(url, params) if result2 and result2.get("overview"): result = result2 self.cache.set(url, result, expiration=expiration) return result def map_details(self, data, media_type): '''helper method to map the details received from tmdb to kodi compatible formatting''' if not data: return {} details = {} details["tmdb_id"] = data["id"] details["rating"] = data["vote_average"] details["votes"] = data["vote_count"] details["rating.tmdb"] = data["vote_average"] details["votes.tmdb"] = data["vote_count"] details["popularity"] = data["popularity"] details["popularity.tmdb"] = data["popularity"] details["plot"] = data["overview"] details["genre"] = [item["name"] for item in data["genres"]] details["homepage"] = data["homepage"] details["status"] = data["status"] details["cast"] = [] details["castandrole"] = [] details["writer"] = [] details["director"] = [] details["media_type"] = media_type # cast if "credits" in data: if "cast" in data["credits"]: for cast_member in data["credits"]["cast"]: cast_thumb = "" if cast_member["profile_path"]: cast_thumb = "http://image.tmdb.org/t/p/original%s" % cast_member[ "profile_path"] details["cast"].append({ "name": cast_member["name"], "role": cast_member["character"], "thumbnail": cast_thumb }) details["castandrole"].append( (cast_member["name"], cast_member["character"])) # crew (including writers and directors) if "crew" in data["credits"]: for crew_member in data["credits"]["crew"]: cast_thumb = "" if crew_member["profile_path"]: cast_thumb = "http://image.tmdb.org/t/p/original%s" % crew_member[ "profile_path"] if crew_member["job"] in ["Author", "Writer"]: details["writer"].append(crew_member["name"]) if crew_member["job"] in [ "Producer", "Executive Producer" ]: details["director"].append(crew_member["name"]) if crew_member["job"] in [ "Producer", "Executive Producer", "Author", "Writer" ]: details["cast"].append({ "name": crew_member["name"], "role": crew_member["job"], "thumbnail": cast_thumb }) # artwork details["art"] = {} if data.get("images"): if data["images"].get("backdrops"): fanarts = self.get_best_images(data["images"]["backdrops"]) details["art"]["fanarts"] = fanarts details["art"]["fanart"] = fanarts[0] if fanarts else "" if data["images"].get("posters"): posters = self.get_best_images(data["images"]["posters"]) details["art"]["posters"] = posters details["art"]["poster"] = posters[0] if posters else "" if not details["art"].get("poster") and data.get("poster_path"): details["art"][ "poster"] = "http://image.tmdb.org/t/p/original%s" % data[ "poster_path"] if not details["art"].get("fanart") and data.get("backdrop_path"): details["art"][ "fanart"] = "http://image.tmdb.org/t/p/original%s" % data[ "backdrop_path"] # movies only if media_type == "movie": details["title"] = data["title"] details["originaltitle"] = data["original_title"] if data["belongs_to_collection"]: details["set"] = data["belongs_to_collection"].get("name", "") if data.get("release_date"): details["premiered"] = data["release_date"] details["year"] = try_parse_int( data["release_date"].split("-")[0]) details["tagline"] = data["tagline"] if data["runtime"]: details["runtime"] = data["runtime"] * 60 details["imdbnumber"] = data["imdb_id"] details["budget"] = data["budget"] details["budget.formatted"] = int_with_commas(data["budget"]) details["revenue"] = data["revenue"] details["revenue.formatted"] = int_with_commas(data["revenue"]) if data.get("production_companies"): details["studio"] = [ item["name"] for item in data["production_companies"] ] if data.get("production_countries"): details["country"] = [ item["name"] for item in data["production_countries"] ] if data.get("keywords"): details["tag"] = [ item["name"] for item in data["keywords"]["keywords"] ] # tvshows only if media_type == "tvshow": details["title"] = data["name"] details["originaltitle"] = data["original_name"] if data.get("created_by"): details["director"] += [ item["name"] for item in data["created_by"] ] if data.get("episode_run_time"): details["runtime"] = data["episode_run_time"][0] * 60 if data.get("first_air_date"): details["premiered"] = data["first_air_date"] details["year"] = try_parse_int( data["first_air_date"].split("-")[0]) if "last_air_date" in data: details["lastaired"] = data["last_air_date"] if data.get("networks"): details["studio"] = [item["name"] for item in data["networks"]] if "origin_country" in data: details["country"] = data["origin_country"] if data.get("external_ids"): details["imdbnumber"] = data["external_ids"].get("imdb_id", "") details["tvdb_id"] = data["external_ids"].get("tvdb_id", "") if "results" in data["keywords"]: details["tag"] = [ item["name"] for item in data["keywords"]["results"] ] # trailer for video in data["videos"]["results"]: if video["site"] == "YouTube" and video["type"] == "Trailer": details[ "trailer"] = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s' % video[ "key"] break return details @staticmethod def get_best_images(images): '''get the best 5 images based on number of likes and the language''' for image in images: score = 0 score += image["vote_count"] score += image["vote_average"] * 10 score += image["height"] if "iso_639_1" in image: if image["iso_639_1"] == KODI_LANGUAGE: score += 1000 image["score"] = score if not image["file_path"].startswith("http"): image[ "file_path"] = "http://image.tmdb.org/t/p/original%s" % image[ "file_path"] images = sorted(images, key=itemgetter("score"), reverse=True) return [image["file_path"] for image in images] @staticmethod def select_best_match(results, prefyear="", preftype="", preftitle="", manual_select=False): '''helper to select best match or let the user manually select the best result from the search''' details = {} # score results if one or more preferences are given if results and (prefyear or preftype or preftitle): newdata = [] preftitle = preftitle.lower() for item in results: item["score"] = 0 itemtitle = item["title"] if item.get( "title") else item["name"] itemtitle = itemtitle.lower() itemorgtitle = item["original_title"] if item.get( "original_title") else item["original_name"] itemorgtitle = itemorgtitle.lower() # high score if year matches if prefyear: if item.get("first_air_date" ) and prefyear in item["first_air_date"]: item["score"] += 800 # matches preferred year if item.get("release_date" ) and prefyear in item["release_date"]: item["score"] += 800 # matches preferred year # find exact match on title if preftitle and preftitle == itemtitle: item["score"] += 1000 # exact match! if preftitle and preftitle == itemorgtitle: item["score"] += 1000 # exact match! # match title by replacing some characters if preftitle and get_compare_string( preftitle) == get_compare_string(itemtitle): item["score"] += 750 if preftitle and get_compare_string( preftitle) == get_compare_string(itemorgtitle): item["score"] += 750 # add SequenceMatcher score to the results if preftitle: stringmatchscore = SM( None, preftitle, itemtitle).ratio() + SM( None, preftitle, itemorgtitle).ratio() if stringmatchscore > 1.6: item["score"] += stringmatchscore * 250 # higher score if result ALSO matches our preferred type or native language # (only when we already have a score) if item["score"]: if preftype and (item["media_type"] in preftype) or ( preftype in item["media_type"]): item["score"] += 250 # matches preferred type if item["original_language"] == KODI_LANGUAGE: item["score"] += 500 # native language! if KODI_LANGUAGE.upper() in item.get("origin_country", []): item["score"] += 500 # native language! if KODI_LANGUAGE in item.get("languages", []): item["score"] += 500 # native language! if item["score"] > 500 or manual_select: newdata.append(item) results = sorted(newdata, key=itemgetter("score"), reverse=True) if results and manual_select: # show selectdialog to manually select the item results_list = [] for item in results: title = item["name"] if "name" in item else item["title"] if item.get("premiered"): year = item["premiered"].split("-")[0] else: year = item.get("first_air_date", "").split("-")[0] if item["poster_path"]: thumb = "http://image.tmdb.org/t/p/original%s" % item[ "poster_path"] else: thumb = "" label = "%s (%s) - %s" % (title, year, item["media_type"]) listitem = xbmcgui.ListItem(label=label, iconImage=thumb, label2=item["overview"]) results_list.append(listitem) if manual_select and results_list: dialog = DialogSelect("DialogSelect.xml", "", listing=results_list, window_title="%s - TMDB" % xbmc.getLocalizedString(283)) dialog.doModal() selected_item = dialog.result del dialog if selected_item != -1: details = results[selected_item] else: results = [] if not details and results: # just grab the first item as best match details = results[0] return details
class USTVnow(object): def __init__(self, sysARG): log('__init__, sysARG = ' + str(sysARG)) self.sysARG = sysARG self.cache = SimpleCache() self.packages = json.loads(USER_PACKAGE) self.header = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36', 'content-type': 'application/json; charset=utf8', 'tenant-code': '%s' % BRAND, 'session-id': LAST_TOKEN, 'box-id': BOX_ID, 'origin': BASEWEB, 'DNT': '1' } def getURL(self, url, params={}, headers={}, life=datetime.timedelta(minutes=15)): log('getURL, url = %s, params = %s, headers = %s' % (url, params, headers)) cacheresponse = self.cache.get(ADDON_NAME + '.getURL, url = %s.%s.%s' % (url, params, headers)) if DEBUG: cacheresponse = None if not cacheresponse: try: r = requests.get(url, params, headers=headers) self.cache.set(ADDON_NAME + '.getURL, url = %s.%s.%s' % (url, params, headers), r.text, expiration=life) return r.json() except Exception as e: log("getURL, Failed! %s" % (e), xbmc.LOGERROR) return {} else: return json.loads(cacheresponse) def postURL(self, url, params={}, headers={}, life=datetime.timedelta(minutes=15)): log('postURL, url = %s, params = %s, headers = %s' % (url, params, headers)) cacheresponse = self.cache.get(ADDON_NAME + '.postURL, url = %s.%s.%s' % (url, params, headers)) if DEBUG: cacheresponse = None if not cacheresponse: try: r = requests.post(url, json=params, headers=headers) self.cache.set(ADDON_NAME + '.postURL, url = %s.%s.%s' % (url, params, headers), r.text, expiration=life) return r.json() except Exception as e: log("postURL, Failed! %s" % (e), xbmc.LOGERROR) return {} else: return json.loads(cacheresponse) def loadReminders(self): try: return json.loads(REAL_SETTINGS.getSetting('User_Reminders')) except: return {} def login(self, user, password): if len(user) > 0: log('login, user = %s' % user) info = (self.getURL(BASEAPI + '/service/api/auth/user/info', headers=self.header) or { 'message': LANGUAGE(30007), 'response': '' }) self.packages = info.get('response', {}).get('packages', []) REAL_SETTINGS.setSetting('User_Package', json.dumps(self.packages)) if not self.packages: log('login, FREE ACCOUNT') if info.get('status', False): return True session = self.getURL( BASEAPI + '/service/api/v1/get/token', { 'tenant_code': '%s' % BRAND, 'product': '%s' % BRAND, 'box_id': BOX_ID, 'device_id': DEV_ID, 'device_sub_type': DEV_TYPE }) if not session.get('status', False): notificationDialog( info.get('error', {}).get('message', '') or info.get('message', '') or LANGUAGE(30007)) return False token = session['response']['sessionId'] self.header['session-id'] = token REAL_SETTINGS.setSetting('User_Token', token) signin = self.postURL(BASEAPI + '/service/api/auth/signin', params={ 'login_id': user, 'login_key': password, 'login_mode': 1, 'manufacturer': 'Kodi' }, headers=self.header) if not signin.get('status', False): notificationDialog( signin.get('error', {}).get('message', '') or signin.get('message', '') or LANGUAGE(30007)) return False notificationDialog( '%s, %s' % (LANGUAGE(30006), signin['response']['firstName'])) return True else: #firstrun wizard if yesnoDialog(LANGUAGE(30008), no=LANGUAGE(30009), yes=LANGUAGE(30010)): user = inputDialog(LANGUAGE(30001)) password = inputDialog(LANGUAGE(30002), opt=xbmcgui.ALPHANUM_HIDE_INPUT) REAL_SETTINGS.setSetting('User_Email', user) REAL_SETTINGS.setSetting('User_Password', password) return self.login(user, password) else: okDialog(LANGUAGE(30003)) return False def buildMenu(self): log('buildMenu') for item in MAIN_MENU: self.addDir(*item) def getcurrentTime(self): return int( str( self.getURL(BASEAPI + '/service/api/current/epoch', life=datetime.timedelta(seconds=60)))[:-3]) def getChanneldata(self, dtime=1): stime = self.getcurrentTime() etime = calendar.timegm((datetime.datetime.fromtimestamp(stime) + datetime.timedelta(days=dtime)).timetuple()) tabs = self.getURL( BASEAPI + '/service/api/v1/tv/guide', { 'page': 0, 'pagesize': 12 }, headers=self.header, life=datetime.timedelta(hours=1))['response']['tabs'] for page in tabs: try: stime = page['startTime'] etime = page['endTime'] items = self.getURL( BASEAPI + '/service/api/v1/tv/guide', { 'start_time': stime, 'end_time': etime, 'page': 0, 'pagesize': 12 }, headers=self.header, life=datetime.timedelta(hours=1))['response']['data'] for item in items: yield item except: break def getContent(self, path='movie', count=24, life=datetime.timedelta(hours=1)): items = self.getURL(BASEAPI + '/service/api/v1/page/content', { 'path': path, 'count': count }, headers=self.header, life=life)['response']['data'] return (item for item in items if len( item.get(item['paneType'], {}).get('sectionData', {}).get( 'data', {})) > 0) def resolveURL(self, path): stream = self.getURL(path, headers=self.header) if not stream.get('status', False): notificationDialog( stream.get('error', {}).get('message', '') or stream.get('message', '') or LANGUAGE(30005)) return None feeds = stream.get('response', {}).get('streams', {}) return [ feed.get('url', None) for feed in feeds if feed.get('streamType', '') == 'akamai' ][0] def buildMeta(self, chname, chlogo, item, opt='guide'): sview = item['display']['layout'] title = uni(item['display']['title']) now = datetime.datetime.now() try: thumb = '%s/epgs/%s' % (BASEIMG, item['display']['imageUrl'].split(',')[1]) except: thumb = chlogo try: stime = datetime.datetime.fromtimestamp( float(item['display']['markers'][1]['value'][:-3])) etime = datetime.datetime.fromtimestamp( float(item['display']['markers'][2]['value'][:-3])) except: stime = etime = now # if stime < now: return None, None, None label = '%s: %s - %s' % ( stime.strftime('%I:%M %p').lstrip('0'), chname, title) if opt == 'guide' else '%s - %s' % (chname, title) label2 = '' plot = uni(item['display']['subtitle2']) path = BASEAPI + '/service/api/v1/page/stream?path=%s' % ( item['target']['path']) liz = xbmcgui.ListItem(label) liz.setInfo(type="Video", infoLabels={ "mediatype": 'episode', "label": label, "label2": label2, "title": label, "plot": plot, "aired": stime.strftime('%Y-%m-%d'), "dateadded": stime.strftime('%Y-%m-%d') }) liz.setArt({"thumb": thumb, "poster": thumb, "fanart": thumb}) liz.setProperty("IsPlayable", "true") liz.setProperty("IsInternetStream", "true") return label, path, liz def buildLive(self): log('buildLive') filter = [] services = self.getContent('live') for service in services: section = service[service['paneType']] info = section.get('sectionInfo', {}) data = section.get('sectionData', {}).get('data', {}) provider = info['name'] for item in data: try: chname = item['display']['parentName'] if not self.packages and chname not in FREE_CHANNEL: continue if chname in filter: continue filter.append(chname) chlogo = BASEIMG + (item.get('display', {}).get( 'imageUrl', '') or item.get('display', {}).get( 'parentIcon', ',')).split(',')[1] chpath = BASEAPI + '/service/api/v1/page/content?path=%s' % ( item['display']['subtitle5']) label, path, liz = self.buildMeta(chname, chlogo, item, 'live') self.addLink(label, path, '9', liz, len(data)) except Exception as e: log( "buildLive, Failed! %s, Item = %s" % (e, json.dumps(item)), xbmc.LOGERROR) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_LABEL) def buildGuide(self, name=None): log('buildGuide, name = %s' % (name)) channels = self.getChanneldata() for channel in channels: chname = channel['channel']['display']['title'] chlogo = BASEIMG + (channel.get('channel', {}).get( 'display', {}).get('imageUrl', '') or channel.get( 'channel', {}).get('display', {}).get('parentIcon', ',')).split(',')[1] chpath = BASEAPI + '/service/api/v1/page/content?path=%s' % ( channel['channel']['target']['path']) if not self.packages and chname not in FREE_CHANNEL: continue if name is not None: if name.lower() == chname.lower(): lineup = channel['programs'] for item in lineup: label, path, liz = self.buildMeta(chname, chlogo, item) if label is None: continue self.addLink(label, path, '9', liz, len(lineup)) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_DATEADDED) else: self.addDir(chname, chname, '3', infoArt={ "thumb": chlogo, "poster": chlogo, "fanart": chlogo }) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_LABEL) def buildContent(self, path): log('buildContent, path = %s' % (path)) contents = self.getContent(path) for content in contents: genre = content['section']['sectionInfo']['name'] items = content['section']['sectionData']['data'] for item in items: try: chname = item['display']['title'] if not self.packages and chname not in FREE_CHANNEL: continue chlogo = BASEIMG + (item.get('display', {}).get( 'imageUrl', '') or item.get('display', {}).get( 'parentIcon', ',')).split(',')[1] chpath = BASEAPI + '/service/api/v1/page/content?path=%s' % ( item['target']['path']) label, path, liz = self.buildMeta(chname, chlogo, item) self.addLink(label, chpath, '9', liz, len(items)) except Exception as e: log( "buildContent, Failed! %s, Item = %s" % (e, json.dumps(item)), xbmc.LOGERROR) def playVideo(self, name, url): log('playVideo') url = self.resolveURL(url) if url is None: return liz = xbmcgui.ListItem(name, path=url) # if 'm3u8' in url.lower(): # if not inputstreamhelper.Helper('hls').check_inputstream(): sys.exit() # liz.setProperty('inputstreamaddon','inputstream.adaptive') # liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(self.sysARG[1]), True, liz) def addLink(self, name, u, mode, liz, total=0): log('addLink, name = ' + name) u = self.sysARG[0] + "?url=" + urllib.quote(u) + "&mode=" + str( mode) + "&name=" + urllib.quote(uni(name)) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, totalItems=total) def addDir(self, name, u, mode, infoList=False, infoArt=False): log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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 = self.sysARG[0] + "?url=" + urllib.quote(u) + "&mode=" + str( mode) + "&name=" + urllib.quote(uni(name)) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, isFolder=True) def getParams(self): return dict(urlparse.parse_qsl(self.sysARG[2][1:])) def run(self): params = self.getParams() try: url = urllib.unquote(params["url"]) except: url = None try: name = urllib.unquote(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: if not self.login(USER_EMAIL, PASSWORD): sys.exit() self.buildMenu() elif mode == 0: self.buildLive() elif mode == 3: self.buildGuide(url) elif mode == 5: self.buildContent(url) elif mode == 9: self.playVideo(name, url) xbmcplugin.setContent(int(self.sysARG[1]), CONTENT_TYPE) xbmcplugin.endOfDirectory(int(self.sysARG[1]), cacheToDisc=False)
class PlayOn: def __init__(self): self.cache = SimpleCache() if URLTYPE == 'upnp': self.chkUPNP() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(minutes=5)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) if url == BASE_URL + PLAYON_DATA: self.getIP() def getIP(self): url = self.openURL(AUTO_URL).replace('|','') match = re.findall(r'[0-9]+(?:\.[0-9]+){3}:[0-9]+', url) log('getIP, match = ' + str(match)) if len(match) == 0: url = getKeyboard(LANGUAGE(30001),LANGUAGE(30002)) if url == False: return if not url.startswith('http'): url = "http://%s"%url BASE_URL = url REAL_SETTINGS.setSetting("playonserver",url) self.chkIP(url) def chkIP(self, url=BASE_URL): log('chkIP, url = ' + str(url)) results = xmltodict.parse(self.openURL(url + PLAYON_DATA)) if results and 'catalog' in results: try: ServerName = results['catalog']['@name'] ServerVer = 'PlayOn v.%s'%results['catalog']['@server'] ServerMSG = "Connected to [B]%s %s[/B]"%(ServerName,ServerVer) log('chkIP, ServerName = ' + ServerName) log('chkIP, ServerVer = ' + ServerVer) REAL_SETTINGS.setSetting("playonServerid",ServerMSG) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30011)%ServerName, ICON, 5000) xbmc.executebuiltin("Container.Refresh") except Exception as e: log("chkIP Failed! " + str(e), xbmc.LOGERROR) else: xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30010), ICON, 5000) def getUPNP(self): log('getUPNP') """ Check Kodi UPNP support. """ json_query = ('{"jsonrpc":"2.0","method":"Settings.GetSettingValue","params":{"setting":"services.upnp"},"id":1}') data = json.loads(xbmc.executeJSONRPC(json_query)) try: if 'result' in data and 'value' in data['result']: return data['result']['value'] except Exception as e: log('getUPNP, Failed! ' + str(e)) return False def setUPNP(self): log('setUPNP') """ Enable Kodi UPNP support. """ json_query = ('{"jsonrpc":"2.0","method":"Settings.SetSettingValue","params":{"setting":"services.upnp","value":true},"id":1}') xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30015), ICON, 5000) return json.loads(xbmc.executeJSONRPC(json_query)) def getUPNP_ID(self): log('getUPNP_ID') """ Check if upnp id is valid. """ json_query = ('{"jsonrpc":"2.0","method":"Files.GetDirectory","params":{"directory":"%s"},"id":1}'%BASE_UPNP) data = json.loads(xbmc.executeJSONRPC(json_query)) try: if not data['result']['files'][0]['file'].endswith('/playonprovider/'): return None except Exception as e: log('getUPNP_ID, Failed! ' + str(e)) return None return BASE_UPNP def chkUPNP(self): log('chkUPNP') """ Query json, locate 'playon server' path, else prompt. """ if self.getUPNP_ID() is not None: return else: if not self.getUPNP(): self.setUPNP() json_query = ('{"jsonrpc":"2.0","method":"Files.GetDirectory","params":{"directory":"upnp://"},"id":1}') data = json.loads(xbmc.executeJSONRPC(json_query)) if data and 'result' in data and 'files' in data['result']: for item in data['result']['files']: if (item['label']).lower().startswith('playon'): REAL_SETTINGS.setSetting("playonUPNPid",item['file'].rstrip('/')) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30013), ICON, 5000) BASE_UPNP = item['file'] REAL_SETTINGS.setSetting("playonUPNPid",BASE_UPNP.rstrip('/')) elif PTVL_RUNNING == False: BASE_UPNP = xbmcgui.Dialog().browse(0, LANGUAGE(30014), 'files', '', False, False, 'upnp://') if BASE_UPNP != -1: REAL_SETTINGS.setSetting("playonUPNPid",BASE_UPNP.rstrip('/')) else: xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30010), ICON, 5000) def buildItemMenu(self, uri=PLAYON_DATA, search=False): log('buildItemMenu, uri = ' + uri) try: genSTRM = False results = [] ranNum = random.randrange(9) response = dict(xmltodict.parse(self.openURL(BASE_URL + uri))) if response and 'catalog' in response and 'group' in response['catalog']: results = response['catalog']['group'] elif response and 'group' in response:# and response['group']['@href'] == uri: results = response['group']['group'] genSTRM = True if isinstance(results,collections.OrderedDict): results = [dict(results)] if not search and uri == PLAYON_DATA: self.addDir('[B][PlayOn][/B] Search','',2,ICON,genSTRM) for item in results: try: if isinstance(item,collections.OrderedDict): item = dict(item) if item['@type'] == 'folder': name = item['@name'].replace('PlayOn','[B][PlayOn][/B]').replace('PlayMark','[B][Playon][/B] PlayMark') if search and name.startswith('[B][PlayOn][/B]'): continue thumb = BASE_URL + ((item.get('@art','').replace('&size=tiny','&size=large')) or folderIcon(ranNum)) if search and item.get('@searchable','false') == 'true': myURL = json.dumps({'id':item.get('@id',''),'uri':item['@href'],'searchable':item.get('@searchable','false')}) self.addDir('Search %s'%name,myURL,3,thumb) elif not search: self.addDir(name,item['@href'],1,thumb, genSTRM) elif item['@type'] == 'video': self.addLink(item['@name'],item['@href'],9,len(results)) except Exception as e: log("buildItemMenu Failed! " + str(e), xbmc.LOGERROR) except Exception as e: log("buildItemMenu Failed! " + str(e), xbmc.LOGERROR) def searchItem(self, uri): log('searchItem, uri = ' + uri) item = json.loads(uri) query = getKeyboard(header=LANGUAGE(30016)) if query == False: self.buildItemMenu(search=True) return else: query = 'dc:description%20contains%20' + urllib.quote(query) self.buildItemMenu(SEARCH_URL%(item['id']) + query) def playLater(self, name, uri): log('playLater, uri = ' + uri) response = dict(xmltodict.parse(self.openURL(BASE_URL + uri))) if response and 'result' in response: result = dict(response['result']) if result['status'] == "true": msg = result['msg'].replace('The media item',name) xbmcgui.Dialog().notification(ADDON_NAME, msg, ICON, 5000) def playVideo(self, name, uri): log('playVideo, uri = ' + uri) liz = self.buildListitem(uri) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def buildListitem(self, uri, contextMenu=[]): result = {} infoList = {} response = dict(xmltodict.parse(self.openURL(BASE_URL + uri))) if response and 'group' in response and response['group']['@href'] == uri: result = dict(response['group']) tvshowtitle = (dict(result.get('series','')).get('@name','') or None) title = (result.get('@name','') or dict(result['media_title'])['@name'] or dict(result['media'])['@name']) label = title mType = 'movie' if tvshowtitle is not None: if tvshowtitle not in title: label = '%s - %s'%(tvshowtitle,title) season, episode = parseSEinfo(title) infoList['season'] = int(season) infoList['episode'] = int(episode) mType = 'episode' plot = (result.get('@description','') or dict(result.get('description','')).get('@name','') or '') thumb = BASE_URL + (result.get('@art','') or dict(result.get('media','')).get('@art','') or ICON).replace('&size=tiny','&size=large') try: aired = (dict(result.get('date','')).get('@name','') or datetime.datetime.now().strftime('%m/%d/%Y')) aired = (datetime.datetime.strptime(aired, '%m/%d/%Y')).strftime('%Y-%m-%d') except: aired = datetime.datetime.now().strftime('%Y-%m-%d') timeData = (dict(result.get('time','')).get('@name','') or '') playLater = dict(result.get('media_playlater','')).get('@src','') contextMenu = contextMenu + [('Add to PlayLater','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(playLater)+"&mode="+str(8)+"&name="+urllib.quote_plus(label.encode("utf-8"))))] if len(timeData) > 0: timeList = timeData.split(':') hours = int(timeList[0]) mins = int(timeList[1]) secs = int(timeList[2]) duration = ((hours * 60 * 60) + (mins * 60) + secs) else: duration = 0 if URLTYPE == 'm3u8' and 'playlaterrecordings' not in result['@href']: url = BASE_VIDEO_URL%(BASE_URL,result['@href'].split('?id=')[1]) elif URLTYPE == 'ext' or 'playlaterrecordings' in result['@href']: url = BASE_URL + '/' + dict(result['media'])['@src'] else: url = BASE_UPNP + '/' + dict(result['media'])['@src'].split('.')[0].split('/')[0] + '/' log('playVideo, url = ' + url) liz=xbmcgui.ListItem(name, path=url) liz.addContextMenuItems(contextMenu) CONTENT_TYPE = mType infoList = {"mediatype":mType,"label":label,"title":label,"tvshowtitle":tvshowtitle,"plot":plot,"duration":duration,"aired":aired} liz.setInfo(type="Video", infoLabels=infoList) liz.setArt({"thumb":thumb,"poster":thumb,"icon":ICON,"fanart":FANART}) liz.setProperty("IsPlayable","true") liz.setProperty("IsInternetStream","true") return liz def addLink(self, name, u, mode, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) contextMenu = [('Add to Library','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(7)+"&name="+urllib.quote_plus(name)))] liz = self.buildListitem(u) 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, thumb=ICON, strm=False): name = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) # if strm: # contextMenu = [('Add to Library','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(7)+"&name="+urllib.quote_plus(name)))] # liz.addContextMenuItems(contextMenu) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name}) liz.setArt({'thumb':thumb,'fanart':FANART}) 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) def genSTRMS(self, name, uri): log('genSTRMS, name = ' + name)
class MultiChannel(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') cacheresponse = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(hours=1)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self): self.addDir(LANGUAGE(30003), BASE_VID, 1) self.addYoutube(LANGUAGE(30004), 'plugin://plugin.video.youtube/channel/UC0VOh1nD6Tlq_5gjkpSecWA/') def browse(self, url): log('browse') soup = BeautifulSoup(self.openURL(url), "html.parser") videos = soup('div', {'class': 'l-grid--item'}) for video in videos: link = video('div', {'class': 'm-card--media'})[0].find('a').attrs['href'] if not link.startswith('/video/'): continue link = BASE_URL+link label = video('div', {'class': 'm-card--media'})[0].find('a').attrs['title'] thumb = video('div', {'class': 'm-card--media'})[0].find('source').attrs['data-srcset'] try: plot = video('div', {'class': 'm-card--content'})[0].find('p').get_text() except: plot = label infoLabels = {"mediatype":"episode","label":label,"title":label,"plot":plot,"genre":'News'} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, link, 9, infoLabels, infoArt, len(videos)) next = soup('li', {'class': 'pager-next'}) if len(next) == 0: return next_url = BASE_URL + next[0].find('a').attrs['href'] next_label = (next[0].find('a').attrs['title'] or next[0].get_text()) self.addDir(next_label, next_url, 1) def playVideo(self, name, url, liz=None): log('playVideo') info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class AnimatedArt(object): '''get animated artwork''' ignore_cache = False def __init__(self, simplecache=None, kodidb=None): '''Initialize - optionaly provide SimpleCache and KodiDb object''' if not kodidb: from kodidb import KodiDb self.kodidb = KodiDb() else: self.kodidb = kodidb if not simplecache: from simplecache import SimpleCache self.cache = SimpleCache() else: self.cache = simplecache @use_cache(14) def get_animated_artwork(self, imdb_id, manual_select=False, ignore_cache=False): '''returns all available animated art for the given imdbid/tmdbid''' # prefer local result kodi_movie = self.kodidb.movie_by_imdbid(imdb_id) if not manual_select and kodi_movie and kodi_movie["art"].get("animatedposter"): result = { "animatedposter": kodi_movie["art"].get("animatedposter"), "animatedfanart": kodi_movie["art"].get("animatedfanart") } else: result = { "animatedposter": self.poster(imdb_id, manual_select), "animatedfanart": self.fanart(imdb_id, manual_select), "imdb_id": imdb_id } self.write_kodidb(result) log_msg("get_animated_artwork for imdbid: %s - result: %s" % (imdb_id, result)) return result def poster(self, imdb_id, manual_select=False): '''return preferred animated poster, optionally show selectdialog for manual selection''' img = self.select_art(self.posters(imdb_id), manual_select, "poster") return self.process_image(img, "poster", imdb_id) def fanart(self, imdb_id, manual_select=False): '''return preferred animated fanart, optionally show selectdialog for manual selection''' img = self.select_art(self.fanarts(imdb_id), manual_select, "fanart") return self.process_image(img, "fanart", imdb_id) def posters(self, imdb_id): '''return all animated posters for the given imdb_id (imdbid can also be tmdbid)''' return self.get_art(imdb_id, "posters") def fanarts(self, imdb_id): '''return animated fanarts for the given imdb_id (imdbid can also be tmdbid)''' return self.get_art(imdb_id, "fanarts") def get_art(self, imdb_id, art_type): '''get the artwork''' art_db = self.get_animatedart_db() if art_db.get(imdb_id): return art_db[imdb_id][art_type] return [] def get_animatedart_db(self): '''get the full animated art database as dict with imdbid and tmdbid as key uses 7 day cache to prevent overloading the server''' # get all animated posters from the online json file cache = self.cache.get("animatedartdb") if cache: return cache art_db = {} data = get_json('http://www.consiliumb.com/animatedgifs/movies.json', None) base_url = data.get("baseURL", "") if data and data.get('movies'): for item in data['movies']: for db_id in ["imdbid", "tmdbid"]: key = item[db_id] art_db[key] = {"posters": [], "fanarts": []} for entry in item['entries']: entry_new = { "contributedby": entry["contributedBy"], "dateadded": entry["dateAdded"], "language": entry["language"], "source": entry["source"], "image": "%s/%s" % (base_url, entry["image"].replace(".gif", "_original.gif")), "thumb": "%s/%s" % (base_url, entry["image"])} if entry['type'] == 'poster': art_db[key]["posters"].append(entry_new) elif entry['type'] == 'background': art_db[key]["fanarts"].append(entry_new) self.cache.set("animatedartdb", art_db, expiration=timedelta(days=7)) return art_db @staticmethod def select_art(items, manual_select=False, art_type=""): '''select the preferred image from the list''' image = None if manual_select: # show selectdialog to manually select the item results_list = [] # add none and browse entries listitem = xbmcgui.ListItem(label=xbmc.getLocalizedString(231), iconImage="DefaultAddonNone.png") results_list.append(listitem) listitem = xbmcgui.ListItem(label=xbmc.getLocalizedString(1030), iconImage="DefaultFolder.png") results_list.append(listitem) for item in items: labels = [item["contributedby"], item["dateadded"], item["language"], item["source"]] label = " / ".join(labels) listitem = xbmcgui.ListItem(label=label, iconImage=item["thumb"]) results_list.append(listitem) if manual_select and results_list: dialog = DialogSelect("DialogSelect.xml", "", listing=results_list, window_title=art_type) dialog.doModal() selected_item = dialog.result del dialog if selected_item == 0: image = "" if selected_item == 1: # browse for image dialog = xbmcgui.Dialog() image = dialog.browse(2, xbmc.getLocalizedString(1030), 'files', mask='.gif').decode("utf-8") del dialog elif selected_item > 1: # user has selected an image from online results image = items[selected_item - 2]["image"] elif items: # just grab the first item as best match image = items[0]["image"] return image @staticmethod def process_image(image_url, art_type, imdb_id): '''animated gifs need to be stored locally, otherwise they won't work''' # make sure that our local path for the gif images exists addon = xbmcaddon.Addon(ADDON_ID) gifs_path = "%sanimatedgifs/" % addon.getAddonInfo('profile') del addon if not xbmcvfs.exists(gifs_path): xbmcvfs.mkdirs(gifs_path) # only process existing images if not image_url or not xbmcvfs.exists(image_url): return None # copy the image to our local path and return the new path as value local_filename = "%s%s_%s.gif" % (gifs_path, imdb_id, art_type) if xbmcvfs.exists(local_filename): xbmcvfs.delete(local_filename) # we don't use xbmcvfs.copy because we want to wait for the action to complete img = xbmcvfs.File(image_url) img_data = img.readBytes() img.close() img = xbmcvfs.File(local_filename, 'w') img.write(img_data) img.close() return local_filename def write_kodidb(self, artwork): '''store the animated artwork in kodi database to access it with ListItem.Art(animatedartX)''' kodi_movie = self.kodidb.movie_by_imdbid(artwork["imdb_id"]) if kodi_movie: params = { "movieid": kodi_movie["movieid"], "art": {"animatedfanart": artwork["animatedfanart"], "animatedposter": artwork["animatedposter"]} } self.kodidb.set_json('VideoLibrary.SetMovieDetails', params)
class StudioLogos(): '''Helper class for studio logo images''' def __init__(self, simplecache=None): '''Initialize - optionaly provide simplecache object''' if not simplecache: from simplecache import SimpleCache self.cache = SimpleCache() else: self.cache = simplecache @use_cache(14) def get_studio_logo(self, studios, lookup_path): '''get the studio logo for the given studio string(s)''' if not studios: return {} result = {} if not isinstance(studios, list): studios = studios.split(" / ") result["Studio"] = studios[0] result['Studios'] = "[CR]".join(studios) result['StudioLogo'] = self.match_studio_logo(studios, self.get_studio_logos(lookup_path)) return result def get_studio_logos(self, lookup_path): '''get all studio logos''' cache_str = u"SkinHelper.StudioLogos" cache = self.cache.get(cache_str, checksum=lookup_path) if cache: return cache # no cache - start lookup all_logos = {} if lookup_path.startswith("resource://"): all_logos = self.get_resource_addon_files(lookup_path) else: if not (lookup_path.endswith("/") or lookup_path.endswith("\\")): lookup_path = lookup_path + os.sep all_logos = self.list_files_in_path(lookup_path) # save in cache and return self.cache.set(cache_str, all_logos, expiration=timedelta(days=14), checksum=lookup_path) return all_logos @staticmethod def match_studio_logo(studios, studiologos): '''try to find a matching studio logo''' studiologo = "" for studio in studios: if studiologo: break studio = studio.lower() # find logo normal if studio in studiologos: studiologo = studiologos[studio] if not studiologo: # find logo by substituting characters if " (" in studio: studio = studio.split(" (")[0] if studio in studiologos: studiologo = studiologos[studio] if not studiologo: # find logo by substituting characters for pvr channels if " HD" in studio: studio = studio.replace(" HD", "") elif " " in studio: studio = studio.replace(" ", "") if studio in studiologos: studiologo = studiologos[studio] return studiologo @use_cache(90) def get_resource_addon_files(self, resourcepath): '''get listing of all files (eg studio logos) inside a resource image addonName read data from our permanent cache file to prevent that we have to query the resource addon''' return self.list_files_in_path(resourcepath) @staticmethod def list_files_in_path(filespath): '''used for easy matching of studio logos''' all_files = {} dirs, files = xbmcvfs.listdir(filespath) if "/" in filespath: sep = "/" else: sep = "\\" for file in files: file = try_decode(file) name = file.split(".png")[0].lower() all_files[name] = filespath + file for directory in dirs: directory = try_decode(directory) files = xbmcvfs.listdir(os.path.join(filespath, directory) + sep)[1] for file in files: file = try_decode(file) name = directory + "/" + file.split(".png")[0].lower() all_files[name] = filespath + directory + sep + file # return the list return all_files
class HSN(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(minutes=5)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def ESTnow(self): is_dst = time.daylight and time.localtime().tm_isdst > 0 utc_offset = - (time.altzone if is_dst else time.timezone) td_local = datetime.timedelta(seconds=utc_offset) return datetime.datetime.utcnow() + td_local def buildHSN(self, name, url): guide = [] isLive = False now = self.ESTnow() soup = BeautifulSoup(self.openURL(LIVE_URL), "html.parser") soup = soup('div' , {'class': 'live-container'})[0] if int(url) is 1: gurl = BASE_URL + soup.find_all('a')[0].attrs['href'] liveurl = soup('div' , {'class': 'watch-nav-container'})[0].attrs['data-wap-hsn-live-video-normal-url'] content = BeautifulSoup(self.openURL(gurl), "html.parser") guide = content('div' , {'class': 'grid-content'})[0]('a', {'class': 'watch-now'}) else: gurl = BASE_URL + soup.find_all('a')[0].attrs['href']+'?network=4' liveurl = soup('div' , {'class': 'watch-nav-container'})[0].attrs['data-wap-hsn2-live-video-normal-url'] content = BeautifulSoup(self.openURL(gurl), "html.parser") guide = content('div' , {'class': 'grid-content'})[0]('button', {'class': 'show-details'}) for idx, item in enumerate(guide): try: isLive = content('div' , {'class': 'grid-content'})[0]('span', {'class': 'live-now'})[idx].get_text() == 'Watch Live' except: isLive = False try: vidurl = YOUTUBE_URL%(item.attrs['data-video-id']) date = item.attrs['data-show-date'] title = item.attrs['data-show-name'] if isLive: label = '[B]Live[/B] - %s'%title vidurl = liveurl elif len(vidurl) == 0: label = 'Coming Up: %s - %s'%(date, title) vidurl = liveurl else: label = '[B]Pre-Recorded: [/B]%s - %s'%(date, title) try: aired = (datetime.datetime.strptime(date, '%m/%d/%Y %I:%M:%S %p')) except: continue except: vidurl = liveurl date = item.attrs['data-startdate'] title = item.get_text() if isLive: label = '[B]Live[/B] - %s'%title else: label = 'Coming Up: %s - %s'%(date, title) try: aired = (datetime.datetime.strptime(date, '%m/%d/%Y %I:%M:%S %p')) except: continue if not isLive and now > aired: continue CONTENT_TYPE = 'episodes' date = aired.strftime('%I:%M:%S %p') infoLabels = {"mediatype":"episode","label":label ,"title":label,"plot":title,"aired":aired.strftime('%Y-%m-%d')} infoArt = {"thumb":ICON,"poster":ICON,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, vidurl, 9, infoLabels, infoArt, len(guide)) def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube("Browse Youtube" , 'plugin://plugin.video.youtube/user/HSN/') def playVideo(self, name, url, liz=None): log('playVideo') liz = xbmcgui.ListItem(name, path=url) if url.startswith('rtmp'): liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class Uncrate(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: cacheresponse = urllib2.urlopen(urllib2.Request(url), timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(minutes=15)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube(LANGUAGE(30006), 'plugin://plugin.video.youtube/channel/UCmqL6p6ZJ9uGvC2N1oZsZvA/') def browse(self, url): log('browse, url = ' + str(url)) soup = BeautifulSoup(self.openURL(url), "html.parser") videos = soup('li', {'class': 'article one-half no-action'}) for video in videos: link = video('div', {'class': 'image-wrapper'})[0].find('a').attrs['href'] thumb = video('div', {'class': 'image-wrapper'})[0].find('img').attrs['src'] genre = [cat.get_text() for cat in video('div', {'class': 'category-group'})[0].find_all('a')] label = video('h1', {'class': 'article-title'})[0].find('a').get_text() plot = video('div', {'class': 'copy-wrapper'})[0].find('p').get_text() infoLabels = {"mediatype":"episode","label":label ,"title":label,"genre":genre,"plot":plot} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":thumb,"logo":thumb} self.addLink(label, link, 9, infoLabels, infoArt, len(videos)) next = soup('div', {'class': 'wrapper pagination-wrapper'}) if len(next) == 0: return next_url = next[0].find('a').attrs['href'] next_label = next[0].find('a').get_text() self.addDir(next_label, next_url, 1) def buildChannels(self): log('buildChannels') soup = BeautifulSoup(self.openURL(CHAN_URL), "html.parser") items = soup('li', {'class': 'article high-impact-banner one-half channel'}) for item in items: thumb = (item('div', {'class': 'image-wrapper'})[0].find('img').attrs['src']).strip() item = item('h1', {'class': 'article-title'})[0] link = item.find('a').attrs['href'] label = item.get_text() infoLabels = {"mediatype":"episode","label":label ,"title":label,"genre":label,"plot":label} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":thumb,"logo":thumb} self.addDir(label, link, 1, infoLabels, infoArt) def playVideo(self, name, url): log('playVideo') info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'm3u8' in url.lower(): liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class PlutoTV(): def __init__(self): self.net = net.Net() self.cache = SimpleCache() self.region = self.getRegion() self.filter = False if self.region == 'US' else True self.categoryMenu = self.getCategories() self.mediaType = self.getMediaTypes() log('__init__, region = ' + self.region) @use_cache(1) def getRegion(self): return (self.openURL(REGION_URL).get('countryCode','') or 'US') def login(self): log('login') #ignore guest login if USER_EMAIL == LANGUAGE(30009): return if len(USER_EMAIL) > 0: header_dict = {} header_dict['Accept'] = 'application/json, text/javascript, */*; q=0.01' header_dict['Host'] = 'api.pluto.tv' header_dict['Connection'] = 'keep-alive' header_dict['Referer'] = 'http://pluto.tv/' header_dict['Origin'] = 'http://pluto.tv' header_dict['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.2; rv:24.0) Gecko/20100101 Firefox/24.0' try: xbmcvfs.rmdir(COOKIE_JAR) except: pass if xbmcvfs.exists(COOKIE_JAR) == False: try: xbmcvfs.mkdirs(SETTINGS_LOC) f = xbmcvfs.File(COOKIE_JAR, 'w') f.close() except: log('login, Unable to create the storage directory', xbmc.LOGERROR) form_data = ({'optIn': 'true', 'password': PASSWORD,'synced': 'false', 'userIdentity': USER_EMAIL}) self.net.set_cookies(COOKIE_JAR) try: loginlink = json.loads(self.net.http_POST(LOGIN_URL, form_data=form_data, headers=header_dict).content.encode("utf-8").rstrip()) if loginlink and loginlink['email'].lower() == USER_EMAIL.lower(): xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30006) + loginlink['displayName'], ICON, 4000) self.net.save_cookies(COOKIE_JAR) else: xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30007), ICON, 4000) except Exception as e: log('login, Unable to create the storage directory ' + str(e), xbmc.LOGERROR) else: #firstrun wizard if yesnoDialog(LANGUAGE(30008),no=LANGUAGE(30009), yes=LANGUAGE(30010)): REAL_SETTINGS.setSetting('User_Email',inputDialog(LANGUAGE(30001))) REAL_SETTINGS.setSetting('User_Password',inputDialog(LANGUAGE(30002))) else: REAL_SETTINGS.setSetting('User_Email',LANGUAGE(30009)) xbmc.executebuiltin('RunScript("' + ADDON_PATH + '/country.py' + '")') def openURL(self, url): log('openURL, url = ' + url) try: header_dict = {} header_dict['Accept'] = 'application/json, text/javascript, */*; q=0.01' header_dict['Host'] = 'api.pluto.tv' header_dict['Connection'] = 'keep-alive' header_dict['Referer'] = 'http://pluto.tv/' header_dict['Origin'] = 'http://pluto.tv' header_dict['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.2; rv:24.0) Gecko/20100101 Firefox/24.0' self.net.set_cookies(COOKIE_JAR) trans_table = ''.join( [chr(i) for i in range(128)] + [' '] * 128 ) cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: try: cacheResponse = self.net.http_GET(url, headers=header_dict).content.encode("utf-8", 'ignore') except: cacheResponse = (self.net.http_GET(url, headers=header_dict).content.translate(trans_table)).encode("utf-8") self.net.save_cookies(COOKIE_JAR) self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheResponse, expiration=datetime.timedelta(hours=1)) if isinstance(cacheResponse, basestring): cacheResponse = json.loads(cacheResponse) return cacheResponse except Exception as e: log('openURL, Unable to open url ' + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, 'Unable to Connect, Check User Credentials', ICON, 4000) return '' def mainMenu(self): log('mainMenu') self.login() for item in PLUTO_MENU: self.addDir(*item) def browseMenu(self): log('browseMenu') for item in self.categoryMenu: self.addDir(*item) def getCategories(self): log('getCategories') collect= [] lineup = [] data = self.openURL(BASE_LINEUP) for channel in data: collect.append(channel['category']) counter = collections.Counter(collect) for key, value in sorted(counter.iteritems()): lineup.append(("%s"%(key) , BASE_LINEUP, 2)) lineup.insert(0,("Featured" , BASE_LINEUP, 2)) lineup.insert(2,("All Channels", BASE_LINEUP, 2)) return lineup def getMediaTypes(self): mediaType = {} for type in self.categoryMenu: type = type[0] if type == 'Movies': mediaType[type] = 'movie' elif type == 'TV': mediaType[type] = 'episodes' elif type == 'Music + Radio': mediaType[type] = 'musicvideo' else: mediaType[type] = 'video' return mediaType def browse(self, chname, url): log('browse, chname = ' + chname) geowarn = False data = (self.openURL(url)) for channel in data: id = channel['_id'] cat = channel['category'] number = channel['number'] region = channel['regionFilter']['include'] exclude = channel['regionFilter']['exclude'] name = channel['name'] plot = channel['description'] feat = (channel.get('featured','') or 0) == -1 if self.filter == True and (self.region in exclude or self.region not in region): if geowarn == False: geowarn = True xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30004), ICON, 4000) continue thumb = ICON if 'thumbnail' in channel: thumb = (channel['thumbnail'].get('path',ICON) or ICON) land = FANART if 'featuredImage' in channel: land = (channel['featuredImage'].get('path',FANART) or FANART) logo = ICON if 'logo' in channel: logo = (channel['logo']['path'] or ICON) if chname == "All Channels": title = "%s - %s: %s" % (cat, number, name) infoLabels ={"mediatype":self.mediaType[cat],"label":title ,"title":title ,"plot":plot, "code":number, "genre":cat, "imdbnumber":id} infoArt ={"thumb":thumb,"poster":thumb,"fanart":land,"icon":logo,"logo":logo} self.addDir(title, id, 8, infoLabels, infoArt) elif chname == "Featured" and feat == True: title = "%s - %s: %s" % (cat, number, name) infoLabels ={"mediatype":self.mediaType[cat],"label":title ,"title":title ,"plot":plot, "code":number, "genre":cat, "imdbnumber":id} infoArt ={"thumb":thumb,"poster":thumb,"fanart":land,"icon":logo,"logo":logo} self.addDir(title, id, 8, infoLabels, infoArt) elif chname.lower() == cat.lower(): title = "%s: %s" % (number, name) infoLabels ={"mediatype":self.mediaType[cat],"label":title ,"title":title ,"plot":plot, "code":number, "genre":cat, "imdbnumber":id} infoArt ={"thumb":thumb,"poster":thumb,"fanart":land,"icon":logo,"logo":logo} self.addDir(title, id, 8, infoLabels, infoArt) def pagination(self, seq, rowlen): for start in xrange(0, len(seq), rowlen): yield seq[start:start+rowlen] def browseGuide(self, start=0, end=14): log('browseGuide') geowarn = False start = 0 if start == BASE_LINEUP else int(start) data = list(self.pagination((self.openURL(BASE_LINEUP)), end)) start = 0 if start >= len(data) else start link = (self.openURL(BASE_GUIDE % (datetime.datetime.now().strftime('%Y-%m-%dT%H:00:00'),(datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%dT%H:00:00')))) for channel in data[start]: chid = channel['_id'] chcat = channel['category'] chnum = channel['number'] region = channel['regionFilter']['include'] exclude = channel['regionFilter']['exclude'] chname = channel['name'] chplot = channel['description'] chthumb = ICON if 'thumbnail' in channel: chthumb = (channel['thumbnail'].get('path',ICON) or ICON) feat = (channel.get('featured','') or 0) == -1 if self.filter == True and (self.region in exclude or self.region not in region): if geowarn == False: geowarn = True xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30004), ICON, 4000) continue item = link[chid] if len(item) == 0: continue item = item[0] epid = (item['episode']['_id']) epname = item['episode']['name'] epplot = (item['episode'].get('description',epname) or epname) epgenre = (item['episode'].get('genre',chcat) or chcat) epdur = int(item['episode'].get('duration','0') or '0') // 1000 live = item['episode']['liveBroadcast'] thumb = chthumb #(item['episode']['thumbnail']['path'] or chthumb) #site doesn't update missing episode thumbs title = "%s: %s - %s" % (chnum, chname, epname) if any(k.lower().startswith(title.lower()) for k in IGNORE_KEYS): continue infoLabels ={"mediatype":self.mediaType[chcat],"label":title ,"title":title ,"plot":epplot, "code":epid, "genre":epgenre, "imdbnumber":chid, "duration":epdur} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(title, chid, 9, infoLabels, infoArt, end) start += 1 self.addDir('>> Next', '%s'%(start), 0) def playChannel(self, name, url): log('playChannel') origurl = url if PTVL_RUN: self.playContent(name, url) link = (self.openURL(BASE_GUIDE % (datetime.datetime.now().strftime('%Y-%m-%dT%H:00:00'),(datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%dT%H:00:00')))) item = link[origurl][0] id = item['episode']['_id'] ch_start = datetime.datetime.fromtimestamp(time.mktime(time.strptime((item["start"].split('.')[0]), "%Y-%m-%dT%H:%M:%S"))) ch_timediff = (datetime.datetime.now() - ch_start).seconds dur_sum = 0 playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist.clear() xbmc.sleep(100) for idx, field in enumerate(self.openURL(BASE_CLIPS %(id))): url = (field['url'] or field['code']) name = field['name'] thumb = (field['thumbnail'] or ICON) provider = field['provider'] url = self.resolveURL(provider, url) dur = int(field['duration'] or '0') // 1000 dur_start = dur_sum dur_sum += dur liz=xbmcgui.ListItem(name, path=url) infoList = {"mediatype":"video","label":name,"title":name,"duration":dur} infoArt = {"thumb":thumb,"poster":thumb,"icon":ICON,"fanart":FANART} liz.setInfo(type="Video", infoLabels=infoList) liz.setArt(infoArt) liz.setProperty("IsPlayable","true") liz.setProperty("IsInternetStream",str(field['liveBroadcast']).lower()) if 'm3u8' in url.lower(): liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') if dur_start < ch_timediff and dur_sum > ch_timediff: vid_offset = ch_timediff - dur_start liz.setProperty('ResumeTime', str(vid_offset)) playlist.add(url, liz, idx) if idx == 0: xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def playContent(self, name, url): log('playContent') origurl = url t1 = datetime.datetime.now().strftime('%Y-%m-%dT%H:00:00') t2 = (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%dT%H:00:00') link = (self.openURL(BASE_GUIDE % (t1,t2))) try: item = link[origurl][0] except Exception as e: return log('playContent, failed! ' + str(e), xbmc.LOGERROR) id = item['episode']['_id'] ch_start = datetime.datetime.fromtimestamp(time.mktime(time.strptime((item["start"].split('.')[0]), "%Y-%m-%dT%H:%M:%S"))) ch_timediff = (datetime.datetime.now() - ch_start).seconds data = (self.openURL(BASE_CLIPS %(id))) dur_sum = 0 for idx, field in enumerate(data): url = (field['url'] or field['code']) name = field['name'] thumb = (field['thumbnail'] or ICON) provider = (field['provider'] or None) url = urllib.quote(json.dumps({"provider":provider,"url":url})) dur = int(field['duration'] or '0') // 1000 dur_start = dur_sum dur_sum += dur if any(k.lower().startswith(name.lower()) for k in IGNORE_KEYS): continue infoList = {"mediatype":"video","label":name,"title":name,"duration":dur} infoArt = {"thumb":thumb,"poster":thumb,"icon":ICON,"fanart":FANART} if PTVL_RUN: self.playVideo(name, url) else: self.addLink(name, url, 7, infoList, infoArt, len(data)) @use_cache(28) def resolveURL(self, provider, url): log('resolveURL, provider = ' + str(provider) + ', url = ' + url) if provider == 'jwplatform' or 'm3u8' in url.lower() or url is None: return url elif provider == 'youtube': url = url.replace('feature=player_embedded&','') if len(re.findall('http[s]?://www.youtube.com/watch', url)) > 0: return YTURL + url.split('/watch?v=')[1] elif len(re.findall('http[s]?://youtu.be/', url)) > 0: return YTURL + url.split('/youtu.be/')[1] elif provider == 'vimeo': if len(re.findall('http[s]?://vimeo.com/', url)) > 0: return VMURL + url.split('/vimeo.com/')[1] else: info = None if isUWP() == False: from YDStreamExtractor import getVideoInfo info = getVideoInfo(url,3,True) if info is None: return YTURL + 'W6FjQgmtt0k' info = info.streams() return info[0]['xbmc_url'] def playVideo(self, name, url, liz=None): log('playVideo') url = json.loads(urllib.unquote(url)) provider = url['provider'] url = url['url'] if liz is None: liz = xbmcgui.ListItem(name, path=self.resolveURL(provider, url)) if 'm3u8' in url.lower(): liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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(u)+"&mode="+str(mode)+"&name="+urllib.quote(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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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(u)+"&mode="+str(mode)+"&name="+urllib.quote(name) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True) def uEPG(self): log('uEPG') #support for uEPG universal epg framework module, available from the Kodi repository. #https://github.com/Lunatixz/KODI_Addons/tree/master/script.module.uepg data = (self.openURL(BASE_LINEUP)) self.link = (self.openURL(BASE_GUIDE % (datetime.datetime.now().strftime('%Y-%m-%dT%H:00:00'),(datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%dT%H:00:00')))) return (self.buildGuide(channel) for channel in data) def buildGuide(self, channel): chthumb = '' chlogo = '' chid = channel['_id'] chcat = channel['category'] chnum = channel['number'] region = channel['regionFilter']['include'] exclude = channel['regionFilter']['exclude'] chname = channel['name'] chplot = channel['description'] isFavorite = False #(channel.get('featured','') or 0) == -1 if self.filter == True and (self.region in exclude or self.region not in region): return if 'thumbnail' in channel: chthumb = (channel['thumbnail'].get('path','') or '') if 'logo' in channel: chlogo = (channel['logo'].get('path','') or '') log('buildGuide, channel = ' + str(chnum)) newChannel = {} guidedata = [] newChannel['channelname'] = chname newChannel['channelnumber'] = chnum newChannel['channellogo'] = chlogo newChannel['isfavorite'] = isFavorite for i in range(len(self.link.get(chid,[]))): item = self.link[chid][i] epname = item['episode']['name'] epid = (item['episode']['_id']) epplot = (item['episode'].get('description',epname) or epname) epgenre = (item['episode'].get('genre',chcat) or chcat) epsubgenre= (item['episode'].get('subGenre','') or '') genre = '%s + %s'%(epgenre, epsubgenre) if len(epsubgenre) > 0 else epgenre epdur = int(item['episode'].get('duration','0') or '0') // 1000 live = item['episode']['liveBroadcast'] == "true" thumb = chthumb poster = (item['episode'].get('thumbnail','').get('path',chthumb) or chthumb) clips = self.link[chid] if len(clips) == 0: return tmpdata = {} clips = clips[0] id = clips['episode']['_id'] data = (self.openURL(BASE_CLIPS %(id))) for field in data: url = (field['url'] or field['code']) name = field['name'] thumb = (field['thumbnail'] or ICON) provider = (field['provider'] or None) url = urllib.quote(json.dumps({"provider":provider,"url":url})) dur = int(field['duration'] or '0') // 1000 title = "%s: %s" %(chname, epname) if any(k.lower().startswith(title.lower()) for k in IGNORE_KEYS): return tmpdata = {"mediatype":self.mediaType[chcat],"label":title,"title":chname,"originaltitle":epname,"plot":epplot, "code":epid, "genre":chcat, "imdbnumber":chid, "duration":dur} tmpdata['starttime'] = int(time.mktime(time.strptime((item["start"].split('.')[0]), "%Y-%m-%dT%H:%M:%S"))) tmpdata['url'] = sys.argv[0]+'?mode=7&name=%s&url=%s'%(title,url) tmpdata['art'] = {"thumb":thumb,"clearart":poster,"fanart":FANART,"icon":chthumb,"clearlogo":chlogo} guidedata.append(tmpdata) newChannel['guidedata'] = guidedata return newChannel
if not tmp_store: initcheck = initCheck(addon_name) api_key, dev_reg_status = initcheck.store_key() addon.setSetting('apikey',api_key) #if aai_username missing open settings, otherwise start device registration if not aai_username and dev_reg_status == 'not_reg': info_dialog_msg = addon.getLocalizedString(30214) dialog.notification('CARNet Meduza', info_dialog_msg, xbmcgui.NOTIFICATION_INFO, 4000) xbmcaddon.Addon().openSettings() elif dev_reg_status == 'not_reg': reg_response = initcheck.dev_reg(api_key) initcheck.check_reg(reg_response, api_key) else: initcheck.pre_run() tmp_store_flag = 1 simplecache.set( addon_name + '.tmp_store', tmp_store_flag, expiration=datetime.timedelta(hours=12)) base_url = sys.argv[0] addon_handle = int(sys.argv[1]) args = urlparse.parse_qs(sys.argv[2][1:]) #set as video addon xbmcplugin.setContent(addon_handle, 'videos') api_base_url = 'https://meduza.carnet.hr/index.php/api/' category_image_base_url = 'https://meduza.carnet.hr/uploads/images/categories/' # api_key == device_id from register (import) device == uid api_key = addon.getSetting('apikey') mode = args.get('mode', None) dir_recommends = addon.getLocalizedString(30201)
class NewsBlender(object): def __init__(self): self.cache = SimpleCache() self.sources = self.openURL(SOURCES_URL).get('sources','') def openURL(self, url): log('openURL, url = ' + url) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') request.add_header('Accept-type', 'application/json') response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=1)) return json.loads(self.cache.get(ADDON_NAME + '.openURL, url = %s'%url)) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self): for idx, item in enumerate(MAIN_MENU): self.addDir(item,'',idx) def buildCategory(self): category = collections.Counter([x['category'] for x in self.sources]) for category, value in sorted(category.iteritems()): self.addDir(category.title(),category,4) def buildCountry(self): countries = collections.Counter([x['country'] for x in self.sources]) for country, value in sorted(countries.iteritems()): self.addDir(getRegionName(country),country,6) def buildLanguage(self): languages = collections.Counter([x['language'] for x in self.sources]) for language, value in sorted(languages.iteritems()): self.addDir(getLanguageName(language),language,7) def buildSource(self, items=None): if items is None: items = self.sources for source in items: label = source['name'] thumb = (LOGO_URL%source['url'] or ICON) infoLabels = {"mediatype":"files","label":label,"title":label,"genre":source.get('category','news'),"plot":source.get('description','news')} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(label, source['id'], 5, infoLabels, infoArt) def browseCategory(self, url): self.buildSource(self.openURL(SOURCES_URL + '&category=%s'%url).get('sources','')) def browseCountry(self, url): self.buildSource(self.openURL(SOURCES_URL + '&country=%s'%url).get('sources','')) def browseLanguage(self, url): self.buildSource(self.openURL(SOURCES_URL + '&language=%s'%url).get('sources','')) def browseTop(self, url): self.browse(self.newsArticles.get_by_top(url).get('sources','')) def browseLatest(self, url): self.browse(self.newsArticles.get_by_latest(url).get('sources','')) def browsePopular(self, url): self.browse(self.newsArticles.get_by_popular(url).get('sources','')) def search(self, name, url): kb = xbmc.Keyboard('', LANGUAGE(30005)%name) xbmc.sleep(1000) kb.doModal() if kb.isConfirmed(): url = (EVRYTHING_URL + '&q=%s&sources=%s'%(urllib.quote_plus(kb.getText()),url)).split('|')[0] try: self.browseArticles(name, url, self.openURL(url).get('articles',''), False) except Exception as e: log('search, failed ' + str(e), xbmc.LOGERROR) def buildArticles(self, name, url): self.browseArticles(name, url, self.openURL(HEADLINE_URL + '&sources=%s'%url).get('articles','')) def browseArticles(self, name, url, items, search=True): tmpList = [] for idx, item in enumerate(items): info = self.getVideo(item['url']) if info is None or len(info) == 0: continue source = item['source']['name'] label = item['title'] thumb = item['urlToImage'] plot = item['description'] try: aired = item['publishedAt'].split('T')[0] except: aired = (datetime.datetime.now()).strftime('%Y-%m-%d') tmpList.append((source, label, thumb, plot, aired, info)) dlg = busyDialog(0) for idx, data in enumerate(tmpList): busyDialog(idx * 100 // len(tmpList),dlg) try: source, label, thumb, plot, aired, info = data url = info[0]['xbmc_url'] try: if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) except: pass infoLabels = {"mediatype":"episode","label":label ,"title":label,"duration":info[0]['ytdl_format'].get('duration',0),"aired":aired,"plot":plot,"genre":"News"} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, url, 9, infoLabels, infoArt) except: pass busyDialog(100,dlg) if len(tmpList) == 0: self.addLink((LANGUAGE(30003)%name), "", "") elif search: self.addSearch(name, url) def getVideo(self, url): cacheresponse = self.cache.get(ADDON_NAME + '.getVideo, url = %s'%url) if not cacheresponse: info = getVideoInfo(url,QUALITY,True) if info is not None: info = info.streams() self.cache.set(ADDON_NAME + '.getVideo, url = %s'%url, json.dumps(info), expiration=datetime.timedelta(days=14)) return json.loads(self.cache.get(ADDON_NAME + '.getVideo, url = %s'%url)) def playVideo(self, name, url, liz=None): log('playVideo') if liz is None: liz = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addSearch(self, name, url): self.addDir((LANGUAGE(30004)%name), url, 8) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name}) else: liz.setInfo(type="Video", infoLabels=infoList) if infoArt == False: liz.setArt({'thumb':LOGO_URL%urllib.quote_plus(name),'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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name}) else: liz.setInfo(type="Video", infoLabels=infoList) if infoArt == False: liz.setArt({'thumb':ICON,'fanart':FANART}) #LOGO_URL%urllib.quote_plus(name) 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)
api_key, dev_reg_status = initcheck.store_key() addon.setSetting('apikey', api_key) #if aai_username missing open settings, otherwise start device registration if not aai_username and dev_reg_status == 'not_reg': info_dialog_msg = addon.getLocalizedString(30214) dialog.notification('CARNet Meduza', info_dialog_msg, xbmcgui.NOTIFICATION_INFO, 4000) xbmcaddon.Addon().openSettings() elif dev_reg_status == 'not_reg': reg_response = initcheck.dev_reg(api_key) initcheck.check_reg(reg_response, api_key) else: initcheck.pre_run() tmp_store_flag = 1 simplecache.set(addon_name + '.tmp_store', tmp_store_flag, expiration=datetime.timedelta(hours=12)) base_url = sys.argv[0] addon_handle = int(sys.argv[1]) args = urlparse.parse_qs(sys.argv[2][1:]) #set as video addon xbmcplugin.setContent(addon_handle, 'videos') api_base_url = 'https://meduza.carnet.hr/index.php/api/' category_image_base_url = 'https://meduza.carnet.hr/uploads/images/categories/' # api_key == device_id from register (import) device == uid api_key = addon.getSetting('apikey') mode = args.get('mode', None)
class FunnyOrDie(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(days=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' @use_cache(1) def getData(self): try: soup = BeautifulSoup(self.openURL(CAT_URL), "html.parser") return json.loads(re.compile("data-react-props='(.*?)'>", re.DOTALL ).findall(str(soup('div' , {'class': 'action-bar'})))[0]) except Exception as e: log("getData Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return None def getSorts(self): log('getSorts') getData = self.getData() if getData is None: return for sort in getData['sortOptions']: yield (sort['title'], '%s/%s'%(sort['key'], sort['date_filter']), 0) def getCategories(self, url): log('getCategories') getData = self.getData() if getData is None: return for cat in getData['categoryOptions']: yield (cat['title'], json.dumps({"url":'%s/%s/%s'%(cat['category'], cat['grade'], url), "page":1}), 1) def buildMenu(self, items, yt=False): for item in items: self.addDir(*item) if yt: self.addYoutube("Browse Youtube" , 'plugin://plugin.video.youtube/user/funnyordie/') def browseVideos(self, name, myurl): log('browse, ' + name) myurl = json.loads(myurl) url = myurl['url'] page = myurl['page'] soup = BeautifulSoup(self.openURL(VID_URL%(url,page)), "html.parser") videos = soup('div' , {'class': 'media-preview-crop'}) if len(videos) == 0: return for video in videos: vidurl= BASE_URL + video.find_all('a')[0].attrs['href'] title = video.find_all('a')[0].attrs['title'] thumb = (video.find_all('a')[0]('img' , {'class': 'media-preview-thumbnail'})[0].attrs['data-src'] or ICON) duration = video.find_all('a')[0]('span' , {'class': 'media-video-duration'})[0].get_text() try: runtime = duration.split(':') if len(runtime) == 3: h, m, s = runtime duration = int(h) * 3600 + int(m) * 60 + int(s) else: m, s = runtime duration = int(m) * 60 + int(s) except: duration = duration infoLabels = {"mediatype":"episode","label":title ,"title":title,"duration":duration,"plot":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} CONTENT_TYPE = 'episodes' self.addLink(title, vidurl, 9, infoLabels, infoArt, len(videos)) myurl = json.dumps({"url":url,"page":page + 1}) self.addDir('>> Next', myurl, 1) def playVideo(self, name, url, liz=None): log('playVideo') info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class TVCatchup(object): def __init__(self): log('__init__') self.cache = SimpleCache() def getTVCtime(self): return datetime.datetime.now(timezone('Europe/London')) def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: cacheresponse = urllib2.urlopen(urllib2.Request(url), timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(minutes=5)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) def buildLive(self): soup = BeautifulSoup(self.openURL(LIVE_URL), "html.parser") results = soup('div' , {'class': 'channelsHolder'}) for channel in results: chname = cleanString(channel.find_all('img')[1].attrs['alt']).replace('Watch ','') label = '%s - %s'%(chname,cleanString(channel.get_text())) link = channel.find_all('a')[0].attrs['href'] thumb = LOGO%chname infoLabels = {"mediatype":"episode","label":label ,"title":label} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":thumb,"logo":thumb} self.addLink(label, link, 9, infoLabels, infoArt, len(results)) def buildLineup(self, name=None): log('buildLineup, name = ' + str(name)) soup = BeautifulSoup(self.openURL(GUIDE_URL), "html.parser") results = soup('div' , {'class': 'row'}) for channel in results: chname = cleanString(channel.find_all('img')[0].attrs['alt']) link = cleanString(channel.find_all('a')[0].attrs['href']) thumb = LOGO%chname if name is None: infoLabels = {"mediatype":"episode","label":chname ,"title":chname} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":thumb,"logo":thumb} self.addDir(chname, chname, 2, infoLabels, infoArt) elif name.lower() == chname.lower(): try: date = soup('a' , {'class': 'last'})[0].attrs['href'] aired = re.findall('/tv-guide/(.+?)/00',date, flags=re.DOTALL)[0] except: aired = self.getTVCtime().strftime('%Y-%m-%d') items = channel('div' , {'class': 'hide'}) for item in items: try: time = trimString(item.find_all('span')[0].get_text()) dur = int((abs(eval(time.replace(':','.'))) * 60) * 60) start = datetime.datetime.strptime(time.split('-')[0], '%H:%M').strftime('%I:%M %p') except: continue label = '%s: %s - %s'%(start,chname,cleanString(item.get_text()).split('\n')[0]) try: desc = trimString(item.find_all('br')[0].get_text()) except: desc = '' infoLabels = {"mediatype":"episode","label":label ,"title":label,"plot":desc,"duration":dur,"aired":aired} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":thumb,"logo":thumb} self.addLink(label, link, 9, infoLabels, infoArt, len(items)) break def uEPG(self): log('uEPG') #support for upcoming uEPG universal epg framework module, module will be available from the Kodi repository. #https://github.com/Lunatixz/KODI_Addons/tree/master/script.module.uepg soup = BeautifulSoup(self.openURL(GUIDE_URL), "html.parser") results = soup('div' , {'class': 'row'}) return (self.buildGuide(idx, channel) for idx, channel in enumerate(results)) def buildGuide(self, idx, channel): log('buildGuide') chname = cleanString(channel.find_all('img')[0].attrs['alt']) link = cleanString(channel.find_all('a')[0].attrs['href']) chlogo = LOGO%chname chnum = idx + 1 isFavorite = False newChannel = {} guidedata = [] newChannel['channelname'] = chname newChannel['channelnumber'] = chnum newChannel['channellogo'] = chlogo newChannel['isfavorite'] = isFavorite try: date = soup('a' , {'class': 'last'})[0].attrs['href'] aired = re.findall('/tv-guide/(.+?)/00',date, flags=re.DOTALL)[0] except: aired = self.getTVCtime().strftime('%Y-%m-%d') items = channel('div' , {'class': 'hide'}) for item in items: try: ttime = trimString(item.find_all('span')[0].get_text()) dur = int((abs(eval(ttime.replace(':','.'))) * 60) * 60) start = datetime.datetime.strptime(ttime.split('-')[0], '%H:%M').strftime('%I:%M %p') title = cleanString(item.get_text()).split('\n')[0] label = '%s - %s'%(chname,title) starttime = (datetime.datetime.strptime('%s - %s'%(aired,start), '%Y-%m-%d - %I:%M %p')) starttime = time.mktime(starttime.timetuple()) except: continue try: desc = trimString(item.find_all('br')[0].get_text()) except: desc = '' tmpdata = {"mediatype":"episode","label":title,"title":label,"originaltitle":label,"plot":desc,"duration":dur} tmpdata['starttime'] = starttime tmpdata['url'] = sys.argv[0]+'?mode=9&name=%s&url=%s'%(label,link) tmpdata['art'] ={"thumb":chlogo,"clearart":chlogo,"fanart":FANART,"icon":chlogo,"clearlogo":chlogo} guidedata.append(tmpdata) newChannel['guidedata'] = guidedata return newChannel def resolverURL(self, url): return re.compile('<source src="(.+?)" type="application/x-mpegURL">').findall(self.openURL(BASE_URL + url))[0] def playVideo(self, name, url, liz=None): log('playVideo') liz = xbmcgui.ListItem(name, path=self.resolverURL(url)) liz.setMimeType('application/x-mpegURL') liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class Cheddar(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): try: cacheResponce = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponce: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout=TIMEOUT) cacheResponce = response.read() response.close() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheResponce, expiration=datetime.timedelta(hours=1)) return cacheResponce except urllib2.URLError as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): log('buildMenu') if items == Cheddar_LIVE: for item in items: self.addLink(*item) else: for item in items: self.addDir(*item) if items == Cheddar_MENU: self.addYoutube(LANGUAGE(30033), 'plugin://plugin.video.youtube/channel/UC04KsGq3npibMCE9Td3mVDg/') def browse(self, link): log('browse') soup = BeautifulSoup(self.openURL(BASEURL + link), "html.parser") latestLink = (soup('div', {'class': 'video_thumb'})) for item in latestLink: uriLink = item('a', {'class': 'cf'})[0] uri = BASEURL + uriLink['href'] thumb = uriLink('div', {'class': 'vid_img'})[0].find_all('img')[0].get('src') airdate, title = uriLink.text.strip().replace('\r','').replace('\t','').split('\n') label = title.strip() plot = '%s [CR]Aired: %s'%(label, airdate) try: airdate = datetime.datetime.strptime(airdate, "%B %d, %Y") except: airdate = datetime.datetime.now() airdate = airdate.strftime('%Y-%m-%d') infoList = {"mediatype":"episode","label":label,"title":label,"plot":plot,'genre':'News',"studio":"cheddar","aired":airdate} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART} self.addLink(label, uri, 9, infoList, infoArt) def playVideo(self, name, url): log('playVideo, name = ' + name) if url.endswith('m3u8'): liz = xbmcgui.ListItem(name, path=url) liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') else: info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class NewsOn(object): def __init__(self): log('__init__') self.cache = SimpleCache() self.stateMenu = self.getStates() def openURL(self, url): try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) request.add_header('Accept-encoding', 'gzip') request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout = TIMEOUT) log(response.headers['content-type']) log(response.headers['content-encoding']) if response.info().get('content-encoding') == 'gzip': buf = StringIO(response.read()) f = gzip.GzipFile(fileobj=buf) cacheResponse = f.read() else: cacheResponse = response response.close() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheResponse, expiration=datetime.timedelta(hours=1)) if isinstance(cacheResponse, basestring): cacheResponse = json.loads(cacheResponse) return cacheResponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def mainMenu(self): log('mainMenu') for item in MENU: self.addDir(*item) def browseMenu(self, id=1): log('browseMenu, id = ' + str(id)) self.stateMenu = [tuple(s.format(id) for s in tup) for tup in self.stateMenu] for item in self.stateMenu: self.addDir(*item) def getStates(self): log('getStates') state = [] stateLST = [] data = self.openURL(BASE_API) if len(data) == 0: return [] for channel in data: try: state.append(channel['config']['state']) except: pass states = collections.Counter(state) for key, value in sorted(states.iteritems()): stateLST.append(("%s"%(key), key , '{}')) return stateLST def newsCasts(self, state): log('newsCasts, state = ' + state) urls = [] data = self.openURL(BASE_API) if len(data) == 0: return for channel in data: try: states = channel['config']['state'] except: continue if state in states: chid = channel['identifier'] title = channel['title'] icon = (channel['icon'] or ICON) for idx, stream in enumerate(channel['streams']): streamType = stream['StreamType'] if streamType == 'website': continue#random.choice(['website','roku']): #multiple urls, only add unique. url = stream['Url'] offset = stream['OffsetFromNow'] delay = url+'&delay=%d' #todo do something with delay option? if url not in urls: urls.append(url) chid = chid+'.%d'%idx if idx > 0 else chid label = "%s - %s" % (chid, title) infoLabels ={"mediatype":"episodes","label":label ,"title":label} infoArt ={"thumb":icon,"poster":icon,"fanart":FANART,"icon":icon,"logo":icon} self.addLink(title, url, 9, infoLabels, infoArt) def videoclips(self, state): log('videoclips, state = ' + state) data = self.openURL(BASE_API) if len(data) == 0: return for channel in data: try: states = channel['config']['state'] except: continue if state in states: chid = channel['identifier'] title = channel['title'] icon = (channel['icon'] or ICON) vidURL = channel['config']['localvodfeed'] if vidURL: label = "%s - %s" % (chid, title) infoLabels ={"mediatype":"video","label":label,"title":label} infoArt ={"thumb":icon,"poster":icon,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(label, vidURL, 4, infoLabels, infoArt) def parseclips(self, url): log('parseclips, url = ' + url) feed = feedparser.parse(url) for item in feed['entries']: if item and 'summary_detail' in item: for vids in item['media_content']: title = item['title'] url = vids['url'] plot = item['summary'] aired = item.get('published','').replace(' EST','').replace(' UTC','').replace(' GMT','') try: aired = (datetime.datetime.strptime(aired, '%a, %d %b %Y %H:%M:%S')) except: aired = datetime.datetime.now() aired = aired.strftime("%Y-%m-%d") thumb = item['media_thumbnail'][0]['url'] tagLST = [] if 'tags' in item: for tag in item['tags']: tagLST.append(((tag['term']).split('/')[0]).title()) if len(tagLST) > 0: genre = (tagLST[0] or '') infoLabels ={"mediatype":"episode","label":title,"title":title,"plot":plot,"aired":aired,'genre':genre,'tags':tagLST} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(title, url, 8, infoLabels, infoArt) def playVideo(self, name, url, live=False): log('playVideo') liz = xbmcgui.ListItem(name, path=url) if live: liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo( type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class EarthCam(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url, force=False): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse or force: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(days=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube(LANGUAGE(30005), 'plugin://plugin.video.youtube/user/earthcam/') def browse(self, name, url): log('browse, ' + name) soup = BeautifulSoup(self.openURL(url), "html.parser") if len(soup) == 0: return networks = soup('a', {'class': 'locationLink'}) for region in networks: title = region.get_text() url = NET_URL + region.attrs['href'] thumb = LOGO_URL%(urllib.quote(title)) infoLabels = {"mediatype":"files","label":title ,"title":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,2,infoLabels,infoArt) def browseVideos(self, name, url): log('browseVideos, ' + name) soup = BeautifulSoup(self.openURL(url), "html.parser") if len(soup) == 0: return featured = soup('div', {'class': 'col-lg-3 col-md-4 col-sm-5 col-xs-12'}) for cam in featured: feat = cam('a', {'class': 'listImg'}) url = cam('a', {'class': 'featuredTitleLink'})[0].attrs['href'] if url.endswith('php'): continue thumb = feat[0].find('img').attrs['src'] title = feat[0].find('img').attrs['title'] infoLabels = {"mediatype":"files","label":title ,"title":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,8,infoLabels,infoArt) def resolveURL(self, name, url): log('resolveURL, url = ' + str(url)) try: response = self.openURL(url) results = json.loads(re.compile("var json_base = (.*?);").findall(response)[0], strict=False) pageids = (json.loads(re.compile("js_cam_list = (.*?);").findall(response)[0], strict=False) or [url.split('?cam=')[1]]) except: return for id in pageids: try: results = results["cam"][id] except: return thumb = results["thumbnail_512"] ofset = results.get("timezone_offset","0") plot = (results["description"] or results["title"]) infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} if results["liveon"] == "true": label = '%s - %s, %s Live (HLS)'%(results["camtext"], name,results["country"]) infoLabels = {"mediatype":"episode","label":label,"title":label,"plot":plot} liveurl = ('http:%s%s'%(results["html5_streamingdomain"],results["html5_streampath"])) self.addLink(label, liveurl, 9, infoLabels, infoArt, len(pageids)) # label = '%s,%s - Live (FLV)'%(results["long_title"],results["country"]) # infoLabels = {"mediatype":"episode","label":label ,"title":label,"plot":plot} # liveurl = ('%s%s'%(results["streamingdomain"],results["livestreamingpath"])) # self.addLink(label, liveurl, 9, infoLabels, infoArt, len(pageids)) elif results["timelapseon"] == "true": label = '%s - %s, %s Timelapse'%(results["camtext"], name,results["country"]) infoLabels = {"mediatype":"episode","label":label,"title":label,"plot":plot} liveurl = ('http:%s%s'%(results["timelapsedomain"],results["timelapsepath"])) self.addLink(label, liveurl, 9, infoLabels, infoArt, len(pageids)) elif results["archiveon"] == "true": label = '%s - %s, %s Archive'%(results["camtext"], name,results["country"]) infoLabels = {"mediatype":"episode","label":label,"title":label,"plot":plot} liveurl = ('http:%s%s'%(results["archivedomain"],results["archivepath"])) self.addLink(label, liveurl, 9, infoLabels, infoArt, len(pageids)) def prepareLink(self, url): log('prepareLink, url = ' + str(url)) try: if len(re.findall('http[s]?://www.youtube.com/watch', url)) > 0: return 'plugin://plugin.video.youtube/play/?video_id=%s'%(url.split('/watch?v=')[1]) elif url.lower().endswith(".m3u8"): return url.replace('playlist', re.search(r'^([^#].+)\.m3u8$', self.openURL(url, True), re.MULTILINE).group(1)) except: return None return url def playVideo(self, name, url): log('playVideo') url = self.prepareLink(url) if url is None: return liz = xbmcgui.ListItem(name, path=url) # if url.endswith(".m3u8"): # liz.setProperty('inputstreamaddon','inputstream.adaptive') # liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class Mubi(object): _URL_MUBI = "https://mubi.com" _mubi_urls = { "login": urljoin(_URL_MUBI, "api/v1/sessions"), "films": urljoin(_URL_MUBI, "services/android/films"), "film": urljoin(_URL_MUBI, "services/android/films/%s"), "set_watching": urljoin(_URL_MUBI, "api/v1/films/%s/viewing/watching"), "set_reel": urljoin(_URL_MUBI, "api/v1/films/%s/viewing/set_reel"), "get_url": urljoin(_URL_MUBI, "api/v1/films/%s/reels/%s/secure_url"), "startup": urljoin(_URL_MUBI, "services/android/app_startup") } def __init__(self, username, password): self._username = username self._password = password self._cache_id = "plugin.video.mubi.filminfo.%s" self._simplecache = SimpleCache() # Need a 20 digit id, hash username to make it predictable self._udid = int(hashlib.sha1(username).hexdigest(), 32) % (10**20) self._token = None self._userid = None self._country = None # The new mubi API (under the route /api/v1/[...] rather than /services/android) asks for these header fields: self._headers = { 'client': 'android', 'client-app': 'mubi', 'client-version': APP_VERSION_CODE, 'client-device-identifier': str(self._udid) } self.login() def login(self): payload = {'email': self._username, 'password': self._password} xbmc.log( "Logging in with username: %s and udid: %s" % (self._username, self._udid), 2) r = requests.post(self._mubi_urls["login"], headers=self._headers, data=payload) result = (''.join(r.text)).encode('utf-8') if r.status_code == 200: self._token = json.loads(result)['token'] self._userid = json.loads(result)['user']['id'] self._headers['authorization'] = 'Bearer ' + self._token xbmc.log( "Login Successful with token=%s and userid=%s" % (self._token, self._userid), 2) else: xbmc.log("Login Failed with result: %s" % result, 4) self.app_startup() return r.status_code def app_startup(self): payload = { 'udid': self._udid, 'token': self._token, 'client': 'android', 'client_version': APP_VERSION_CODE } r = requests.post(self._mubi_urls['startup'] + "?client=android", data=payload) result = (''.join(r.text)).encode('utf-8') if r.status_code == 200: self._country = json.loads(result)['country'] xbmc.log("Successfully got country as %s" % self._country, 2) else: xbmc.log("Failed to get country: %s" % result, 4) return def get_film_page(self, film_id): cached = self._simplecache.get(self._cache_id % film_id) if cached: return json.loads(cached) args = "?client=android&country=%s&token=%s&udid=%s&client_version=%s" % ( self._country, self._token, self._udid, APP_VERSION_CODE) r = requests.get((self._mubi_urls['film'] % str(film_id)) + args) if r.status_code != 200: xbmc.log( "Invalid status code %s getting film info for %s" % (r.status_code, film_id), 4) self._simplecache.set(self._cache_id % film_id, r.text, expiration=datetime.timedelta(days=32)) return json.loads(r.text) def get_film_metadata(self, film_overview): film_id = film_overview['id'] available_at = dateutil.parser.parse(film_overview['available_at']) expires_at = dateutil.parser.parse(film_overview['expires_at']) # Check film is valid, has not expired and is not preview now = datetime.datetime.now(available_at.tzinfo) if available_at > now: xbmc.log("Film %s is not yet available" % film_id, 2) return None elif expires_at < now: xbmc.log("Film %s has expired" % film_id, 2) return None hd = film_overview['hd'] drm = film_overview['reels'][0]['drm'] audio_lang = film_overview['reels'][0]['audio_language'] subtitle_lang = film_overview['reels'][0]['subtitle_language'] # Build plot field. Place lang info in here since there is nowhere else for it to go drm_string = "" #"Protected by DRM\n" if drm else "" lang_string = ("Language: %s" % audio_lang) + ( (", Subtitles: %s\n" % subtitle_lang) if subtitle_lang else "\n") plot_string = "Synopsis: %s\n\nOur take: %s" % ( film_overview['excerpt'], film_overview['editorial']) # Get detailed look at film to get cast info film_page = self.get_film_page(film_id) cast = [(m['name'], m['credits']) for m in film_page['cast']] # Build film metadata object metadata = Metadata( title=film_overview['title'], director=film_overview['directors'], year=film_overview['year'], duration=film_overview['duration'] * 60, # This is in seconds country=film_overview['country'], plot=drm_string + lang_string + plot_string, overlay=6 if hd else 0, genre=', '.join(film_overview['genres']), originaltitle=film_overview['original_title'], # Out of 5, kodi uses 10 rating=film_overview['average_rating'] * 2 if film_overview['average_rating'] is not None else None, votes=film_overview['number_of_ratings'], castandrole=cast, trailer=film_overview['trailer_url']) listview_title = film_overview['title'] + (" [HD]" if hd else "") return Film(listview_title, film_id, film_overview['stills']['standard'], metadata) def get_now_showing_json(self): # Get list of available films args = "?client=android&country=%s&token=%s&udid=%s&client_version=%s" % ( self._country, self._token, self._udid, APP_VERSION_CODE) r = requests.get(self._mubi_urls['films'] + args) if r.status_code != 200: xbmc.log("Invalid status code %s getting list of films", 4) return r.text def now_showing(self): films = [ self.get_film_metadata(film) for film in json.loads(self.get_now_showing_json()) ] return [f for f in films if f] def set_watching(self, film_id): # this call tells the api that the user wants to watch a certain movie and returns the default reel id payload = {'last_time_code': 0} r = requests.put((self._mubi_urls['set_watching'] % str(film_id)), data=payload, headers=self._headers) result = (''.join(r.text)).encode('utf-8') if r.status_code == 200: return json.loads(result)['reel_id'] else: xbmc.log("Failed to obtain the reel id with result: %s" % result, 4) return -1 def get_default_reel_id_is_drm(self, film_id): reel_id = [(f['reels'][0]['id'], f['reels'][0]['drm']) for f in json.loads(self.get_now_showing_json()) if str(f['id']) == str(film_id)] if len(reel_id) == 1: return reel_id[0] elif reel_id: xbmc.log( "Multiple default_reel's returned for film %s: %s" % (film_id, ', '.join(reel_id)), 3) return reel_id[0] else: xbmc.log("Could not find default reel id for film %s" % film_id, 4) return None # function to obtain the film id from the web version of MUBI (not the API) def get_film_id_by_web_url(self, mubi_url): r = requests.get(mubi_url) result = (''.join(r.text)).encode('utf-8') import re m = re.search('"film_id":([0-9]+)', result) film_id = m.group(1) xbmc.log("Got film id: %s" % film_id, 3) return film_id def get_play_url(self, film_id): # reels probably refer to different streams of the same movie (usually when the movie is available in two dub versions) # it is necessary to tell the API that one wants to watch a film before requesting the movie URL from the API, otherwise # the URL will not be delivered. # this can be done by either calling # [1] api/v1/{film_id}/viewing/set_reel, or # [2] api/v1/{film_id}/viewing/watching # the old behavior of the addon was calling [1], as the reel id could be known from loading the film list. # however, with the new feature of playing a movie by entering the MUBI web url, the reel id is not always known (i.e. # if the film is taken from the library, rather than from the "now showing" section). # by calling [2], the default reel id for a film (which is usually the original dub version of the movie) is returned. # <old> # (reel_id, is_drm) = self.get_default_reel_id_is_drm(film_id) # set the current reel before playing the movie (if the reel was never set, the movie URL will not be delivered) # payload = {'reel_id': reel_id, 'sidecar_subtitle_language_id': 20} # r = requests.put((self._mubi_urls['set_reel'] % str(film_id)), data=payload, headers=self._headers) # result = (''.join(r.text)).encode('utf-8') # xbmc.log("Set reel response: %s" % result, 2) # </old> # new: get the default reel id by calling api/v1/{film_id}/viewing/watching reel_id = self.set_watching(film_id) is_drm = True # let's just assume, that is_drm is always true # get the movie URL args = "?country=%s&download=false" % (self._country) r = requests.get( (self._mubi_urls['get_url'] % (str(film_id), str(reel_id))) + args, headers=self._headers) result = (''.join(r.text)).encode('utf-8') if r.status_code != 200: xbmc.log( "Could not get secure URL for film %s with reel_id=%s" % (film_id, reel_id), 4) xbmc.log("Response was: %s" % result, 2) url = json.loads(result)["url"] # return the video info item_result = { 'url': url, 'is_mpd': "mpd" in url, 'drm_header': base64.b64encode('{"userId":' + str(self._userid) + ',"sessionId":"' + self._token + '","merchant":"mubi"}') if is_drm else None } xbmc.log("Got video info as: '%s'" % json.dumps(item_result), 2) return item_result
class Tmdb(object): '''get metadata from tmdb''' api_key = None def __init__(self, simplecache=None): '''Initialize - optionaly provide simplecache object''' if not simplecache: from simplecache import SimpleCache self.cache = SimpleCache() else: self.cache = simplecache addon = xbmcaddon.Addon(id=ADDON_ID) self.api_key = addon.getSetting("tmdb_apikey") del addon def search_movie(self, title, year="", manual_select=False): ''' Search tmdb for a specific movie, returns full details of best match parameters: title: (required) the title of the movie to search for year: (optional) the year of the movie to search for (enhances search result if supplied) manual_select: (optional) if True will show select dialog with all results ''' details = self.select_best_match(self.search_movies(title, year), manual_select=manual_select) if details: details = self.get_movie_details(details["id"]) return details @use_cache(30) def search_movieset(self, title): '''search for movieset details providing the title of the set''' details = {} params = {"query": title, "language": KODI_LANGUAGE} result = self.get_data("search/collection", params) if result: set_id = result[0]["id"] details = self.get_movieset_details(set_id) return details @use_cache(4) def search_tvshow(self, title, year="", manual_select=False): ''' Search tmdb for a specific movie, returns full details of best match parameters: title: (required) the title of the movie to search for year: (optional) the year of the movie to search for (enhances search result if supplied) manual_select: (optional) if True will show select dialog with all results ''' details = self.select_best_match(self.search_tvshows(title, year), manual_select=manual_select) if details: details = self.get_tvshow_details(details["id"]) return details @use_cache(4) def search_video(self, title, prefyear="", preftype="", manual_select=False): ''' Search tmdb for a specific entry (can be movie or tvshow), returns full details of best match parameters: title: (required) the title of the movie/tvshow to search for prefyear: (optional) prefer result if year matches preftype: (optional) prefer result if type matches manual_select: (optional) if True will show select dialog with all results ''' results = self.search_videos(title) details = self.select_best_match(results, prefyear=prefyear, preftype=preftype, preftitle=title, manual_select=manual_select) if details and details["media_type"] == "movie": details = self.get_movie_details(details["id"]) elif details and "tv" in details["media_type"]: details = self.get_tvshow_details(details["id"]) return details @use_cache(4) def search_videos(self, title): ''' Search tmdb for a specific entry (can be movie or tvshow), parameters: title: (required) the title of the movie/tvshow to search for ''' results = [] page = 1 maxpages = 5 while page < maxpages: params = {"query": title, "language": KODI_LANGUAGE, "page": page} subresults = self.get_data("search/multi", params) page += 1 if subresults: for item in subresults: if item["media_type"] in ["movie", "tv"]: results.append(item) else: break return results @use_cache(4) def search_movies(self, title, year=""): ''' Search tmdb for a specific movie, returns a list of all closest matches parameters: title: (required) the title of the movie to search for year: (optional) the year of the movie to search for (enhances search result if supplied) ''' params = {"query": title, "language": KODI_LANGUAGE} if year: params["year"] = try_parse_int(year) return self.get_data("search/movie", params) @use_cache(4) def search_tvshows(self, title, year=""): ''' Search tmdb for a specific tvshow, returns a list of all closest matches parameters: title: (required) the title of the tvshow to search for year: (optional) the first air date year of the tvshow to search for (enhances search result if supplied) ''' params = {"query": title, "language": KODI_LANGUAGE} if year: params["first_air_date_year"] = try_parse_int(year) return self.get_data("search/tv", params) def get_actor(self, name): ''' Search tmdb for a specific actor/person, returns the best match as kodi compatible dict required parameter: name --> the name of the person ''' params = {"query": name, "language": KODI_LANGUAGE} result = self.get_data("search/person", params) if result: result = result[0] cast_thumb = "http://image.tmdb.org/t/p/original%s" % result[ "profile_path"] if result["profile_path"] else "" item = {"name": result["name"], "thumb": cast_thumb, "roles": [item["title"] if item.get("title") else item["name"] for item in result["known_for"]]} return item else: return {} def get_movie_details(self, movie_id): '''get all moviedetails''' params = { "append_to_response": "keywords,videos,credits,images", "include_image_language": "%s,en" % KODI_LANGUAGE, "language": KODI_LANGUAGE } return self.map_details(self.get_data("movie/%s" % movie_id, params), "movie") def get_movieset_details(self, movieset_id): '''get all moviesetdetails''' details = {"art": {}} params = {"language": KODI_LANGUAGE} result = self.get_data("collection/%s" % movieset_id, params) if result: details["title"] = result["name"] details["plot"] = result["overview"] details["tmdb_id"] = result["id"] details["art"]["poster"] = "http://image.tmdb.org/t/p/original%s" % result["poster_path"] details["art"]["fanart"] = "http://image.tmdb.org/t/p/original%s" % result["backdrop_path"] details["totalmovies"] = len(result["parts"]) return details def get_tvshow_details(self, tvshow_id): '''get all tvshowdetails''' params = { "append_to_response": "keywords,videos,external_ids,credits,images", "include_image_language": "%s,en" % KODI_LANGUAGE, "language": KODI_LANGUAGE } return self.map_details(self.get_data("tv/%s" % tvshow_id, params), "tvshow") def get_videodetails_by_externalid(self, extid, extid_type): '''get metadata by external ID (like imdbid)''' params = {"external_source": extid_type, "language": KODI_LANGUAGE} results = self.get_data("find/%s" % extid, params) if results and results["movie_results"]: return self.get_movie_details(results["movie_results"][0]["id"]) elif results and results["tv_results"]: return self.get_tvshow_details(results["tv_results"][0]["id"]) return {} def get_data(self, endpoint, params): '''helper method to get data from tmdb json API''' if self.api_key: params["api_key"] = self.api_key rate_limit = None expiration = datetime.timedelta(days=7) else: params["api_key"] = "ae06df54334aa653354e9a010f4b81cb" # without personal api key = rate limiting and older info from cache rate_limit = ("themoviedb.org",10) expiration = datetime.timedelta(days=60) cachestr = "tmdb.%s" % params.itervalues() cache = self.cache.get(cachestr) if cache: # data obtained from cache result = cache else: # no cache, grab data from API url = u'http://api.themoviedb.org/3/%s' % endpoint result = get_json(url, params) # make sure that we have a plot value (if localized value fails, fallback to english) if result and "language" in params and "overview" in result: if not result["overview"] and params["language"] != "en": params["language"] = "en" result2 = get_json(url, params) if result2 and result2.get("overview"): result = result2 self.cache.set(url, result, expiration=expiration) return result def map_details(self, data, media_type): '''helper method to map the details received from tmdb to kodi compatible formatting''' if not data: return {} details = {} details["tmdb_id"] = data["id"] details["rating"] = data["vote_average"] details["votes"] = data["vote_count"] details["rating.tmdb"] = data["vote_average"] details["votes.tmdb"] = data["vote_count"] details["popularity"] = data["popularity"] details["popularity.tmdb"] = data["popularity"] details["plot"] = data["overview"] details["genre"] = [item["name"] for item in data["genres"]] details["homepage"] = data["homepage"] details["status"] = data["status"] details["cast"] = [] details["castandrole"] = [] details["writer"] = [] details["director"] = [] details["media_type"] = media_type # cast if "credits" in data: if "cast" in data["credits"]: for cast_member in data["credits"]["cast"]: cast_thumb = "" if cast_member["profile_path"]: cast_thumb = "http://image.tmdb.org/t/p/original%s" % cast_member["profile_path"] details["cast"].append({"name": cast_member["name"], "role": cast_member["character"], "thumbnail": cast_thumb}) details["castandrole"].append((cast_member["name"], cast_member["character"])) # crew (including writers and directors) if "crew" in data["credits"]: for crew_member in data["credits"]["crew"]: cast_thumb = "" if crew_member["profile_path"]: cast_thumb = "http://image.tmdb.org/t/p/original%s" % crew_member["profile_path"] if crew_member["job"] in ["Author", "Writer"]: details["writer"].append(crew_member["name"]) if crew_member["job"] in ["Producer", "Executive Producer"]: details["director"].append(crew_member["name"]) if crew_member["job"] in ["Producer", "Executive Producer", "Author", "Writer"]: details["cast"].append({"name": crew_member["name"], "role": crew_member["job"], "thumbnail": cast_thumb}) # artwork details["art"] = {} if data.get("images"): if data["images"].get("backdrops"): fanarts = self.get_best_images(data["images"]["backdrops"]) details["art"]["fanarts"] = fanarts details["art"]["fanart"] = fanarts[0] if fanarts else "" if data["images"].get("posters"): posters = self.get_best_images(data["images"]["posters"]) details["art"]["posters"] = posters details["art"]["poster"] = posters[0] if posters else "" if not details["art"].get("poster") and data.get("poster_path"): details["art"]["poster"] = "http://image.tmdb.org/t/p/original%s" % data["poster_path"] if not details["art"].get("fanart") and data.get("backdrop_path"): details["art"]["fanart"] = "http://image.tmdb.org/t/p/original%s" % data["backdrop_path"] # movies only if media_type == "movie": details["title"] = data["title"] details["originaltitle"] = data["original_title"] if data["belongs_to_collection"]: details["set"] = data["belongs_to_collection"].get("name", "") if data.get("release_date"): details["premiered"] = data["release_date"] details["year"] = try_parse_int(data["release_date"].split("-")[0]) details["tagline"] = data["tagline"] if data["runtime"]: details["runtime"] = data["runtime"] * 60 details["imdbnumber"] = data["imdb_id"] details["budget"] = data["budget"] details["budget.formatted"] = int_with_commas(data["budget"]) details["revenue"] = data["revenue"] details["revenue.formatted"] = int_with_commas(data["revenue"]) if data.get("production_companies"): details["studio"] = [item["name"] for item in data["production_companies"]] if data.get("production_countries"): details["country"] = [item["name"] for item in data["production_countries"]] if data.get("keywords"): details["tag"] = [item["name"] for item in data["keywords"]["keywords"]] # tvshows only if media_type == "tvshow": details["title"] = data["name"] details["originaltitle"] = data["original_name"] if data.get("created_by"): details["director"] += [item["name"] for item in data["created_by"]] if data.get("episode_run_time"): details["runtime"] = data["episode_run_time"][0] * 60 if data.get("first_air_date"): details["premiered"] = data["first_air_date"] details["year"] = try_parse_int(data["first_air_date"].split("-")[0]) if "last_air_date" in data: details["lastaired"] = data["last_air_date"] if data.get("networks"): details["studio"] = [item["name"] for item in data["networks"]] if "origin_country" in data: details["country"] = data["origin_country"] if data.get("external_ids"): details["imdbnumber"] = data["external_ids"].get("imdb_id", "") details["tvdb_id"] = data["external_ids"].get("tvdb_id", "") if "results" in data["keywords"]: details["tag"] = [item["name"] for item in data["keywords"]["results"]] # trailer for video in data["videos"]["results"]: if video["site"] == "YouTube" and video["type"] == "Trailer": details["trailer"] = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s' % video["key"] break return details @staticmethod def get_best_images(images): '''get the best 5 images based on number of likes and the language''' for image in images: score = 0 score += image["vote_count"] score += image["vote_average"] * 10 score += image["height"] if "iso_639_1" in image: if image["iso_639_1"] == KODI_LANGUAGE: score += 1000 image["score"] = score if not image["file_path"].startswith("http"): image["file_path"] = "http://image.tmdb.org/t/p/original%s" % image["file_path"] images = sorted(images, key=itemgetter("score"), reverse=True) return [image["file_path"] for image in images] @staticmethod def select_best_match(results, prefyear="", preftype="", preftitle="", manual_select=False): '''helper to select best match or let the user manually select the best result from the search''' details = {} # score results if one or more preferences are given if results and (prefyear or preftype or preftitle): newdata = [] preftitle = preftitle.lower() for item in results: item["score"] = 0 itemtitle = item["title"] if item.get("title") else item["name"] itemtitle = itemtitle.lower() itemorgtitle = item["original_title"] if item.get("original_title") else item["original_name"] itemorgtitle = itemorgtitle.lower() # high score if year matches if prefyear: if item.get("first_air_date") and prefyear in item["first_air_date"]: item["score"] += 800 # matches preferred year if item.get("release_date") and prefyear in item["release_date"]: item["score"] += 800 # matches preferred year # find exact match on title if preftitle and preftitle == itemtitle: item["score"] += 1000 # exact match! if preftitle and preftitle == itemorgtitle: item["score"] += 1000 # exact match! # match title by replacing some characters if preftitle and get_compare_string(preftitle) == get_compare_string(itemtitle): item["score"] += 750 if preftitle and get_compare_string(preftitle) == get_compare_string(itemorgtitle): item["score"] += 750 # add SequenceMatcher score to the results if preftitle: stringmatchscore = SM(None, preftitle, itemtitle).ratio( ) + SM(None, preftitle, itemorgtitle).ratio() if stringmatchscore > 1.6: item["score"] += stringmatchscore * 250 # higher score if result ALSO matches our preferred type or native language # (only when we already have a score) if item["score"]: if preftype and (item["media_type"] in preftype) or (preftype in item["media_type"]): item["score"] += 250 # matches preferred type if item["original_language"] == KODI_LANGUAGE: item["score"] += 500 # native language! if KODI_LANGUAGE.upper() in item.get("origin_country", []): item["score"] += 500 # native language! if KODI_LANGUAGE in item.get("languages", []): item["score"] += 500 # native language! if item["score"] > 500 or manual_select: newdata.append(item) results = sorted(newdata, key=itemgetter("score"), reverse=True) if results and manual_select: # show selectdialog to manually select the item results_list = [] for item in results: title = item["name"] if "name" in item else item["title"] if item.get("premiered"): year = item["premiered"].split("-")[0] else: year = item.get("first_air_date", "").split("-")[0] if item["poster_path"]: thumb = "http://image.tmdb.org/t/p/original%s" % item["poster_path"] else: thumb = "" label = "%s (%s) - %s" % (title, year, item["media_type"]) listitem = xbmcgui.ListItem(label=label, iconImage=thumb, label2=item["overview"]) results_list.append(listitem) if manual_select and results_list: dialog = DialogSelect("DialogSelect.xml", "", listing=results_list, window_title="%s - TMDB" % xbmc.getLocalizedString(283)) dialog.doModal() selected_item = dialog.result del dialog if selected_item != -1: details = results[selected_item] else: results = [] if not details and results: # just grab the first item as best match details = results[0] return details
class SCAN(object): def __init__(self): self.cache = SimpleCache() self.silent = False self.pDialog = None self.pUpdate = 0 self.matchCNT = 0 self.errorCNT = 0 self.kodiModules = {} def sendJSON(self, command, life=datetime.timedelta(seconds=60)): log('sendJSON, command = ' + (command)) cacheresponse = self.cache.get(ADDON_NAME + '.sendJSON, command = %s'%command) if DEBUG: cacheresponse = None if not cacheresponse: cacheresponse = xbmc.executeJSONRPC(command) self.cache.set(ADDON_NAME + '.sendJSON, command = %s'%command, cacheresponse, expiration=life) return loadJSON(cacheresponse) def okDisable(self, string1, addonName, addonID, state): if yesnoDialog(string1): query = DISABLE_QUERY%(addonID, str(state).lower()) log('okDisable, addonID = %s, state = %s, query = %s'%(addonID, state, query)) results = self.sendJSON(query, life=datetime.timedelta(seconds=1)) if results: if results['result'] == "OK": notificationDialog(LANGUAGE(32010)) return True else: notificationDialog(LANGUAGE(30001)) return False def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: headers = {'User-Agent':'Kodi-Auditor'} req = urllib2.Request(url, None, headers) page = urllib2.urlopen(req, timeout=TIMEOUT) if page.headers.get('Content-Type').find('gzip') >= 0 or page.headers.get('Content-Type').find('application/octet-stream') >= 0: d = zlib.decompressobj(16+zlib.MAX_WBITS) cacheresponse = d.decompress(page.read()) else: cacheresponse = page.read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(hours=12)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) notificationDialog(LANGUAGE(30001)) return '' def preliminary(self): self.validate(background=True) if self.errorCNT > 0: notificationDialog(LANGUAGE(32006)%(self.errorCNT,'s' if self.errorCNT > 1 else ''),time=8000) def validate(self, background=False): log('validate') if getProperty('Running') == 'True': return setProperty('Running','True') self.matchCNT = 0 self.errorCNT = 0 summary = self.scanModules(background) setProperty('Running','False') if background: return if yesnoDialog(LANGUAGE(32009), yes=LANGUAGE(32008), no=LANGUAGE(32007)): self.buildDetails(filterErrors(summary)) else: textViewer('\n'.join([item['label'] for item in summary])) def checkID(self, id): log('checkID, id = %s'%id) match = self.scanID(id) if match: setProperty('checkID.%s'%(id),str(match[0][0])) def scanID(self, id): myModules = self.sendJSON(MOD_QUERY)['result']['addons'] return [self.findModule(myModule, self.kodiModules[self.buildRepo()]) for myModule in myModules if myModule['addonid'].lower() == id.lower()] def buildDetails(self, items): log('buildDetails') select = -1 listItems = [] for item in items: addonID = (item['myModule'].get('id','') or item['myModule'].get('addonid',None)) if addonID is None: continue author = (item['myModule'].get('provider-name','') or item['myModule']['author']) label = '%s v.%s by %s'%(addonID,item['myModule']['version'],author) label2 = 'Enabled: [B]%s[/B] | %s'%(str(item['myModule'].get('enabled',True)),item['label2']) liz = buildListItem((label,label2,addonID)) # liz.setProperty('myModule' ,dumpJSON(item['myModule'])) # liz.setProperty('kodiModule',dumpJSON(item['kodiModule'])) listItems.append(liz) while select is not None: select = selectDialog(listItems,LANGUAGE(32012), preselect=select) if select is None: return sitem = listItems[select] label = sitem.getLabel() label2 = sitem.getLabel2() addonID = sitem.getPath() pselect = selectDialog([buildListItem((mItem,'','')) for mItem in MENU_ITEMS], LANGUAGE(32025)%(addonID)) if pselect == 0: state = not(cleanString(label2.split('Enabled: ')[1].split(' |')[0])) if self.okDisable(LANGUAGE(32011)%(label), label, addonID, state): listItems.pop(select) elif pselect == 1: setWhiteList(addonID) listItems.pop(select) def scanModules(self, background=False): log('scanModules') summary = [] progCNT = 0 if not background: self.pDialog = progressDialog() repository = self.buildRepo() whiteList = getWhiteList()['modules'] myModules = self.sendJSON(MOD_QUERY)['result']['addons'] pTotal = len(myModules) if not background: self.pDialog = progressDialog(1, control=self.pDialog, string1=LANGUAGE(32014), string2=LANGUAGE(32015)%(repository.title())) for idx1, myModule in enumerate(myModules): found = False error = False self.label = '{name} v.{version}{filler}[B]{status}[/B]'.format(name=(myModule['name']),version=(myModule['version']),filler='{filler}',status='{status}') self.pUpdate = (idx1) * 100 // pTotal if not background: self.pDialog = progressDialog(self.pUpdate, control=self.pDialog, string1=LANGUAGE(32016)) found, error, kodiModule = self.findModule(myModule, self.kodiModules[repository], background) log('scanModules, myModule = %s, repository = %s, found = %s'%(myModule['addonid'],repository, found)) verifed = 'True' if found and not error else 'False' if not background: self.pDialog = progressDialog(self.pUpdate, control=self.pDialog, string2=LANGUAGE(32017)%((myModule['addonid']))) if found and error: self.status = LANGUAGE(32004) self.label = self.label.format(filler=generateFiller(self.label,LANGUAGE(32004)),status=self.status) self.label2 = LANGUAGE(32004) elif found and not error: self.status = LANGUAGE(32002) self.label = self.label.format(filler=generateFiller(self.label,LANGUAGE(32002)),status=self.status) self.label2 = LANGUAGE(32002) if not found and not error: self.status = LANGUAGE(32003) self.label = self.label.format(filler=generateFiller(self.label,LANGUAGE(32003)),status=self.status) self.label2 = LANGUAGE(32003) setProperty('checkID',self.status) if myModule['addonid'] in whiteList: continue summary.append({'found':found,'error':error,'label':self.label,'label2':self.label2,'kodiModule':(kodiModule),'myModule':(myModule)}) summary = sortItems(summary) filler = generateFiller(LANGUAGE(32013),'') filler = filler[:(len(filler)/2)-1] summary.insert(0,{'found':False,'error':False,'label':'%s%s%s'%(filler,LANGUAGE(32013),filler),'label2':'','kodiModule':{},'myModule':{}}) summary.insert(1,{'found':False,'error':False,'label':'\n','label2':'','kodiModule':{},'myModule':{}}) if not background: self.pDialog = progressDialog(100, control=self.pDialog, string3=LANGUAGE(32018)) return summary def findModule(self, myModule, kodiModules, background=True): found = False error = False whiteList = getWhiteList()['modules'] for kodiModule in kodiModules: if not background: self.pDialog = progressDialog(self.pUpdate, control=self.pDialog, string3='Checking %s ...'%((kodiModule['id']))) try: if myModule['addonid'].lower() == kodiModule['id'].lower(): found = True self.matchCNT += 1 if myModule['version'] != kodiModule['version']: if not myModule['addonid'] in whiteList: error = True self.errorCNT += 1 break except Exception as e: log('findModule, failed parse %s - %s'%(str(myModule),str(e)), xbmc.LOGERROR) if found: return found, error, kodiModule return found, error, myModule def buildRepo(self): busy = False if self.silent else busyDialog() repository = BUILDS[self.sendJSON(VER_QUERY)['result']['version']['major']] log('buildRepo, repository = %s'%(repository)) self.kodiModules[repository] = list(self.buildModules(repository, busy)) return repository def buildModules(self, branch, busy=False): log('buildModules, branch = ' + (branch)) try: tree = ET.fromstring(self.openURL(BASE_URL%(branch))) for idx, elem in enumerate(tree.iter()): if busy: busy = busyDialog(idx + 1, busy) if elem.tag == 'addon': addon = elem.attrib.copy() if elem.tag == 'extension' and elem.attrib.copy()['point'] == 'xbmc.python.module': yield (addon) except Exception as e: log("buildModules, Failed! " + str(e), xbmc.LOGERROR) if busy: busyDialog(100) def getParams(self): try: return dict(parse_qsl(sys.argv[2][1:])) except: return None
class Disclose(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self): self.addDir(LANGUAGE(30003), BASE_VID, 1) self.addYoutube(LANGUAGE(30004), 'plugin://plugin.video.youtube/channel/UCA-Ls4dkRBXHMjRjeTDTdjg/') def browse(self, url): log('browse') soup = BeautifulSoup(self.openURL(url), "html.parser") videos = soup('div', {'class': 'teaser teaser--third'}) for video in videos: try: try: thumb = 'http:%s'%(video('div', {'class': 'ratio-container ratio16_9'})[0].find('img').attrs['data-src']) except: thumb = FANART items = video('div', {'class': 'teaser__caption'}) vid_url = BASE_URL + (items[0]('a', {'class': 'article-link'})[0].attrs['href']) label = items[0]('a', {'class': 'article-link'})[0].get_text() timeago = items[0]('span', {'class': 'meta-timeago'})[0].get_text() plot = '%s - %s'%(timeago, label) try: genre = video('span', {'class': 'teaser-figure__cat'})[0].get_text() except: genre = 'Unknown' runtime = (video('span', {'class': 'teaser-figure__len'})[0].get_text()).split(':') if len(runtime) == 3: h, m, s = runtime duration = int(h) * 3600 + int(m) * 60 + int(s) else: m, s = runtime duration = (int(m) * 60) + int(s) infoLabels = {"mediatype":"episode","label":label ,"title":label,"duration":duration,"plot":plot} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, vid_url, 9, infoLabels, infoArt, len(videos)) except Exception as e: log("browse Failed! " + str(e), xbmc.LOGERROR) next = soup('li', {'class': 'more-container__button m-auto'}) if len(next) == 0: return next_url = BASE_URL + next[0].find('a').attrs['href'] next_label = (next[0].find('a').attrs['title'] or next[0].get_text()) self.addDir(next_label, next_url, 1) def resolveURL(self, name, url): try: data = json.loads(re.findall('"drupal-settings-json">(.+?)</script>',self.openURL(url), flags=re.DOTALL)[0])['dtv_video'] provider = data['provider'] log('resolveURL, provider = ' + provider) url = re.findall('src="(.+?)"',(data['player_code']), flags=re.DOTALL)[0].split('?')[0] if provider == 'youtube': if len(re.findall('http[s]?://www.youtube.com/embed', url)) > 0: url = YTURL + url.split('/embed/')[1] elif len(re.findall('http[s]?://www.youtube.com/watch', url)) > 0: url = YTURL + url.split('/watch?v=')[1] elif len(re.findall('http[s]?://youtu.be/', url)) > 0: url = YTURL + url.split('/youtu.be/')[1] elif provider == 'vimeo': if len(re.findall('http[s]?://vimeo.com/', url)) > 0: url = VMURL + url.split('/vimeo.com/')[1] else: raise Exception('resolveURL, unknown provider; data =' + json.dumps(data)) log('resolveURL, url = ' + url) return xbmcgui.ListItem(name, path=url) except Exception as e: log("resolveURL Failed! " + str(e), xbmc.LOGERROR) if isUWP(): return '' from YDStreamExtractor import getVideoInfo info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) try: if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) except: pass return liz def playVideo(self, name, url): log('playVideo') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.resolveURL(name, url)) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class Newsmax(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=6)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMainMenu(self): self.addLink(LANGUAGE(30002),'',0) self.addYoutube(LANGUAGE(30003),'plugin://plugin.video.youtube/user/NewsmaxTV/') def buildLiveLink(self): log('buildLiveLink') try: site = json.loads(self.openURL(BASE_URL).split('<script type="application/ld+json">')[1].split('</script>')[0])['embedUrl'] data = ((self.openURL(site).replace('\n','').replace('\r','').replace('\t','').replace('\\','')).split('"entryResult":')[1]).split(',"recordedEntryId"')[0]+'}}' url = 'https://'+((re.compile('https://(.+?)m3u8', re.DOTALL).search(data)).group(1))+'m3u8' except: url = YT_LIVE self.playVideo(LANGUAGE(30004),url) def playVideo(self, name, url): log('playVideo') liz = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name,"genre":"News"}) 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 addYoutube(self, title, url): liz=xbmcgui.ListItem(title) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":title,"title":title} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True)
class CC(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(days=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) if str(e).startswith('HTTP Error 500'): return '' xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube(LANGUAGE(30006), 'plugin://plugin.video.youtube/user/comedycentral/') def browse(self, name, url): log('browse, ' + name) response = self.openURL(url) if len(response) == 0: return try: items = json.loads(re.search('var triforceManifestFeed = (.+?);\n',response).group(1)) except: items = json.loads(re.search('var triforceManifestURL = "(.+?)";',response).group(1)) try: thumb = (response.split('//meta[@property="og:image"]/@content')[0].strip() or ICON) except: thumb = ICON if not thumb.endswith(('.png','.jpg')): thumb = ICON elif thumb.startswith('//'): thumb = 'http:%s'%thumb if items and 'manifest' not in items: return for item in items['manifest']['zones']: if item in ('header', 'footer', 'ads-reporting', 'ENT_M171'): continue try: result = items['manifest']['zones'][item]['feed'] except: result = None if result is None: continue try: ent_code = result.split('/feeds/')[1].split('/')[0] except: try: ent_code = result.split('/modules/')[1].split('/')[0] except: ent_code = '' ent_code = ent_code.split('_cc')[0].split('_tosh')[0] try: jsonResponse = json.loads(self.openURL(result))['result'] except: log('browse, jsonResponse failed! ' + str(jsonResponse)) if ent_code == 'ent_m081': return self.buildEpisodes(name, url, jsonResponse, jsonResponse['episodes']) elif ent_code == 'ent_m013': return self.buildEpisodes(name, url, jsonResponse, jsonResponse['episodes']) elif ent_code in ['ent_m100','ent_m150']: for item in jsonResponse['data']['items']: if ent_code == 'ent_m100' and name == LANGUAGE(30008): self.buildShow(item) elif ent_code == 'ent_m150' and name == LANGUAGE(30004): for show in item['sortedItems']: self.buildShow(show) def buildShow(self, show): vid_url = (show.get('canonicalURL','') or show.get('url',None)) title = (show.get('title','') or show.get('shortTitle',None)) plot = (show.get('description','') or show.get('shortDescription','') or title) if vid_url is None or title is None or not vid_url.startswith(BASE_URL): return try: thumb = show['image']['url'] except: thumb = LOGO_URL%(urllib.quote(title)) infoLabels = {"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title,"plot":plot} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,vid_url,1,infoLabels,infoArt) def buildEpisodes(self, name, url, jsonResponse=None, videos=[], jsonKey='episodes'): log('buildEpisodes, ' + name) if jsonResponse is None: jsonResponse = json.loads(self.openURL(url))['result'] videos = jsonResponse[jsonKey] for video in videos: vid_url = (video.get('canonicalURL','') or video.get('url',None)) title = (video.get('title','') or video.get('shortTitle',None)) plot = (video.get('description','') or video.get('shortDescription','') or title) if vid_url is None or title is None: continue elif not vid_url.startswith(BASE_URL): continue try: show = video['show'].get('title',None) except: show = name try: thumb = video['images'][0]['url'] except: thumb = video['image'][0]['url'] try: season = int(video['season']['seasonNumber']) except: season = 0 try: episode = int(video['season']['episodeAiringOrder']) except: episode = 0 label = '%s - %s'%(show,title) seinfo = ('S' + ('0' if season < 10 else '') + str(season) + 'E' + ('0' if episode < 10 else '') + str(episode)) if season + episode > 0: label = '%s - %s - %s'%(show, seinfo, title) try: aired = datetime.datetime.fromtimestamp(float(video['airDate'])) except: aired = datetime.datetime.now() try: duration = video['duration'] except: duration = 0 infoLabels = {"mediatype":"episode","label":label ,"title":label,"TVShowTitle":show,"plot":plot,"aired":aired.strftime('%Y-%m-%d'),"duration":duration,"season":season,"episode":episode} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} CONTENT_TYPE = 'episodes' self.addLink(label, vid_url, 9, infoLabels, infoArt, len(videos)) try: next_page = jsonResponse['nextPageURL'] except: next_page = None if next_page: self.addDir('>> Next',next_page, 2) def playVideo(self, name, url, liz=None): log('playVideo') info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() if len(info) > 1: if PTVL_RUNNING: return xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30007), ICON, 4000) info = sorted(info, key=lambda x: x['idx']) plst = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) plst.clear() xbmc.sleep(200) for videos in info: vidIDX = videos['idx'] url = videos['xbmc_url'] liz = xbmcgui.ListItem(videos['title'], path=url) try: if 'subtitles' in videos['ytdl_format']: liz.setSubtitles([x['url'] for x in videos['ytdl_format']['subtitles'].get('en','') if 'url' in x]) except: pass plst.add(url, liz, vidIDX) if vidIDX == 0: xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) plst.unshuffle() else: liz = xbmcgui.ListItem(info[0]['title'], path=info[0]['xbmc_url']) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class PlutoTV(object): def __init__(self, sysARG): log('__init__, sysARG = ' + str(sysARG)) self.sysARG = sysARG self.net = net.Net() self.cache = SimpleCache() self.region = self.getRegion() self.filter = False if self.region == 'US' else True self.categoryMenu = self.getCategories() self.mediaType = self.getMediaTypes() log('__init__, region = ' + self.region) def getRegion(self): return (self.openURL(REGION_URL, life=datetime.timedelta( hours=12)).get('countryCode', '') or 'US') def login(self): log('login') #ignore guest login if USER_EMAIL == LANGUAGE(30009): return if len(USER_EMAIL) > 0: header_dict = {} header_dict[ 'Accept'] = 'application/json, text/javascript, */*; q=0.01' header_dict['Host'] = 'api.pluto.tv' header_dict['Connection'] = 'keep-alive' header_dict['Referer'] = 'http://pluto.tv/' header_dict['Origin'] = 'http://pluto.tv' header_dict[ 'User-Agent'] = 'Mozilla/5.0 (Windows NT 6.2; rv:24.0) Gecko/20100101 Firefox/24.0' try: xbmcvfs.rmdir(COOKIE_JAR) except: pass if xbmcvfs.exists(COOKIE_JAR) == False: try: xbmcvfs.mkdirs(SETTINGS_LOC) f = xbmcvfs.File(COOKIE_JAR, 'w') f.close() except: log('login, Unable to create the storage directory', xbmc.LOGERROR) form_data = ({ 'optIn': 'true', 'password': PASSWORD, 'synced': 'false', 'userIdentity': USER_EMAIL }) self.net.set_cookies(COOKIE_JAR) try: loginlink = json.loads( self.net.http_POST( LOGIN_URL, form_data=form_data, headers=header_dict).content.encode("utf-8").rstrip()) if loginlink and loginlink['email'].lower( ) == USER_EMAIL.lower(): xbmcgui.Dialog().notification( ADDON_NAME, LANGUAGE(30006) + loginlink['displayName'], ICON, 4000) self.net.save_cookies(COOKIE_JAR) else: xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30007), ICON, 4000) except Exception as e: log('login, Unable to create the storage directory ' + str(e), xbmc.LOGERROR) else: #firstrun wizard if yesnoDialog(LANGUAGE(30008), no=LANGUAGE(30009), yes=LANGUAGE(30010)): REAL_SETTINGS.setSetting('User_Email', inputDialog(LANGUAGE(30001))) REAL_SETTINGS.setSetting('User_Password', inputDialog(LANGUAGE(30002))) else: REAL_SETTINGS.setSetting('User_Email', LANGUAGE(30009)) xbmc.executebuiltin('RunScript("' + ADDON_PATH + '/country.py' + '")') def openURL(self, url, life=datetime.timedelta(minutes=1)): log('openURL, url = ' + url) try: header_dict = {} header_dict[ 'Accept'] = 'application/json, text/javascript, */*; q=0.01' header_dict['Host'] = 'api.pluto.tv' header_dict['Connection'] = 'keep-alive' header_dict['Referer'] = 'http://pluto.tv/' header_dict['Origin'] = 'http://pluto.tv' header_dict[ 'User-Agent'] = 'Mozilla/5.0 (Windows NT 6.2; rv:24.0) Gecko/20100101 Firefox/24.0' self.net.set_cookies(COOKIE_JAR) trans_table = ''.join([chr(i) for i in range(128)] + [' '] * 128) cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponse: try: cacheResponse = self.net.http_GET( url, headers=header_dict).content.encode("utf-8", 'ignore') except: cacheResponse = (self.net.http_GET( url, headers=header_dict).content.translate(trans_table) ).encode("utf-8") self.net.save_cookies(COOKIE_JAR) self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, cacheResponse, expiration=life) if isinstance(cacheResponse, basestring): cacheResponse = json.loads(cacheResponse) return cacheResponse except Exception as e: log('openURL, Unable to open url ' + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification( ADDON_NAME, 'Unable to Connect, Check User Credentials', ICON, 4000) return {} def mainMenu(self): log('mainMenu') self.login() for item in PLUTO_MENU: self.addDir(*item) def browseMenu(self): log('browseMenu') for item in self.categoryMenu: self.addDir(*item) def getCategories(self): log('getCategories') collect = [] lineup = [] data = self.getChannels() for channel in data: collect.append(channel['category']) counter = collections.Counter(collect) for key, value in sorted(counter.iteritems()): lineup.append(("%s" % (key), BASE_LINEUP, 2)) lineup.insert(0, ("Featured", BASE_LINEUP, 2)) # lineup.insert(2,(LANGUAGE(30014), BASE_LINEUP, 2)) return lineup def getMediaTypes(self): mediaType = {} for type in self.categoryMenu: type = type[0] if type == 'Movies': mediaType[type] = 'movie' elif type == 'TV': mediaType[type] = 'episodes' elif type == 'Music + Radio': mediaType[type] = 'musicvideo' else: mediaType[type] = 'video' return mediaType def browse(self, chname, url): log('browse, chname = ' + chname) geowarn = False data = (self.openURL(url)) for channel in data: id = channel['_id'] cat = channel['category'] number = channel['number'] filter = channel.get('regionFilter', {}) if isinstance(filter, list): filter = dict(filter) region = filter.get('include', 'US') exclude = filter.get('exclude', '') name = channel['name'] plot = channel.get('description', '') feat = (channel.get('featured', '') or 0) == -1 if self.filter == True and (self.region in exclude or self.region not in region): if geowarn == False: geowarn = True xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30004), ICON, 4000) continue thumb = ICON if 'thumbnail' in channel: thumb = (channel['thumbnail'].get('path', ICON) or ICON) land = FANART if 'featuredImage' in channel: land = (channel['featuredImage'].get('path', FANART) or FANART) logo = ICON if 'logo' in channel: logo = (channel['logo']['path'] or ICON) if chname == LANGUAGE(30014): title = "%s - %s: %s" % (cat, number, name) infoLabels = { "mediatype": self.mediaType[cat], "label": title, "title": title, "plot": plot, "code": number, "genre": cat, "imdbnumber": id } infoArt = { "thumb": thumb, "poster": thumb, "fanart": land, "icon": logo, "logo": logo } self.addDir(title, id, 8, infoLabels, infoArt) elif chname == "Featured" and feat == True: title = "%s - %s: %s" % (cat, number, name) infoLabels = { "mediatype": self.mediaType[cat], "label": title, "title": title, "plot": plot, "code": number, "genre": cat, "imdbnumber": id } infoArt = { "thumb": thumb, "poster": thumb, "fanart": land, "icon": logo, "logo": logo } self.addDir(title, id, 8, infoLabels, infoArt) elif chname.lower() == cat.lower(): title = "%s: %s" % (number, name) infoLabels = { "mediatype": self.mediaType[cat], "label": title, "title": title, "plot": plot, "code": number, "genre": cat, "imdbnumber": id } infoArt = { "thumb": thumb, "poster": thumb, "fanart": land, "icon": logo, "logo": logo } self.addDir(title, id, 8, infoLabels, infoArt) def pagination(self, seq, rowlen): for start in xrange(0, len(seq), rowlen): yield seq[start:start + rowlen] def browseGuide(self, start=0, end=14): log('browseGuide') geowarn = False start = 0 if start == BASE_LINEUP else int(start) data = list(self.pagination(self.getChannels(), end)) start = 0 if start >= len(data) else start # if start == 0 and end == 14: self.addDir(LANGUAGE(30014), '', 10) for channel in data[start]: chid = channel['_id'] chcat = channel['category'] chnum = channel['number'] filter = channel.get('regionFilter', {}) if isinstance(filter, list): filter = dict(filter) region = filter.get('include', 'US') exclude = filter.get('exclude', '') chname = channel['name'] chplot = channel.get('description', '') chthumb = ICON if 'thumbnail' in channel: chthumb = ((channel['thumbnail'].get('path', '')).replace( ' ', '%20') or ICON) feat = (channel.get('featured', '') or 0) == -1 if self.filter == True and (self.region in exclude or self.region not in region): if geowarn == False: geowarn = True xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30004), ICON, 4000) continue timelines = channel['timelines'] item = timelines[0] epid = (item['episode']['_id']) epname = item['episode']['name'] epplot = (item['episode'].get('description', epname) or epname) epgenre = (item['episode'].get('genre', chcat) or chcat) epdur = int(item['episode'].get('duration', '0') or '0') // 1000 live = item['episode']['liveBroadcast'] thumb = chthumb #(item['episode']['thumbnail']['path'] or chthumb) #site doesn't update missing episode thumbs title = "%s: %s - %s" % (chnum, chname, epname) if any(k.lower().startswith(title.lower()) for k in IGNORE_KEYS): continue infoLabels = { "mediatype": self.mediaType[chcat], "label": title, "title": title, "plot": epplot, "code": epid, "genre": epgenre, "imdbnumber": chid, "duration": epdur } infoArt = { "thumb": thumb, "poster": thumb, "fanart": FANART, "icon": ICON, "logo": ICON } self.addLink(title, chid, 9, infoLabels, infoArt, end) start += 1 if end == 14: self.addDir(LANGUAGE(30015), '%s' % (start), 0) def playChannel(self, name, url): log('playChannel, url = %s' % url) origurl = url if PTVL_RUN: self.playContent(name, url) data = self.getChannels() for link in data: if link['_id'] == url: break item = link['timelines'][0] id = item['episode']['_id'] ch_start = datetime.datetime.fromtimestamp( time.mktime( time.strptime((item["start"].split('.')[0]), "%Y-%m-%dT%H:%M:%S"))) ch_timediff = (datetime.datetime.now() - ch_start).seconds dur_sum = 0 playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist.clear() xbmc.sleep(100) for idx, field in enumerate(self.openURL(BASE_CLIPS % (id))): url = (field['url'] or field['code']) name = field['name'] thumb = (field['thumbnail'] or ICON) provider = field['provider'] url = self.resolveURL(provider, url) dur = int(field['duration'] or '0') // 1000 dur_start = dur_sum dur_sum += dur liz = xbmcgui.ListItem(name, path=url) infoList = { "mediatype": "video", "label": name, "title": name, "duration": dur } infoArt = { "thumb": thumb, "poster": thumb, "icon": ICON, "fanart": FANART } liz.setInfo(type="Video", infoLabels=infoList) liz.setArt(infoArt) liz.setProperty("IsPlayable", "true") liz.setProperty("IsInternetStream", str(field['liveBroadcast']).lower()) if 'm3u8' in url.lower() and inputstreamhelper.Helper( 'hls').check_inputstream(): liz.setProperty('inputstreamaddon', 'inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type', 'hls') if dur_start < ch_timediff and dur_sum > ch_timediff: vid_offset = ch_timediff - dur_start liz.setProperty('ResumeTime', str(vid_offset)) playlist.add(url, liz, idx) if idx == 0: xbmcplugin.setResolvedUrl(int(self.sysARG[1]), True, liz) def playContent(self, name, url): log('playContent, url = %s' % (url)) origurl = url data = self.getGuidedata() for link in data: if link['_id'] == url: break try: item = link['timelines'][0] except Exception as e: return id = item['episode']['_id'] ch_start = datetime.datetime.fromtimestamp( time.mktime( time.strptime((item["start"].split('.')[0]), "%Y-%m-%dT%H:%M:%S"))) ch_timediff = (datetime.datetime.now() - ch_start).seconds data = (self.openURL(BASE_CLIPS % (id))) dur_sum = 0 for idx, field in enumerate(data): url = (field['url'] or field['code']) name = field['name'] thumb = (field['thumbnail'] or ICON) provider = (field['provider'] or None) url = urllib.quote(json.dumps({"provider": provider, "url": url})) dur = int(field['duration'] or '0') // 1000 dur_start = dur_sum dur_sum += dur if any(k.lower().startswith(name.lower()) for k in IGNORE_KEYS): continue infoList = { "mediatype": "video", "label": name, "title": name, "duration": dur } infoArt = { "thumb": thumb, "poster": thumb, "icon": ICON, "fanart": FANART } if PTVL_RUN: self.playVideo(name, url) else: self.addLink(name, url, 7, infoList, infoArt, len(data)) @use_cache(1) def resolveURL(self, provider, url): log('resolveURL, provider = ' + str(provider) + ', url = ' + url) if provider == 'jwplatform' or 'm3u8' in url.lower() or url is None: return url elif provider == 'youtube': url = url.replace('feature=player_embedded&', '') if len(re.findall('http[s]?://www.youtube.com/watch', url)) > 0: return YTURL + url.split('/watch?v=')[1] elif len(re.findall('http[s]?://youtu.be/', url)) > 0: return YTURL + url.split('/youtu.be/')[1] elif provider == 'vimeo': if len(re.findall('http[s]?://vimeo.com/', url)) > 0: return VMURL + url.split('/vimeo.com/')[1] else: info = None if isUWP() == False: from YDStreamExtractor import getVideoInfo info = getVideoInfo(url, 3, True) if info is None: return YTURL + 'W6FjQgmtt0k' info = info.streams() return info[0]['xbmc_url'] def playVideo(self, name, url, liz=None): log('playVideo') url = json.loads(urllib.unquote(url)) provider = url['provider'] url = url['url'] if liz is None: liz = xbmcgui.ListItem(name, path=self.resolveURL(provider, url)) if 'm3u8' in url.lower() and inputstreamhelper.Helper( 'hls').check_inputstream() and not DEBUG: liz.setProperty('inputstreamaddon', 'inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type', 'hls') xbmcplugin.setResolvedUrl(int(self.sysARG[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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 = self.sysARG[0] + "?url=" + urllib.quote(u) + "&mode=" + str( mode) + "&name=" + urllib.quote(name) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, totalItems=total) def addDir(self, name, u, mode, infoList=False, infoArt=False): name = name.encode("utf-8") log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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 = self.sysARG[0] + "?url=" + urllib.quote(u) + "&mode=" + str( mode) + "&name=" + urllib.quote(name) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, isFolder=True) def poolList(self, method, items): results = [] if ENABLE_POOL: pool = ThreadPool(cpu_count()) results = pool.imap_unordered(method, items) pool.close() pool.join() else: results = [method(item) for item in items] results = filter(None, results) return results def getChannels(self): return sorted(self.openURL(BASE_LINEUP, life=datetime.timedelta(minutes=5)), key=lambda i: i['number']) def getGuidedata(self): return sorted((self.openURL( BASE_GUIDE % (datetime.datetime.now().strftime('%Y-%m-%dT%H:00:00').replace( 'T', '%20').replace(':00:00', '%3A00%3A00.000-400')), life=datetime.timedelta(hours=1))), key=lambda i: i['number']) # @buildChannels({'refresh_path':urllib.quote("plugin://%s?mode=20"%ADDON_ID),'refresh_interval':"7200"}) def uEPG(self): log('uEPG') #support for uEPG universal epg framework module available from the Kodi repository. https://github.com/Lunatixz/KODI_Addons/tree/master/script.module.uepg data = self.getChannels() self.link = self.getGuidedata() return self.poolList(self.buildGuide, data) def buildGuide(self, channel): chthumb = '' chlogo = '' chid = channel['_id'] chcat = channel['category'] chnum = channel['number'] filter = channel.get('regionFilter', {}) if isinstance(filter, list): filter = dict(filter) region = filter.get('include', 'US') exclude = filter.get('exclude', '') chname = channel['name'] chplot = channel.get('description', '') isFavorite = False #(channel.get('featured','') or 0) == -1 if self.filter == True and (self.region in exclude or self.region not in region): return if 'thumbnail' in channel: chthumb = (channel['thumbnail'].get('path', '') or '') if 'logo' in channel: chlogo = (channel['logo'].get('path', '') or '') log('buildGuide, channel = ' + str(chnum)) newChannel = {} guidedata = [] newChannel['channelname'] = chname newChannel['channelnumber'] = chnum newChannel['channellogo'] = chlogo newChannel['isfavorite'] = isFavorite for i in range(len(self.link.get(chid, []))): item = self.link[chid][i] epname = item['episode']['name'] epid = (item['episode']['_id']) epplot = (item['episode'].get('description', epname) or epname) epgenre = (item['episode'].get('genre', chcat) or chcat) epsubgenre = (item['episode'].get('subGenre', '') or '') genre = '%s + %s' % ( epgenre, epsubgenre) if len(epsubgenre) > 0 else epgenre epdur = int(item['episode'].get('duration', '0') or '0') // 1000 live = item['episode']['liveBroadcast'] == "true" thumb = chthumb poster = (item['episode'].get('thumbnail', '').get( 'path', chthumb) or chthumb) clips = self.link[chid] if len(clips) == 0: return tmpdata = {} clips = clips[0] id = clips['episode']['_id'] data = (self.openURL(BASE_CLIPS % (id))) for field in data: url = (field['url'] or field['code']) name = field['name'] thumb = (field['thumbnail'] or ICON) provider = (field['provider'] or None) url = urllib.quote( json.dumps({ "provider": provider, "url": url })) dur = int(field['duration'] or '0') // 1000 title = "%s: %s" % (chname, epname) if any(k.lower().startswith(title.lower()) for k in IGNORE_KEYS): return tmpdata = { "mediatype": self.mediaType[chcat], "label": title, "title": chname, "originaltitle": epname, "plot": epplot, "code": epid, "genre": chcat, "imdbnumber": chid, "duration": dur } tmpdata['starttime'] = int( time.mktime( time.strptime((item["start"].split('.')[0]), "%Y-%m-%dT%H:%M:%S"))) tmpdata['url'] = self.sysARG[0] + '?mode=7&name=%s&url=%s' % ( title, url) tmpdata['art'] = { "thumb": thumb, "clearart": poster, "fanart": FANART, "icon": chthumb, "clearlogo": chlogo } guidedata.append(tmpdata) newChannel['guidedata'] = guidedata return newChannel def getParams(self): return dict(urlparse.parse_qsl(self.sysARG[2][1:])) def run(self): params = self.getParams() 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: self.mainMenu() elif mode == 0: self.browseGuide(url) elif mode == 1: self.browseMenu() elif mode == 2: self.browse(name, url) elif mode == 7: self.playVideo(name, url) elif mode == 8: self.playContent(name, url) elif mode == 9: self.playChannel(name, url) elif mode == 10: self.browseGuide(end=5000) elif mode == 20: xbmc.executebuiltin( "RunScript(script.module.uepg,json=%s&skin_path=%s&refresh_path=%s&refresh_interval=%s&row_count=%s)" % (urllib.quote(json.dumps(list( self.uEPG()))), urllib.quote(ADDON_PATH), urllib.quote(self.sysARG[0] + "?mode=20"), "7200", "5")) xbmcplugin.setContent(int(self.sysARG[1]), CONTENT_TYPE) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_NONE) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_TITLE) xbmcplugin.endOfDirectory(int(self.sysARG[1]), cacheToDisc=False)
class SpikeTV(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(days=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube("Browse Youtube" , 'plugin://plugin.video.youtube/user/SpikeTV/') def browse(self, name, url): log('browse, ' + name) response = self.openURL(url) if len(response) == 0: return try: items = json.loads(re.search('var triforceManifestURL = "(.+?)";',response).group(1)) except: items = json.loads(re.search('var triforceManifestFeed = (.+?);',response).group(1)) try: thumb = (response.split('//meta[@property="og:image"]/@content')[0].strip() or ICON) except: thumb = ICON thumb = (thumb or ICON) if not thumb.endswith(('.png','.jpg')): thumb = ICON if items and 'manifest' not in items: return for item in items['manifest']['zones']: if item in ('header', 'footer', 'ads-reporting', 'ENT_M171'): continue try: result = items['manifest']['zones'][item]['feed'] except: result = None if result is None: continue try: ent_code = result.split('/feeds/')[1].split('/')[0] except: ent_code = '' ent_code = ent_code.split('_spike')[0] if ent_code not in IGNORE_LIST: continue jsonResponse = json.loads(self.openURL(result)) if ent_code == 'ent_m151': try: title = jsonResponse['result']['data']['headerText'].title() except: continue infoLabels = {"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} if name != 'Latest Episodes': self.addDir(title,result,2,infoLabels,infoArt) for item in jsonResponse['result']['data']['shows']: title = item['title'] infoLabels = {"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} if name == 'Latest Episodes' and title == 'all shows': return self.browseVideos(title, item['url']) elif name == 'Latest Episodes': continue self.addDir(title,item['url'],2,infoLabels,infoArt) elif ent_code == 'ent_m112': try: title = jsonResponse['result']['promo']['headline'].title() except: continue if title == 'Full Episodes': return self.browseVideos(title, result) elif name != 'Full Episodes': continue infoLabels = {"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,result,2,infoLabels,infoArt) else: if ent_code == 'ent_m116': type = 'filters' try: title = jsonResponse['result']['promo']['headline'].title() except: continue else: type = 'items' try: title = jsonResponse['result']['data']['headerText'].title() except: try: title = jsonResponse['result']['data']['header']['title'].title() except: continue myURL = json.dumps({"url":result,"type":type}) if title in ['Full Episodes','All Shows']: return self.browseShows(title, myURL) elif title == 'Featured Shows': continue elif name != 'Full Episodes' and name != 'Browse Shows': continue infoLabels = {"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} # if name == 'Browse Shows' and title == 'All Shows': return self.browseShows(title, myURL) self.addDir(title,myURL,3,infoLabels,infoArt) def browseVideos(self, name, url): log('browseVideos, ' + name) jsonResponse = json.loads(self.openURL(url)) try: videos = jsonResponse['result']['items'] except: try: videos = jsonResponse['result']['data']['items'] except: return for video in videos: try: vid_url = video['canonicalURL'] except: try: vid_url = video['itemURL'] except: continue if not ('/video-clips/') in vid_url and not ('/video-playlists/') in vid_url and not ('/full-episodes/') in vid_url and not ('/episodes/') in vid_url: continue if 'bellator.spike.com' in vid_url: continue try: thumb = (video['images'][0]['url'] or ICON) except: try: thumb = (video['image'][0]['url'] or ICON) except: thumb = ICON thumb = (thumb or ICON) if thumb and thumb.startswith('//'): thumb = 'http:' + thumb try: show = video['show']['title'] except: show = video['showTitle'] try: episodeNumber = int(video['season']['episodeNumber']) except: episodeNumber = 0 try: seasonNumber = int(video['season']['seasonNumber']) except: seasonNumber = 0 try: raw_date = video['airDate'] except: raw_date = video['publishDate'] try: aired = (datetime.datetime.strptime(raw_date, '%m/%d/%y')) except: aired = datetime.datetime.now() aired = aired.strftime('%Y-%m-%d') try: runtime = video['duration'].split(':') if len(runtime) == 3: h, m, s = runtime duration = int(h) * 3600 + int(m) * 60 + int(s) else: m, s = runtime duration = int(m) * 60 + int(s) except: duration = video['duration'] label = video['title'] seinfo = ('S' + ('0' if seasonNumber < 10 else '') + str(seasonNumber) + 'E' + ('0' if episodeNumber < 10 else '') + str(episodeNumber)) label = '%s'%(label) if seasonNumber + episodeNumber == 0 else '%s - %s'%(label, seinfo) label = '%s - %s'%(show,label) if len(show) > 0 else label infoLabels = {"mediatype":"episode","label":label ,"title":label,"TVShowTitle":show,"duration":duration,"plot":video['description'],"aired":aired} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} CONTENT_TYPE = 'episodes' self.addLink(label, vid_url, 9, infoLabels, infoArt, len(videos)) try: next_page = jsonResponse['result']['data']['nextPageURL'] except: next_page = None if next_page: self.addDir('>> Next',next_page, 2) def browseShows(self, name, myURL): log('browseShows, ' + name) myURL = json.loads(myURL) url = myURL['url'] type = myURL['type'] feed = url counter = 0 jsonResponse = json.loads(self.openURL(url)) try: shows = jsonResponse['result']['data'][type] except: try: shows = jsonResponse['result'][type] except: shows = [] for item in shows: if '/ent_m150/' in feed or '/ent_m100/' in feed or '/ent_m069/' in feed: try: url = item['canonicalURL'] except: try: url = item['url'] except: continue if '/shows/' not in url: continue if item['title'] in IGNORE_LIST: continue try: thumb = (item['image']['url'] or ICON) except: try: thumb = (item['image'][0]['url'] or ICON) except: thumb = ICON thumb = (thumb or ICON) if thumb.startswith('//'): thumb = 'https:' + thumb title = item['title'] infoLabels = {"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,1,infoLabels,infoArt) def playVideo(self, name, url, liz=None): log('playVideo') if PTVL_RUNNING: return info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() info = sorted(info, key=lambda x: x['idx']) plst = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) plst.clear() for videos in info: vidIDX = videos['idx'] url = videos['xbmc_url'] liz = xbmcgui.ListItem(videos['title'], path=url) if 'subtitles' in videos['ytdl_format']: liz.setSubtitles([x['url'] for x in videos['ytdl_format']['subtitles'].get('en','') if 'url' in x]) plst.add(url, liz, vidIDX) if vidIDX == 0: xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) plst.unshuffle() def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class CBS(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) response = urllib2.urlopen(request, timeout = TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=1)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except urllib2.URLError as e:log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout as e:log("openURL Failed! " + str(e), xbmc.LOGERROR) except Exception as e:log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube("Browse Youtube" , 'plugin://plugin.video.youtube/user/CBS/') def browseLatest(self, url=None): if url is None: url = WATCH_URL soup = BeautifulSoup(self.openURL(url), "html.parser") items = soup('li', {'class': 'episode'}) for item in items: seasonNumber = 0 episodeNumber = 0 metas = re.findall(r'<meta content="(.*?)>', str(item), re.DOTALL) metaTYPES = ["name","description","thumbnailUrl","uploadDate","url","seasonNumber"] metaLST = {} metaKeys = [] for type in metaTYPES: for meta in metas: if 'itemprop="%s"'%type in meta: if type == "name" and 'Episode' in meta: try: metaLST['episodeNumber'] = (int(filter(str.isdigit, str(meta.split('" itemprop="%s"'%type)[0]))) or 0) except: metaLST['episodeNumber'] = 0 elif type == "name" and 'Season' in meta: try: metaLST['seasonNumber'] = (int(filter(str.isdigit, str(meta.split('" itemprop="%s"'%type)[0]))) or 0) except: metaLST['episodeNumber'] = 0 else: if type not in metaKeys: metaKeys.append(type) metaLST[type] = meta.split('" itemprop="%s"'%type)[0] try: label = unescape(uni((metaLST.get('description') or metaLST['name']).decode("utf-8"))) plot = unescape(uni((metaLST.get('description','') or metaLST['name']).decode("utf-8"))) except: continue thumb = metaLST['thumbnailUrl'] aired = metaLST['uploadDate'] url = metaLST['url'] if not url.startswith('http://'): url = (BASE_URL + '%s'%url).lstrip('/') # seinfo = ('S' + ('0' if seasonNumber < 10 else '') + str(seasonNumber) + 'E' + ('0' if episodeNumber < 10 else '') + str(episodeNumber)) # label = '%s'%(label) if seasonNumber + episodeNumber == 0 else '%s - %s'%(label, seinfo) infoLabels ={"mediatype":"episodes","label":label ,"title":label,"TVShowTitle":label,"plot":plot,"aired":aired} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, url, 9, infoLabels, infoArt, len(items)) def browseEpisodes(self, url): log('browseEpisodes') items = json.loads(self.openURL(url))['result']['data'] for item in items: if 'status' in item and item['status'].lower() != 'available': continue if 'is_protected' in item and item['is_protected']: continue if 'is_paid_content' in item and item['is_paid_content']: continue title = uni(item['title'] or item['label'] or item['episode_title']) thumb = (item['thumb']['large'] or item['thumb']['small'] or ICON) aired = str(item['airdate_iso']).split('T')[0] showTitle = uni(item['series_title']) plot = uni(item['description']) runtime = item['duration'].split(':') if len(runtime) == 3: h, m, s = runtime duration = int(h) * 3600 + int(m) * 60 + int(s) else: h, m = runtime duration = int(h) * 3600 + int(m) * 60 seasonNumber = int(item.get('season_number','0') or '0') episodeNumber = int(item.get('episode_number','0') or '0') url = item['url'] if not url.startswith('http://'): url = (BASE_URL + '%s'%url).lstrip('/') seinfo = ('S' + ('0' if seasonNumber < 10 else '') + str(seasonNumber) + 'E' + ('0' if episodeNumber < 10 else '') + str(episodeNumber)) label = '%s - %s'%(showTitle, title) if seasonNumber + episodeNumber == 0 else '%s - %s - %s'%(showTitle, seinfo, title) infoLabels ={"mediatype":"episodes","label":label ,"title":label,"TVShowTitle":showTitle,"plot":plot,"aired":aired,"duration":duration,"season":seasonNumber,"episode":episodeNumber} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addLink(label, url, 9, infoLabels, infoArt, len(items)) def browseSeasons(self, url): log('browseSeasons') items = json.loads(url) url = items['url'] thumb = items['thumb'] seasons = eval(items['seasons']) if len(seasons) == 0: return for item in seasons: if item["total_count"] == item["premiumCount"]: continue title = uni(item["title"]) url = '%s/%s/' %(url,item["season"]) try: items = json.loads(self.openURL(url)) except: items = '' if 'success' in items: infoLabels ={"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,5,infoLabels,infoArt) def browseCategory(self, url): log('browseCategory') items = json.loads(url) url = items['url'] thumb = items['thumb'] response = self.openURL(url).replace('\n','').replace('\r','').replace('\t','') items = re.search('(?:video\.section_ids = |"section_ids"\:)\[([^\]]+)\]',response) if items: items = items.group(1).split(',') metas = json.loads(re.search('(?:video.section_metadata = |"section_metadata"\:)({.+?}})',response).group(1)) for item in items: url = SHOW_URL%item seasons = (metas.get(item,'').get('display_seasons','') or False) if seasons: title = uni(metas[item]['title']) seasonLST = '%s}]'%((re.search('video.seasons = {(.*)filter: (\S.+?);',response).group(2)).split('}]')[0]) url = json.dumps({'url':url,'seasons':seasonLST,'thumb':thumb}) infoLabels ={"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,4,infoLabels,infoArt) else: item = json.loads(self.openURL(url)) if item and 'success' in item: title = uni(item['result']['title']) infoLabels ={"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,5,infoLabels,infoArt) def browseShows(self, url=None): log('browseShows') soup = BeautifulSoup(self.openURL(SHOWS_URL), "html.parser") item = soup('ul', {'class': 'shows-list'})[0] isFreeLST = item('div', {'class': 'tune-info'}) titlesLST = item('div', {'class': 'title hide'}) thumbLST = item('img', {'class': 'poster-thumb'}) uriLST = item('a', {'class': 'link-show-thumb-text'}) for idx, show in enumerate(titlesLST): isFree = 'all access' not in isFreeLST[idx].get_text().lower() if not isFree: continue title = uni(show.get_text()) thumb = thumbLST[idx]['src'] url = uriLST[idx]['href'] if not url.startswith('http://'): url = (BASE_URL + url).lstrip('/') if not url.endswith('/video/'): url = '%s/video/'%url.rstrip('/') url = json.dumps({'url':url,'thumb':thumb}) infoLabels ={"mediatype":"tvshows","label":title ,"title":title,"TVShowTitle":title} infoArt ={"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":ICON,"logo":ICON} self.addDir(title,url,3,infoLabels,infoArt) def playVideo(self, name, url, liz=None): log('playVideo') info = getVideoInfo(url,QUALITY,True) if info is None: return info = info.streams() url = info[0]['xbmc_url'] liz = xbmcgui.ListItem(name, path=url) if 'subtitles' in info[0]['ytdl_format']: liz.setSubtitles([x['url'] for x in info[0]['ytdl_format']['subtitles'].get('en','') if 'url' in x]) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name,"genre":"News"}) 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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name,"genre":"News"}) 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)
class Mubi(object): _URL_MUBI = "https://mubi.com" _mubi_urls = { "login": urljoin(_URL_MUBI, "services/android/sessions"), "films": urljoin(_URL_MUBI, "services/android/films"), "film": urljoin(_URL_MUBI, "services/android/films/%s"), "viewing": urljoin(_URL_MUBI, "services/android/viewings/%s/secure_url"), "startup": urljoin(_URL_MUBI, "services/android/app_startup") } def __init__(self, username, password): self._username = username self._password = password self._cache_id = "plugin.video.mubi.filminfo.%s" self._simplecache = SimpleCache() # Need a 20 digit id, hash username to make it predictable self._udid = int(hashlib.sha1(username).hexdigest(), 32) % (10 ** 20) self._token = None self._country = None self.login() def login(self): payload = {'udid': self._udid, 'token': '', 'client': 'android', 'client_version': '3.05', 'email': self._username, 'password': self._password} xbmc.log("Logging in with username: %s and udid: %s" % (self._username, self._udid), 2) r = requests.post(self._mubi_urls["login"] + "?client=android", data=payload) if r.status_code == 200: self._token = json.loads(r.text)['token'] xbmc.log("Login Successful and got token %s" % self._token, 2) else: xbmc.log("Login Failed", 4) self.app_startup() return r.status_code def app_startup(self): payload = {'udid': self._udid, 'token': self._token, 'client': 'android', 'client_version': '3.05'} r = requests.post(self._mubi_urls['startup'] + "?client=android", data=payload) if r.status_code == 200: self._country = json.loads(r.text)['country'] xbmc.log("Successfully got country as %s" % self._country, 2) else: xbmc.log("Failed to get country: %s" % r.text, 4) return def get_film_page(self, film_id): cached = self._simplecache.get(self._cache_id % film_id) if cached: return json.loads(cached) args = "?client=android&country=%s&token=%s&udid=%s&client_version=3.05" % (self._country, self._token, self._udid) r = requests.get((self._mubi_urls['film'] % str(film_id)) + args) if r.status_code != 200: xbmc.log("Invalid status code %s getting film info for %s" % (r.status_code, film_id), 4) self._simplecache.set(self._cache_id % film_id, r.text, expiration=datetime.timedelta(days=32)) return json.loads(r.text) def get_film_metadata(self, film_overview): film_id = film_overview['id'] available_at = dateutil.parser.parse(film_overview['available_at']) expires_at = dateutil.parser.parse(film_overview['expires_at']) # Check film is valid, has not expired and is not preview now = datetime.datetime.now(available_at.tzinfo) if available_at > now: xbmc.log("Film %s is not yet available" % film_id, 2) return None elif expires_at < now: xbmc.log("Film %s has expired" % film_id, 2) return None hd = film_overview['hd'] drm = film_overview['default_reel']['drm'] audio_lang = film_overview['default_reel']['audio_language'] subtitle_lang = film_overview['default_reel']['subtitle_language'] # Build plot field. Place lang info in here since there is nowhere else for it to go drm_string = "Warning: this film cannot be played since it uses DRM\n" if drm else "" lang_string = ("Language: %s" % audio_lang) + ((", Subtitles: %s\n" % subtitle_lang) if subtitle_lang else "\n") plot_string = "Synopsis: %s\n\nOur take: %s" % (film_overview['excerpt'], film_overview['editorial']) # Get detailed look at film to get cast info film_page = self.get_film_page(film_id) cast = [(m['name'], m['credits']) for m in film_page['cast']] # Build film metadata object metadata = Metadata( title=film_overview['title'], director=film_overview['directors'], year=film_overview['year'], duration=film_overview['duration'] * 60, # This is in seconds country=film_overview['country'], plot=drm_string + lang_string + plot_string, overlay=6 if hd else 0, genre=', '.join(film_overview['genres']), originaltitle=film_overview['original_title'], rating=film_overview['average_rating'] * 2, # Out of 5, kodi uses 10 votes=film_overview['number_of_ratings'], castandrole=cast, trailer=film_overview['trailer_url'] ) listview_title = film_overview['title'] + (" [HD]" if hd else "") return Film(listview_title, film_id, film_overview['stills']['standard'], metadata) def get_now_showing_json(self): # Get list of available films args = "?client=android&country=%s&token=%s&udid=%s&client_version=3.05" % (self._country, self._token, self._udid) r = requests.get(self._mubi_urls['films'] + args) if r.status_code != 200: xbmc.log("Invalid status code %s getting list of films", 4) return r.text def now_showing(self): films = [self.get_film_metadata(film) for film in json.loads(self.get_now_showing_json())] return [f for f in films if f] def get_default_reel_id_is_drm(self, film_id): reel_id = [(f['default_reel']['id'], f['default_reel']['drm']) for f in json.loads(self.get_now_showing_json()) if str(f['id']) == str(film_id)] if len(reel_id) == 1: return reel_id[0] elif reel_id: xbmc.log("Multiple default_reel's returned for film %s: %s" % (film_id, ', '.join(reel_id)), 3) return reel_id[0] else: xbmc.log("Could not find default reel id for film %s" % film_id, 4) return None def get_play_url(self, film_id): (reel_id, is_drm) = self.get_default_reel_id_is_drm(film_id) args = "?client=android&country=%s&token=%s&udid=%s&client_version=3.05&film_id=%s&reel_id=%s&download=false" \ % (self._country, self._token, self._udid, film_id, reel_id) r = requests.get((self._mubi_urls['viewing'] % str(film_id)) + args) if r.status_code != 200: xbmc.log("Could not get secure URL for film %s" % film_id, 4) url = json.loads(r.text)["url"] # For DRM you will have to find the following info: # {"userId": long(result['username']), "sessionId": result['transaction'], "merchant": result['accountCode']} # This might need optdata in header however looking in requests during browser negotiation I don't see it # https://stackoverflow.com/questions/35792897/http-request-header-field-optdata # The best conversation for this is: # https://github.com/emilsvennesson/kodi-viaplay/issues/9 # You can pick this conversation up using Android Packet Capture item_result = {'url': url, 'is_mpd': "mpd" in url, 'is_drm': is_drm} xbmc.log("Got video info as: '%s'" % json.dumps(item_result), 2) return item_result
class Newsy(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponse: request = urllib2.Request(url) request.add_header( 'User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' ) response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, response, expiration=datetime.timedelta(hours=6)) return self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) except urllib2.URLError as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except socket.timeout as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMainMenu(self): self.addLink('Live', '', 0) self.addDir('Browse', '0', 1) def buildLiveLink(self): soup = BeautifulSoup(self.openURL(LIVEURL), "html.parser") link = 'http:' + soup( 'div', {'class': 'live-player'})[0].find('script').get('src') item = json.loads('{' + self.openURL(link).split('}; // end config') [0].split('var jwConfig = {')[1] + '}') self.playVideo('Newsy Live', item['playlist'][0]['sources'][0]['file']) def pagination(self, seq, rowlen): for start in xrange(0, len(seq), rowlen): yield seq[start:start + rowlen] def buildRSS(self, start=0, end=9): data = feedparser.parse(RSSURL)['entries'] data = list(self.pagination(data, end)) start = 0 if start >= len(data) else start for item in data[start]: if item and 'summary_detail' in item: try: path, duration, plot = self.resolveURL( item['links'][0]['href']) thumb = item['summary_detail']['value'].encode( "utf-8").split('<img alt="" src="')[1].split( '.jpg')[0] + '.jpg' label = item['title'] date = item['published'] plot = '%s - %s' % (date, plot) aired = (datetime.datetime.strptime( date[0:16], '%a, %d %b %Y')).strftime('%Y-%m-%d') infoLabel = { "mediatype": "video", "label": label, "title": label, "plot": plot, "plotoutline": plot, "genre": "News", "duration": duration, "aired": aired } infoArt = { "thumb": thumb, "poster": thumb, "icon": ICON, "fanart": FANART } self.addLink(label, path, 9, infoLabel, infoArt, end) except: log("buildRSS, no video found") start += 1 self.addDir('>> Next', '%s' % (start), 1) def resolveURL(self, url): log('resolveURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.resolveURL, url = %s' % url) if not cacheResponse: soup = BeautifulSoup(self.openURL(url), "html.parser") link = soup('div', {'class': 'video-container'})[0] item = json.loads( link('div', {'class': 'video-container' })[0].get('data-video-player')) path = item[ 'file'] if VID_TYPE == 'MP4' else 'http:' + item['stream'] rep = path, item['duration'] // 1000, item['headline'] self.cache.set(ADDON_NAME + '.resolveURL, url = %s' % url, rep, expiration=datetime.timedelta(hours=48)) return self.cache.get(ADDON_NAME + '.resolveURL, url = %s' % url) except Exception as e: log("resolveURL Failed! " + str(e), xbmc.LOGERROR) def playVideo(self, name, url, liz=None): log('playVideo') if not liz: liz = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "label": name, "title": name, "genre": "News" }) 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 = name.encode("utf-8") log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "label": name, "title": name, "genre": "News" }) 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)
class PluginContent: """Hidden plugin entry point providing some helper features""" params = {} win = None def __init__(self): self.cache = SimpleCache() self.kodi_db = KodiDb() self.win = xbmcgui.Window(10000) try: self.params = dict(urlparse.parse_qsl(sys.argv[2].replace("?", "").lower().decode("utf-8"))) log_msg("plugin called with parameters: %s" % self.params) self.main() except Exception as exc: log_exception(__name__, exc) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) # cleanup when done processing self.close() def close(self): """Cleanup Kodi Cpython instances""" self.cache.close() del self.win def main(self): """main action, load correct function""" action = self.params.get("action", "") if self.win.getProperty("SkinHelperShutdownRequested"): # do not proceed if kodi wants to exit log_msg("%s --> Not forfilling request: Kodi is exiting" % __name__, xbmc.LOGWARNING) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) else: try: if hasattr(self.__class__, action): # launch module for action provided by this plugin getattr(self, action)() else: # legacy (widget) path called !!! self.load_widget() except Exception as exc: log_exception(__name__, exc) def load_widget(self): """legacy entrypoint called (widgets are moved to seperate addon), start redirect...""" action = self.params.get("action", "") newaddon = "script.skin.helper.widgets" log_msg( "Deprecated method: %s. Please reassign your widgets to get rid of this message. -" "This automatic redirect will be removed in the future" % (action), xbmc.LOGWARNING, ) paramstring = "" for key, value in self.params.iteritems(): paramstring += ",%s=%s" % (key, value) if xbmc.getCondVisibility("System.HasAddon(%s)" % newaddon): # TEMP !!! for backwards compatability reasons only - to be removed in the near future!! import imp addon = xbmcaddon.Addon(newaddon) addon_path = addon.getAddonInfo("path").decode("utf-8") imp.load_source("plugin", os.path.join(addon_path, "plugin.py")) from plugin import main main.Main() del addon else: # trigger install of the addon if KODI_VERSION >= 17: xbmc.executebuiltin("InstallAddon(%s)" % newaddon) else: xbmc.executebuiltin("RunPlugin(plugin://%s)" % newaddon) def playchannel(self): """play channel from widget helper""" params = {"item": {"channelid": int(self.params["channelid"])}} self.kodi_db.set_json("Player.Open", params) def playrecording(self): """retrieve the recording and play to get resume working""" recording = self.kodi_db.recording(self.params["recordingid"]) params = {"item": {"recordingid": recording["recordingid"]}} self.kodi_db.set_json("Player.Open", params) # manually seek because passing resume to the player json cmd doesn't seem to work if recording["resume"].get("position"): for i in range(50): if xbmc.getCondVisibility("Player.HasVideo"): break xbmc.sleep(50) xbmc.Player().seekTime(recording["resume"].get("position")) def launch(self): """launch any builtin action using a plugin listitem""" if "runscript" in self.params["path"]: self.params["path"] = self.params["path"].replace("?", ",") xbmc.executebuiltin(self.params["path"]) def playalbum(self): """helper to play an entire album""" xbmc.executeJSONRPC( '{ "jsonrpc": "2.0", "method": "Player.Open", "params": { "item": { "albumid": %d } }, "id": 1 }' % int(self.params["albumid"]) ) def smartshortcuts(self): """called from skinshortcuts to retrieve listing of all smart shortcuts""" import skinshortcuts skinshortcuts.get_smartshortcuts(self.params.get("path", "")) @staticmethod def backgrounds(): """called from skinshortcuts to retrieve listing of all backgrounds""" import skinshortcuts skinshortcuts.get_backgrounds() def widgets(self): """called from skinshortcuts to retrieve listing of all widgetss""" import skinshortcuts skinshortcuts.get_widgets(self.params.get("path", ""), self.params.get("sublevel", "")) def resourceimages(self): """retrieve listing of specific resource addon images""" from resourceaddons import get_resourceimages addontype = self.params.get("addontype", "") for item in get_resourceimages(addontype, True): listitem = xbmcgui.ListItem(item[0], label2=item[2], path=item[1], iconImage=item[3]) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item[1], listitem=listitem, isFolder=False) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) def extrafanart(self): """helper to display extrafanart in multiimage control in the skin""" fanarts = eval(self.params["fanarts"]) # process extrafanarts for item in fanarts: listitem = xbmcgui.ListItem(item, path=item) listitem.setProperty("mimetype", "image/jpeg") xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item, listitem=listitem) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) def genrebackground(self): """helper to display images for a specific genre in multiimage control in the skin""" genre = self.params.get("genre").split(".")[0] arttype = self.params.get("arttype", "fanart") randomize = self.params.get("random", "false") == "true" mediatype = self.params.get("mediatype", "movies") if genre and genre != "..": filters = [{"operator": "is", "field": "genre", "value": genre}] if randomize: sort = {"method": "random", "order": "descending"} else: sort = None items = getattr(self.kodi_db, mediatype)(sort=sort, filters=filters, limits=(0, 50)) for item in items: image = get_clean_image(item["art"].get(arttype, "")) if image: image = get_clean_image(item["art"][arttype]) listitem = xbmcgui.ListItem(image, path=image) listitem.setProperty("mimetype", "image/jpeg") xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=image, listitem=listitem) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) def getcastmedia(self): """helper to display get all media for a specific actor""" name = self.params.get("name") if name: all_items = self.kodi_db.castmedia(name) all_items = process_method_on_list(self.kodi_db.prepare_listitem, all_items) all_items = process_method_on_list(self.kodi_db.create_listitem, all_items) xbmcplugin.addDirectoryItems(int(sys.argv[1]), all_items, len(all_items)) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) def getcast(self): """helper to get all cast for a given media item""" db_id = None all_cast = [] all_cast_names = list() cache_str = "" download_thumbs = self.params.get("downloadthumbs", "") == "true" extended_cast_action = self.params.get("castaction", "") == "extendedinfo" tmdb = Tmdb() movie = self.params.get("movie") tvshow = self.params.get("tvshow") episode = self.params.get("episode") movieset = self.params.get("movieset") try: # try to parse db_id if movieset: cache_str = "movieset.castcache-%s-%s" % (self.params["movieset"], download_thumbs) db_id = int(movieset) elif tvshow: cache_str = "tvshow.castcache-%s-%s" % (self.params["tvshow"], download_thumbs) db_id = int(tvshow) elif movie: cache_str = "movie.castcache-%s-%s" % (self.params["movie"], download_thumbs) db_id = int(movie) elif episode: cache_str = "episode.castcache-%s-%s" % (self.params["episode"], download_thumbs) db_id = int(episode) except Exception: pass cachedata = self.cache.get(cache_str) if cachedata: # get data from cache all_cast = cachedata else: # retrieve data from json api... if movie and db_id: all_cast = self.kodi_db.movie(db_id)["cast"] elif movie and not db_id: filters = [{"operator": "is", "field": "title", "value": movie}] result = self.kodi_db.movies(filters=filters) all_cast = result[0]["cast"] if result else [] elif tvshow and db_id: all_cast = self.kodi_db.tvshow(db_id)["cast"] elif tvshow and not db_id: filters = [{"operator": "is", "field": "title", "value": tvshow}] result = self.kodi_db.tvshows(filters=filters) all_cast = result[0]["cast"] if result else [] elif episode and db_id: all_cast = self.kodi_db.episode(db_id)["cast"] elif episode and not db_id: filters = [{"operator": "is", "field": "title", "value": episode}] result = self.kodi_db.episodes(filters=filters) all_cast = result[0]["cast"] if result else [] elif movieset: if not db_id: for item in self.kodi_db.moviesets(): if item["title"].lower() == movieset.lower(): db_id = item["setid"] if db_id: json_result = self.kodi_db.movieset(db_id, include_set_movies_fields=["cast"]) if "movies" in json_result: for movie in json_result["movies"]: all_cast += movie["cast"] # optional: download missing actor thumbs if all_cast and download_thumbs: for cast in all_cast: if cast.get("thumbnail"): cast["thumbnail"] = get_clean_image(cast.get("thumbnail")) if not cast.get("thumbnail"): artwork = tmdb.get_actor(cast["name"]) cast["thumbnail"] = artwork.get("thumb", "") # lookup tmdb if item is requested that is not in local db if not all_cast: tmdbdetails = {} if movie and not db_id: tmdbdetails = tmdb.search_movie(movie) elif tvshow and not db_id: tmdbdetails = tmdb.search_tvshow(tvshow) if tmdbdetails.get("cast"): all_cast = tmdbdetails["cast"] # save to cache self.cache.set(cache_str, all_cast) # process listing with the results... for cast in all_cast: if cast.get("name") not in all_cast_names: liz = xbmcgui.ListItem(label=cast.get("name"), label2=cast.get("role"), iconImage=cast.get("thumbnail")) if extended_cast_action: url = "RunScript(script.extendedinfo,info=extendedactorinfo,name=%s)" % cast.get("name") url = "plugin://script.skin.helper.service/?action=launch&path=%s" % url is_folder = False else: url = "RunScript(script.skin.helper.service,action=getcastmedia,name=%s)" % cast.get("name") url = "plugin://script.skin.helper.service/?action=launch&path=%s" % urlencode(url) is_folder = False all_cast_names.append(cast.get("name")) liz.setThumbnailImage(cast.get("thumbnail")) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=liz, isFolder=is_folder) xbmcplugin.endOfDirectory(int(sys.argv[1])) @staticmethod def alphabet(): """display an alphabet scrollbar in listings""" all_letters = [] if xbmc.getInfoLabel("Container.NumItems"): for i in range(int(xbmc.getInfoLabel("Container.NumItems"))): all_letters.append(xbmc.getInfoLabel("Listitem(%s).SortLetter" % i).upper()) start_number = "" for number in ["2", "3", "4", "5", "6", "7", "8", "9"]: if number in all_letters: start_number = number break for letter in [ start_number, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ]: if letter == start_number: label = "#" else: label = letter listitem = xbmcgui.ListItem(label=label) if letter not in all_letters: lipath = "noop" listitem.setProperty("NotAvailable", "true") else: lipath = "plugin://script.skin.helper.service/?action=alphabetletter&letter=%s" % letter xbmcplugin.addDirectoryItem(int(sys.argv[1]), lipath, listitem, isFolder=False) xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) def alphabetletter(self): """used with the alphabet scrollbar to jump to a letter""" if KODI_VERSION > 16: xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=False, listitem=xbmcgui.ListItem()) letter = self.params.get("letter", "").upper() jumpcmd = "" if letter in ["A", "B", "C", "2"]: jumpcmd = "2" elif letter in ["D", "E", "F", "3"]: jumpcmd = "3" elif letter in ["G", "H", "I", "4"]: jumpcmd = "4" elif letter in ["J", "K", "L", "5"]: jumpcmd = "5" elif letter in ["M", "N", "O", "6"]: jumpcmd = "6" elif letter in ["P", "Q", "R", "S", "7"]: jumpcmd = "7" elif letter in ["T", "U", "V", "8"]: jumpcmd = "8" elif letter in ["W", "X", "Y", "Z", "9"]: jumpcmd = "9" if jumpcmd: xbmc.executebuiltin("SetFocus(50)") for i in range(40): xbmc.executeJSONRPC( '{ "jsonrpc": "2.0", "method": "Input.ExecuteAction",\ "params": { "action": "jumpsms%s" }, "id": 1 }' % (jumpcmd) ) xbmc.sleep(50) if xbmc.getInfoLabel("ListItem.Sortletter").upper() == letter: break
class SlingTV(object): def __init__(self, sysARG): log('__init__') self.sysARG = sysARG self.cache = SimpleCache() self.endPoints = self.buildEndpoints() self.user = UserClient() self.contentType = 'episodes' def getURL(self, url, header=HEADERS, life=datetime.timedelta(minutes=5), auth=None, msg=True): try: log('getURL, url = ' + url + " | Auth= " + str(auth)) cacheresponse = self.cache.get(ADDON_NAME + '.getURL, url = %s' % url) if not cacheresponse: if auth is not None: response = requests.get(url, headers=header, auth=auth, verify=VERIFY) else: response = requests.get(url, headers=header, verify=VERIFY) if response.status_code == 403: response = requests.get(url, headers=header, auth=self.user.getAuth(), verify=VERIFY) if response.status_code == 200: cacheresponse = response.json() if 'message' in cacheresponse: return self.cache.set(ADDON_NAME + '.getURL, url = %s'%url, dumpJSON(cacheresponse), expiration=life) if 'message' in response.json() and msg: notificationDialog(response.json()['message']) if isinstance(cacheresponse, basestring): cacheresponse = loadJSON(cacheresponse) return cacheresponse except Exception as e: log("getURL Failed! " + str(e), xbmc.LOGERROR) notificationDialog(LANGUAGE(30001)) return {} def buildMenu(self): response = self.getURL(MAIN_URL) self.addDir(LANGUAGE(30015), '', 'live') # Channels self.addDir(LANGUAGE(30017), '', 'vod') # On Demand for item in response: # Omit Rentals for the time being if item['title'].lower() == 'rentals': continue self.addDir(item['title'].title(), (item.get('url') or item['id']), item['id'], False, {'thumb': item.get('thumbnail', {}).get('url', ICON)}) def buildOnNow(self, context): success, region = self.user.getRegionInfo() if not success: notificationDialog(LANGUAGE(30016)) return USER_DMA = region['USER_DMA'] USER_OFFSET = region['USER_OFFSET'] if USER_SUBS == '' or USER_DMA == '' or USER_OFFSET == '': return dashboardUrl = "https://p-mgcs.movetv.com/rubens-online/rest/v1/dma/{}/offset/{}/domain/1/product/sling/platform/browser/context/{}/ribbons?sub_pack_ids={}" dashboardUrl = dashboardUrl.format(USER_DMA, USER_OFFSET, context, USER_SUBS) log('buildOnNow, Dashboard URL = ' + dashboardUrl) response = self.getURL(dashboardUrl) if response: ribbons = response['ribbons'] for ribbon in ribbons: ribbonUrl = ribbon['_href'] response = self.getURL(ribbonUrl) if response: tile_count = response['total_tiles'] if tile_count > 0: log('buildOnNow, ribbon = ' + str(ribbon['title'].encode('utf-8'))) self.addDir(ribbon['title'], ribbonUrl, 'on_now_item') def buildOnNowItems(self, name, ribbonUrl): ribbonName = urllib.quote(name) log('getRibbonChannels, ribbon = ' + name) log('getRibbonChannels, ribbonUrl = ' + ribbonUrl) response = self.getURL(ribbonUrl) if response: tiles = response['tiles'] for tile in tiles: log('getRibbonChannels, ribbon = ' + tile['title'].encode("utf-8")) infoLabels, infoArt = self.setAssetInfo(tile) log(str(tile)) start_time = None if 'start_time' in tile: start_time = stringToDate(tile['start_time'], '%Y-%m-%dT%H:%M:%SZ') qvt_url = tile['qvt'] elif tile['availability_type'] == 'svod': jsonBlock = self.getURL(tile['_href']) for source in jsonBlock['entitlements']: start_time = stringToDate(source['playback_start'], '%Y-%m-%dT%H:%M:%SZ') stop_time = stringToDate(source['playback_stop'], '%Y-%m-%dT%H:%M:%SZ') qvt_url = source['qvt_url'] if start_time < datetime.datetime.utcnow() < stop_time: break if start_time is not None and start_time < datetime.datetime.utcnow(): self.addLink(infoLabels['label'], qvt_url, 'play', infoLabels, infoArt) elif start_time is not None: title = 'Upcoming - ' + utcToLocal(start_time).strftime('%m/%d %H:%M') + ' - ' + infoLabels['label'] self.addDir(title, '', 'no_play', infoLabels, infoArt) def buildMyChannels(self): url = '%s/watchlists/v4/watches?product=sling&platform=browser' % ( self.endPoints['environments']['production']['cmw_url']) myChannels = self.getURL(url, HEADERS, auth=self.user.getAuth())['favorites'] for fav in myChannels: #url = '%s/cms/publish3/channel/schedule/24/1810090400/1/%s.json' % ( #self.endPoints['environments']['production']['cms_url'], fav['guid']) url = fav['_href'] schedule = self.getURL(url) if 'schedule' in schedule: channel = schedule['schedule'] meta = channel['metadata'] label = (meta['channel_name'] or meta['title'] or meta['call_sign']) thumb = (channel.get('thumbnail', {}).get('url', '') or ICON) logo = (meta.get('thumbnail_cropped', {}).get('url', '') or ICON) fanart = (meta.get('default_schedule_image', {}).get('url', '') or FANART) infoLabels = {'label': label, 'title': label, 'genre': meta.get('genre', '')} infoArt = {'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'logo': logo, 'clearlogo': logo} self.addLink(label, channel['qvt_url'], 'play', infoLabels, infoArt) def buildMyFavs(self, mode): url = '%s/watchlists/v4/watches?product=sling&platform=browser' % ( self.endPoints['environments']['production']['cmw_url']) myFavs = self.getURL(url, HEADERS, auth=self.user.getAuth())['favorites'] for fav in myFavs: if mode == 'favorites': if 'title' in fav: label = fav['title'] thumb = fav['thumbnail']['url'] fanart = thumb logo = thumb #logo = item['network_thumbnail']['url'] infoLabels = {'label': label, 'title': label} infoArt = {'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'logo': logo, 'clearlogo': logo} self.addDir(fav['title'], fav['_href'], 'tv_show', infoLabels, infoArt) else: url = fav['_href'] try: channel = self.getURL(url) label = (channel['channel_name'] or channel['title'] or channel['call_sign']) logo = (channel.get('image', {}).get('url', '') or ICON) thumb = logo fanart = logo infoLabels = {'label': label, 'title': label, 'genre': channel.get('genre', '')} infoArt = {'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'logo': logo, 'clearlogo': logo} if channel['has_linear_schedule'] is False: xbmc.log('add dir') self.addDir(label, '%s/network' % channel['_href'], 'on_demand', infoLabels, infoArt) else: self.addLink(label, channel['qvt'], 'play', infoLabels, infoArt) except: continue def buildContinueWatching(self): url = '%s/resumes/v4/resumes.json?platform=browser&product=sling' % ( self.endPoints['environments']['production']['cmw_url']) resumes = self.getURL(url, HEADERS, auth=self.user.getAuth(), life=datetime.timedelta(minutes=1)) for show in resumes: jsonBlock = self.getURL('%s/cms/publish3/asset/info/%s.json' % ( self.endPoints['environments']['production']['cms_url'], show['external_id'])) if jsonBlock is None: continue qvt_url = None if len(jsonBlock['schedules']) > 0: qvt_url = jsonBlock['schedules'][0]['playback_info'] elif len(jsonBlock['entitlements']) > 0: qvt_url = jsonBlock['entitlements'][0]['qvt_url'] if qvt_url is not None: infoLabels, infoArt = self.setAssetInfo(jsonBlock) properties = {'totaltime': str(show['duration']), 'resumetime': str(show['position'])} self.addLink(infoLabels['label'], qvt_url, 'play', infoLabels, infoArt, 0, None, properties) def setAssetInfo(self, jsonBlock, meta=True): try: channelGuid = jsonBlock['channel']['guid'] except: channelGuid = '' asset_info = jsonBlock metadata = jsonBlock if meta and '_href' in jsonBlock: try: url = jsonBlock['_href'] asset_info = self.getURL(url, life=datetime.timedelta(days=7)) metadata = asset_info['metadata'] except Exception as e: log("setAssetInfo, extra meta Failed! " + str(e), xbmc.LOGERROR) # ------------------------------- # Info Labels # ------------------------------- if len(asset_info) == 0: asset_info = jsonBlock title = asset_info['title'].encode("utf-8") tv_show_title = '' if 'episode_title' in metadata: tv_show_title = title title = metadata['episode_title'] infoLabels = {'label': title, 'title': title} # ------------------------------- # Conditional Info # ------------------------------- if 'id' in asset_info: infoLabels['tracknumber'] = asset_info['id'] if 'year' in asset_info: infoLabels['year'] = asset_info['release_year'] if 'duration' in asset_info: infoLabels['duration'] = asset_info['duration'] if 'celebrities' in asset_info and 'director' in asset_info['celebrities']: infoLabels['director'] = asset_info['celebrities']['director'][0]['display_name'] elif 'director' in metadata: infoLabels['director'] = metadata['director'][0] if 'genre' in metadata: infoLabels['genre'] = metadata['genre'] if 'description' in metadata: infoLabels['plot'] = metadata['description'].encode("utf-8") if tv_show_title != '': infoLabels['tvshowtitle'] = tv_show_title type = (jsonBlock.get('type', '') or asset_info.get('type', '') or metadata.get('type', 'video')) if type == 'movie' or 'Film' in metadata.get('category', ''): infoLabels['mediatype'] = 'movie' elif 'episode_number' in metadata: if 'episode_season' in metadata: infoLabels['season'] = int(metadata['episode_season']) elif 'season_number' in metadata: infoLabels['season'] = int(metadata['season_number']) infoLabels['episode'] = int(metadata['episode_number']) infoLabels['mediatype'] = 'episode' elif 'type' in metadata: if metadata['type'] == 'series': infoLabels['mediatype'] = 'tvshow' else: infoLabels['mediatype'] = 'video' if 'ratings' in metadata: mpaa = metadata['ratings'][0] mpaa = mpaa.replace('US_UPR_', '') mpaa = mpaa.replace('US_MPAA_', '') infoLabels['mpaa'] = mpaa if 'celebrities' in asset_info and 'cast' in asset_info['celebrities']: infoLabels['cast'] = [person['display_name'] for person in asset_info['celebrities']['cast']] # ------------------------------- # Info Art # ------------------------------- thumb = ICON if 'program' in asset_info and 'thumbnail' in asset_info['program'] and asset_info['program'][ 'thumbnail'] is not None: thumb = asset_info['program']['thumbnail']['url'] elif 'thumbnail' in asset_info and asset_info['thumbnail'] is not None: thumb = asset_info['thumbnail']['url'] fanart = FANART if 'program' in asset_info: if 'background_image' in asset_info['program'] and asset_info['program']['background_image'] is not None: fanart = asset_info['program']['background_image']['url'] elif 'thumbnail' in asset_info and asset_info['thumbnail'] is not None: fanart = asset_info['thumbnail']['url'] try: channel_logo = jsonBlock['channel']['image']['url'] except: channel_logo = '' if channelGuid != '' and channel_logo == '' and 'schedules' in asset_info: for logo in asset_info['schedules']: if logo['channel_guid'] == channelGuid: channel_logo = logo['channel_image']['url'] break if channel_logo != '' and thumb == ICON: thumb = channel_logo if channel_logo != '' and fanart == FANART: fanart = channel_logo infoArt = { 'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'logo': channel_logo, 'clearlogo': channel_logo } return infoLabels, infoArt # @use_cache(1) def buildEndpoints(self): return (self.getURL(WEB_ENDPOINTS)) # @use_cache(1) def getChannels(self): log('getChannels') success, region = self.user.getRegionInfo() if not success: notificationDialog(LANGUAGE(30016)) return USER_DMA = region['USER_DMA'] USER_OFFSET = region['USER_OFFSET'] channelURL = '%s/cms/publish3/domain/channels/v4/%s/%s/%s/1.json' % \ (self.endPoints['environments']['production']['cms_url'], USER_OFFSET, USER_DMA, base64.b64encode(LEGACY_SUBS.replace('+', ','))) subpacks = self.getURL(channelURL)['subscriptionpacks'] for subpack in subpacks: if 'channels' in subpack: for channel in subpack['channels']: yield dict(channel) def buildLive(self, channel_type): log('buildLive') channels = self.getChannels() for channel in channels: log(str(channel)) meta = channel['metadata'] label = (meta.get('channel_name', '') or meta.get('title', '') or meta.get('call_sign', '')) thumb = (channel.get('thumbnail', {}).get('url', '') or ICON) logo = (meta.get('thumbnail_cropped', {}).get('url', '') or ICON) fanart = (meta.get('default_schedule_image', {}).get('url', '') or FANART) infoLabels = {'label': label, 'title': label, 'genre': meta.get('genre', '')} infoArt = {'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'logo': logo, 'clearlogo': logo} on_demand = False on_demand_url = '%s/cms/api/channels/%s/network' % \ (self.endPoints['environments']['production']['cms_url'], channel['channel_guid']) response = self.getURL(on_demand_url, msg=False) # log('CHANNELRESPONSE => ' +str(response)) if response is not None and len(response): for category in response: if len(category['tiles']) > 0: on_demand = True break response = self.getURL(channel['qvt_url'], msg=False) if response is not None: if 'playback_info' in response: if 'asset' in response['playback_info']: if 'episode_title' in response['playback_info']['asset']: if response['playback_info']['asset']['title'] != response['playback_info']['asset']['episode_title']: infoLabels['plot'] = ('%s - %s' % (response['playback_info']['asset']['title'], response['playback_info']['asset']['episode_title'])) else: infoLabels['plot'] = response['playback_info']['asset']['title'] if channel_type == 'vod' and on_demand: self.addDir(label, on_demand_url, 'on_demand', infoLabels, infoArt) else: self.addLink(label, channel['qvt_url'], 'play', infoLabels, infoArt) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_LABEL) def buildOnDemand(self, url): items = (self.getURL(url)) if 'ribbon=' not in url and items is not None: for item in items: self.addDir(item['title'], item['_href'], 'on_demand', False, False) else: tiles = (self.getURL(url))['tiles'] for item in tiles: if item['type'] == 'series': label = item['title'] thumb = item['thumbnail']['url'] fanart = thumb logo = item['network_thumbnail']['url'] infoLabels = {'label': label, 'title': label} infoArt = {'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'logo': logo, 'clearlogo': logo} self.addDir(item['title'], item['_href'], 'tv_show', infoLabels, infoArt) else: jsonBlock = self.getURL('%s/cms/publish3/asset/info/%s.json' % ( self.endPoints['environments']['production']['cms_url'], item['external_id'])) if jsonBlock is None: continue start_time = '' try: for source in jsonBlock['entitlements']: start_time = stringToDate(source['playback_start'], '%Y-%m-%dT%H:%M:%SZ') stop_time = stringToDate(source['playback_stop'], '%Y-%m-%dT%H:%M:%SZ') qvt_url = source['qvt_url'] if start_time < datetime.datetime.utcnow() < stop_time: break except: continue infoLabels, infoArt = self.setAssetInfo(jsonBlock) if start_time != '': if start_time < datetime.datetime.utcnow(): self.addLink(infoLabels['label'], qvt_url, 'play', infoLabels, infoArt) else: title = 'Upcoming - ' + utcToLocal(start_time).strftime('%m/%d %H:%M') \ + ' - ' + infoLabels['label'] self.addDir(title, '', 'no_play', infoLabels, infoArt) if '_next' in items.keys(): self.buildOnDemand(items['_next']) def buildMyTV(self, name, item=None): log('buildMyTV, name = %s' % (name)) if item is None: items = (self.getURL(MYTV))['ribbons'] for item in items: item['page'] = 0 if '_href' not in item: self.addDir(item['title'], dumpJSON(item), 'my_tv_item', False, {'thumb': '%s/config/shared/images/mytv-icon.png' % (BASE_WEB)}) else: url = item['_href'].replace('{{', '{').replace('}}', '}').replace(' ', '%20').replace('my_tv_tvod', 'my_tv') url = url.format(dma=USER_DMA, timezone_offset=USER_OFFSET, domain='1', product='sling', platform='browser', subscription_pack_ids=USER_SUBS, legacy_subscription_pack_ids=LEGACY_SUBS, page_size='large') # Check if href is empty so we don't display an empty listitem, cache this for a day or two? response = self.getURL(url, life=datetime.timedelta(days=1)) if response: if response['total_tiles'] > 0: self.addDir(response['title'], dumpJSON(item), 'my_tv_item', False, {'thumb': '%s/config/shared/images/mytv-icon.png' % (BASE_WEB)}) else: page = item['page'] if 'my channels' == item['title'].lower() or 'favorites' == item['title'].lower(): #self.buildMyChannels() self.buildMyFavs(item['title'].lower()) elif 'continue watching' == item['title'].lower(): self.buildContinueWatching() elif '_href' in item: url = item['_href'].replace('{{', '{').replace('}}', '}').replace('page=0', 'page=%d' % (page)).replace( ' ', '%20').replace('my_tv_tvod', 'my_tv') url = url.format(dma=USER_DMA, timezone_offset=USER_OFFSET, domain='1', product='sling', platform='browser', subscription_pack_ids=USER_SUBS, legacy_subscription_pack_ids=LEGACY_SUBS, page_size='large') log(url) items = (self.getURL(url)) if 'movies' in item['title'].lower(): movies = items['tiles'] for movie in movies: infoLabels, infoArt = self.setAssetInfo(movie) self.addLink(infoLabels['label'], movie['qvt'], 'play', infoLabels, infoArt) if 'shows' in item['title'].lower(): shows = items['tiles'] for show in shows: infoLabels, infoArt = self.setAssetInfo(show, meta=False) self.addDir(infoLabels['label'], show['_href'], 'tv_show', infoLabels, infoArt) elif items is None: self.addDir(LANGUAGE(30014), '', '') elif items and len(items.get('tiles', [])) == 0: self.addDir(LANGUAGE(30014), '', '') else: shows = items['tiles'] for show in shows: infoLabels, infoArt = self.setAssetInfo(show) self.addLink(infoLabels['label'], show['qvt'], 'play', infoLabels, infoArt) def buildShow(self, name, url=None): log('buildShow, name = %s' % (name)) item = (self.getURL(url, auth=self.user.getAuth())) seasons = item.get('seasons', []) if seasons: self.buildSeasons(seasons) def buildSeasons(self, seasons): for season in seasons: programs = season['programs'] self.buildPrograms(programs) def buildPrograms(self, programs): for program in programs: try: next_airing_time = None for airing in program['airings']: start_time = stringToDate(airing['availability'][0]['start'], '%Y-%m-%dT%H:%M:%SZ') stop_time = stringToDate(airing['availability'][0]['stop'], '%Y-%m-%dT%H:%M:%SZ') if start_time < datetime.datetime.utcnow() < stop_time: next_airing_time = None link = airing['availability'][0]['qvt'] # todo add stream select? break elif start_time > datetime.datetime.utcnow(): if next_airing_time is None or next_airing_time > start_time: next_airing_time = start_time except: continue if ( start_time > datetime.datetime.utcnow() or datetime.datetime.utcnow() > stop_time) and next_airing_time is None: continue title = program['name'] infoLabels, infoArt = self.setAssetInfo(airing) if next_airing_time is None: self.addLink(title, link, 'play', infoLabels, infoArt, len(programs)) else: title = 'Next Airing - ' + utcToLocal(next_airing_time).strftime('%m/%d %H:%M') + ' - ' + title self.addDir(title, '', 'no_play', infoLabels, infoArt) def search(self, result_url=None): if result_url is None: keyword = inputDialog('Shows, Movies...') if keyword is not None: headers = { 'Origin': 'https://watch.sling.com', 'User-Agent': USER_AGENT, 'Accept': '*/*', 'Referer': 'https://watch.sling.com/browse/dynamic/on-now', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'en-US,en;q=0.9' } tv_results = self.getURL('%s/cms/api/search/v2/franchises/keyword=%s;subpacks=%s;timezone=%s;dma=%s' % (self.endPoints['environments']['production']['cms_url'], urllib.quote_plus(keyword), base64.b64encode(sortGroup(LEGACY_SUBS.replace('+', ','))), USER_OFFSET, USER_DMA), header=headers) for item in tv_results['franchises']: label = item['title'] thumb = item['image']['url'] fanart = thumb infoLabels = {'label': label, 'title': label} infoArt = {'thumb': thumb, 'poster': thumb, 'fanart': fanart, } self.addDir(item['title'], item['_href'], 'search_result', infoLabels, infoArt) else: result = self.getURL(result_url) if result: if 'seasons' in result: self.buildSeasons(result['seasons']) elif 'programs' in result: self.buildPrograms(result['programs']) else: sys.exit() def resolverURL(self, url): log('resolverURL, url = ' + url) video = (self.getURL(url)) if video is None or 'message' in video: return if 'playback_info' not in video: sys.exit() mpd_url = video['playback_info']['dash_manifest_url'] qmx_url = video['playback_info']['clips'][0]['location'] if 'UNKNOWN_' not in mpd_url: qmx = (self.getURL(qmx_url)) if 'message' in qmx: return lic_url = '' if 'encryption' in qmx: lic_url = qmx['encryption']['providers']['widevine']['proxy_url'] log('resolverURL, lic_url = ' + lic_url) if 'channel_guid' in video: channel_id = video['channel_guid'] if 'playback' in video and 'linear_info' in video['playback_info']: channel_id = video['playback_info']['linear_info']['channel_guid'] elif 'playback' in video and 'asset' in video['playback_info']: channel_id = video['playback_info']['asset']['guid'] elif 'playback_info' in video and 'vod_info' in video['playback_info']: try: channel_id = video['playback_info']['vod_info']['svod_channels'][0] except: channel_id = '' else: channel_id = '' license_key = '' if lic_url != '': license_key = '%s||{"env":"production","user_id":"%s","channel_id":"%s","message":[D{SSM}]}|' % ( lic_url, self.user.getUserID(), channel_id) log('license_key = ' + license_key) else: if 'vod_info' in video['playback_info']: fod_url = video['playback_info']['vod_info'].get('media_url', '') response = requests.get(fod_url, headers=HEADERS, verify=VERIFY) if response.status_code == 200: mpd_url = response.json()['stream'] license_key = '' elif 'message' in response.json(): notificationDialog(response.json()['message']) elif 'linear_info' in video['playback_info'] \ and 'disney_stream_service_url' in video['playback_info']['linear_info']: log('resolverURL, Inside Disney/ABC') utc_datetime = str(time.mktime(datetime.datetime.utcnow().timetuple())).split('.')[0] sha1_user_id = hashlib.sha1(SUBSCRIBER_ID).hexdigest() rsa_sign_url = '%s/cmw/v1/rsa/sign' % self.endPoints['environments']['production']['cmw_url'] stream_headers = HEADERS stream_headers['Content-Type'] = 'application/x-www-form-urlencoded' payload = 'document=%s_%s_' % (sha1_user_id, utc_datetime) log('resolverURL, RSA payload => %s' % payload) response = requests.post(rsa_sign_url, headers=stream_headers, data=payload, verify=VERIFY) if response.status_code == 200 and 'signature' in response.json(): signature = response.json()['signature'] log('resolverURL, RSA Signature => %s' % signature) disney_info = video['playback_info']['linear_info'] if 'abc' in disney_info['disney_network_code']: brand = '003' else: brand = disney_info['disney_brand_code'] params = { 'ak': 'fveequ3ecb9n7abp66euyc48', 'brand': brand, 'device': '001_14', 'locale': disney_info.get('disney_locale', ''), 'token': '%s_%s_%s' % (sha1_user_id, utc_datetime, signature), 'token_type': 'offsite_dish_ott', 'user_id': sha1_user_id, 'video_type': 'live', 'zip_code': USER_ZIP } service_url = disney_info['disney_stream_service_url'] log('service url %s' % service_url) payload = '' for key in params.keys(): payload += '%s=%s&' % (key, params[key]) payload = payload[:-1] response = requests.post(service_url, headers=stream_headers, data=payload, verify=VERIFY) if response.status_code == 200: log(str(response.text)) session_xml = xmltodict.parse(response.text) service_stream = session_xml['playmanifest']['channel']['assets']['asset']['#text'] log('resolverURL, XML Stream: ' + str(service_stream)) mpd_url = service_stream license_key = '' asset_id = '' if 'entitlement' in video and 'asset_id' in video['entitlement']: asset_id = video['entitlement']['asset_id'] return mpd_url, license_key, asset_id def playVideo(self, name, url, liz=None): log('playVideo, url = ' + url) try: url, license_key, external_id = self.resolverURL(url) except: license_key = '' external_id = '' if 'mpd' in url: is_helper = inputstreamhelper.Helper('mpd', drm='widevine') if not is_helper.check_inputstream(): sys.exit() liz = xbmcgui.ListItem(name, path=url) liz.setProperty('inputstreamaddon', 'inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type', 'mpd') liz.setProperty('inputstream.adaptive.stream_headers', 'User-Agent=' + USER_AGENT) if license_key != '': liz.setProperty('inputstream.adaptive.license_type', 'com.widevine.alpha') liz.setProperty('inputstream.adaptive.license_key', license_key) liz.setMimeType('application/dash+xml') liz.setContentLookup(False) xbmcplugin.setResolvedUrl(int(self.sysARG[1]), True, liz) else: liz = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(self.sysARG[1]), True, liz) # Hack to fix 6 channel audio causing buffering issues while not xbmc.Player().isPlayingVideo(): xbmc.Monitor().waitForAbort(0.25) if xbmc.Player().isPlayingVideo() and len(xbmc.Player().getAvailableAudioStreams()) > 1: xbmc.Player().setAudioStream(0) if external_id != '': while xbmc.Player().isPlayingVideo() and not xbmc.Monitor().abortRequested(): position = int( float(xbmc.Player().getTime())) # Get timestamp of video from VideoPlayer to save as resume time duration = int(float(xbmc.Player().getTotalTime())) # Get the total time of video playing xbmc.Monitor().waitForAbort(3) self.setResume(external_id, position, duration) def setResume(self, external_id, position, duration): # If there's only 2 min left delete the resume point if duration - position < 120: url = '%s/resumes/v4/resumes/%s' % ( self.endPoints['environments']['production']['cmw_url'], str(external_id)) payload = '{"platform":"browser","product":"sling"}' requests.delete(url, headers=HEADERS, data=payload, auth=self.user.getAuth(), verify=VERIFY) else: url = '%s/resumes/v4/resumes' % (self.endPoints['environments']['production']['cmw_url']) payload = '{"external_id":"' + str(external_id) + '","position":' + str(position) + ',"duration":' + str( duration) + ',"resume_type":"fod","platform":"browser","product":"sling"}' requests.put(url, headers=HEADERS, data=payload, auth=self.user.getAuth(), verify=VERIFY) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0, contextMenu=None, properties=None): try: name = name.encode("utf-8") except: pass log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) if mode == 21: liz.setProperty("IsPlayable","false") else: liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","title":name}) else: if 'mediatype' in infoList: self.contentType = '%ss' % (infoList['mediatype']) liz.setInfo(type="Video", infoLabels=infoList) if infoArt == False: liz.setArt({'thumb': ICON, 'fanart': FANART}) else: liz.setArt(infoArt) if contextMenu is not None: liz.addContextMenuItems(contextMenu) if properties is not None: for key, value in properties.iteritems(): liz.setProperty(key, value) u = self.sysARG[0] + "?url=" + urllib.quote_plus(u) + "&mode=" + str(mode) + "&name=" + urllib.quote_plus(name) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, totalItems=total) def addDir(self, name, u, mode, infoList=False, infoArt=False): name = name.encode("utf-8") log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","title":name}) else: if 'mediatype' in infoList: self.contentType = '%ss'%(infoList['mediatype']) liz.setInfo(type="Video", infoLabels=infoList) if infoArt == False: liz.setArt({'thumb':ICON,'fanart':FANART}) else: liz.setArt(infoArt) u=self.sysARG[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]),url=u,listitem=liz,isFolder=True) def getSchedule(self, channel): if channel.get('metadata',{}).get('has_linear_schedule', True) == False: log('getSchedule, skipping channel with no linear schedule: ' + self.getChannelName(channel)) return {} # don't request schedule for this channel utcdate = datetime.datetime.utcnow().strftime("%y%m%d") + '0000' scheduleURL = '%s/cms/publish3/channel/schedule/24/%s/1/{}.json' % \ (self.endPoints['environments']['production']['cms_url'], utcdate) scheduleURL = scheduleURL.format(channel['channel_guid']) log('getSchedule, calling getURL for ' + str(self.getChannelName(channel))) response = self.getURL(scheduleURL) if response and 'schedule' in response: return response['schedule'] else: return {} #support for uEPG universal epg framework module available from the Kodi repository. https://github.com/Lunatixz/KODI_Addons/tree/master/script.module.uepg def uEPG(self): log('uEPG') channels = self.getChannels() return poolList(self.buildGuide, [(idx + 1, channel, self.getSchedule(channel)) for idx, channel in enumerate(channels)]) def getChannelName(self, channel): chmeta = channel.get('metadata',{}) chname = (chmeta.get('channel_name','') or chmeta.get('title','') or chmeta.get('call_sign','')) return chname def getTimeForLog(self, time): return datetime.datetime.fromtimestamp(time).strftime("%a %H:%M") def buildGuide(self, data): chnum, channel, schedule = data chname = self.getChannelName(channel) log('buildGuide for channel: ' + chname) if channel.get('metadata',{}).get('has_linear_schedule', True) == False: log('buildGuide, on demand only') return None chmeta = channel['metadata'] link = channel['qvt_url'] chlogo = (chmeta.get('thumbnail_cropped',{}).get('url','') or ICON) thumb = (channel.get('thumbnail',{}).get('url','') or chlogo or ICON) fanart = (chmeta.get('default_schedule_image',{}).get('url','') or FANART) url = self.sysARG[0]+'?mode=play&name=%s&url=%s'%(chname, link) art = { 'thumb': thumb, 'poster': thumb, 'fanart': fanart, 'icon': chlogo, 'clearlogo': chlogo } currenttime = time.time() guidedata = [] previousstarttime = 0 previousduration = 0 for scheduleList in schedule['scheduleList']: starttime = float(scheduleList['schedule_start']) duration = float(scheduleList['duration']) title = scheduleList.get('title','') program = scheduleList.get('program',{}) metadata = scheduleList.get('metadata',{}) if previousstarttime > 0 and previousstarttime + previousduration < starttime: # fill in the missing schedule data missingstarttime = previousstarttime + previousduration + 1 missingduration = starttime - missingstarttime log('buildGuide, ' + self.getTimeForLog(missingstarttime) + ' - ' + self.getTimeForLog(missingstarttime + missingduration) + ': no schedule data') guidedata.append({ 'starttime': missingstarttime, 'duration': missingduration, 'mediatype': '', 'label': 'no program data', 'title': 'no program data', 'plot': '', 'genre': '', 'url': url, 'art': art }) log('buildGuide, ' + self.getTimeForLog(starttime) + ' - ' + self.getTimeForLog(starttime + duration) + ': ' + title.encode('utf8')) guidedata.append({ 'starttime': starttime, 'duration': duration, 'mediatype': program.get('type',''), 'label': title, 'title': title, 'plot': metadata.get('description',''), 'genre': metadata.get('genre',''), 'url': url, 'art': art }) previousstarttime = starttime previousduration = duration if len(guidedata) < 1: log('buildGuide, no schedule found') return None return { 'channelname': chname, 'channelnumber': chnum, 'channellogo': chlogo, 'isfavorite': False, 'guidedata': guidedata } def getParams(self): return dict(urlparse.parse_qsl(self.sysARG[2][1:])) def run(self): global LOGIN_URL, USER_SUBS cache = True update = False params = self.getParams() LOGIN_URL = self.endPoints['environments']['production_noplay'][ 'micro_ums_url'] + '/sling-api/oauth/authenticate-user' loggedIn, message = self.user.logIn(LOGIN_URL, USER_EMAIL, USER_PASSWORD) log("Sling Class is logIn() ==> Success: " + str(loggedIn) + " | Message: " + message) if loggedIn: log("self.user Subscriptions URL => " + USER_INFO_URL) gotSubs, message = self.user.getUserSubscriptions(USER_INFO_URL) if gotSubs: USER_SUBS = message log("self.user Subscription Attempt, Success => " + str(gotSubs) + "Message => " + message) else: sys.exit() try: url = urllib.unquote(params["url"]) except: url = None try: name = urllib.unquote_plus(params["name"]) except: name = None try: mode = params["mode"] if mode.isdigit(): mode = int(mode) except: mode = None log("Mode: " + str(mode)) log("URL : " + str(url)) log("Name: " + str(name)) if mode is None: self.buildMenu() elif mode == 'live': self.buildLive('live') elif mode == 'vod': self.buildLive('vod') elif mode == 'my_tv': self.buildMyTV(name) elif mode == 'my_tv_item': self.buildMyTV(name, loadJSON(url)) elif mode == 'search': self.search() elif mode == 'search_result': self.search(url) elif mode == 'tv_show': self.buildShow(name, url) elif mode == 'play': self.playVideo(name, url) elif mode == 'no_play': sys.exit() elif mode == 'on_demand': self.buildOnDemand(url) elif mode == 'on_now' or mode == 'sports': self.buildOnNow(mode) elif mode == 'on_now_item': self.buildOnNowItems(name, url) cache = False elif mode == 'logout': self.user.logOut() elif mode == 'settings': REAL_SETTINGS.openSettings(), self.buildMenu() update = True elif mode == 'guide': cache = False xbmc.executebuiltin( "RunScript(script.module.uepg,json=%s&skin_path=%s&refresh_path=%s&refresh_interval=%s&row_count=%s)" % ( urllib.quote(json.dumps(list(self.uEPG()))), urllib.quote(json.dumps(ADDON_PATH)), urllib.quote(json.dumps(sys.argv[0] + "?mode=20")), urllib.quote(json.dumps("7200")), urllib.quote(json.dumps("5")))) xbmcplugin.setContent(int(self.sysARG[1]), self.contentType) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_NONE) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_TITLE) xbmcplugin.endOfDirectory(int(self.sysARG[1]), updateListing=update, cacheToDisc=cache)
class PlayOn: def __init__(self, sysARG): log('__init__, sysARG = ' + str(sysARG)) self.sysARG = sysARG self.cache = SimpleCache() if URLTYPE == 'upnp': self.chkUPNP() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheResponse: request = urllib2.Request(url) request.add_header( 'User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' ) cacheResponse = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, cacheResponse, expiration=datetime.timedelta(minutes=5)) return cacheResponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) if url == BASE_URL + PLAYON_DATA: self.getIP() def getIP(self): urls = self.openURL(AUTO_URL).split('|') for url in urls: match = re.findall(r'[0-9]+(?:\.[0-9]+){3}:[0-9]+', url) log('getIP, match = ' + str(match)) if len(match) == 0: url = getKeyboard(LANGUAGE(30001), LANGUAGE(30002)) if url == False: return if not url.startswith('http'): url = "http://%s" % url BASE_URL = url REAL_SETTINGS.setSetting("playonserver", url) if self.chkIP(url): return def chkIP(self, url=BASE_URL): log('chkIP, url = ' + str(url)) results = xmltodict.parse(self.openURL(url + PLAYON_DATA)) if results and 'catalog' in results: try: ServerName = results['catalog']['@name'] ServerVer = 'PlayOn v.%s' % results['catalog']['@server'] ServerMSG = "Connected to [B]%s %s[/B]" % (ServerName, ServerVer) log('chkIP, ServerName = ' + ServerName) log('chkIP, ServerVer = ' + ServerVer) REAL_SETTINGS.setSetting("playonServerid", ServerMSG) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30011) % ServerName, ICON, 5000) xbmc.executebuiltin("Container.Refresh") return True except Exception as e: log("chkIP Failed! " + str(e), xbmc.LOGERROR) else: xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30010), ICON, 5000) return False def getUPNP(self): log('getUPNP') """ Check Kodi UPNP support. """ json_query = ( '{"jsonrpc":"2.0","method":"Settings.GetSettingValue","params":{"setting":"services.upnp"},"id":1}' ) data = json.loads(xbmc.executeJSONRPC(json_query)) try: if 'result' in data and 'value' in data['result']: return data['result']['value'] except Exception as e: log('getUPNP, Failed! ' + str(e)) return False def setUPNP(self): log('setUPNP') """ Enable Kodi UPNP support. """ json_query = ( '{"jsonrpc":"2.0","method":"Settings.SetSettingValue","params":{"setting":"services.upnp","value":true},"id":1}' ) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30015), ICON, 5000) return json.loads(xbmc.executeJSONRPC(json_query)) def getUPNP_ID(self): log('getUPNP_ID') """ Check if upnp id is valid. """ json_query = ( '{"jsonrpc":"2.0","method":"Files.GetDirectory","params":{"directory":"%s"},"id":1}' % BASE_UPNP) try: if not json.loads( xbmc.executeJSONRPC(json_query) )['result']['files'][0]['file'].endswith('/playonprovider/'): return None except Exception as e: log('getUPNP_ID, Failed! ' + str(e)) return None return BASE_UPNP def chkUPNP(self): log('chkUPNP') """ Query json, locate 'playon server' path, else prompt. """ if self.getUPNP_ID() is not None: return else: if not self.getUPNP(): self.setUPNP() json_query = ( '{"jsonrpc":"2.0","method":"Files.GetDirectory","params":{"directory":"upnp://"},"id":1}' ) data = json.loads(xbmc.executeJSONRPC(json_query)) if data and 'result' in data and 'files' in data['result']: for item in data['result']['files']: if (item['label']).lower().startswith('playon'): REAL_SETTINGS.setSetting("playonUPNPid", item['file'].rstrip('/')) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30013), ICON, 5000) BASE_UPNP = item['file'] REAL_SETTINGS.setSetting("playonUPNPid", BASE_UPNP.rstrip('/')) elif PTVL_RUNNING == False: BASE_UPNP = xbmcgui.Dialog().browse(0, LANGUAGE(30014), 'files', '', False, False, 'upnp://') if BASE_UPNP != -1: REAL_SETTINGS.setSetting("playonUPNPid", BASE_UPNP.rstrip('/')) else: xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30010), ICON, 5000) def buildItemMenu(self, uri=PLAYON_DATA, search=False): log('buildItemMenu, uri = ' + uri) try: genSTRM = False results = [] ranNum = random.randrange(9) response = dict(xmltodict.parse(self.openURL(BASE_URL + uri))) if response and 'catalog' in response and 'group' in response[ 'catalog']: results = response['catalog']['group'] elif response and 'group' in response: # and response['group']['@href'] == uri: results = response['group']['group'] genSTRM = True if isinstance(results, collections.OrderedDict): results = [dict(results)] if not search and uri == PLAYON_DATA: self.addDir('[B][PlayOn][/B] Search', '', 2, ICON, genSTRM) for item in results: try: if isinstance(item, collections.OrderedDict): item = dict(item) if item['@type'] == 'folder': name = item['@name'].replace( 'PlayOn', '[B][PlayOn][/B]').replace( 'PlayMark', '[B][Playon][/B] PlayMark') if search and name.startswith('[B][PlayOn][/B]'): continue thumb = BASE_URL + ((item.get('@art', '').replace( '&size=tiny', '&size=large')) or folderIcon(ranNum)) if search and item.get('@searchable', 'false') == 'true': myURL = json.dumps({ 'id': item.get('@id', ''), 'uri': item['@href'], 'searchable': item.get('@searchable', 'false') }) self.addDir('Search %s' % name, myURL, 3, thumb) elif not search: self.addDir(name, item['@href'], 1, thumb, genSTRM) elif item['@type'] == 'video': self.addLink(item['@name'], item['@href'], 9, len(results)) except Exception as e: log("buildItemMenu Failed! " + str(e), xbmc.LOGERROR) except Exception as e: log("buildItemMenu Failed! " + str(e), xbmc.LOGERROR) def searchItem(self, uri): log('searchItem, uri = ' + uri) item = json.loads(uri) query = getKeyboard(header=LANGUAGE(30016)) if query == False: self.buildItemMenu(search=True) else: self.buildItemMenu(SEARCH_URL % (item['id']) + 'dc:description%20contains%20' + urllib.quote(query)) def playLater(self, name, uri): log('playLater, uri = ' + uri) response = dict(xmltodict.parse(self.openURL(BASE_URL + uri))) if response and 'result' in response: result = dict(response['result']) if result['status'] == "true": msg = result['msg'].replace('The media item', name) xbmcgui.Dialog().notification(ADDON_NAME, msg, ICON, 5000) def playVideo(self, name, uri): log('playVideo, uri = ' + uri) liz = self.buildListitem(uri) if 'm3u8' in uri.lower(): liz.setProperty('inputstreamaddon', 'inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type', 'hls') xbmcplugin.setResolvedUrl(int(self.sysARG[1]), True, liz) def buildListitem(self, uri, contextMenu=[]): result = {} infoList = {} response = dict(xmltodict.parse(self.openURL(BASE_URL + uri))) if response and 'group' in response and response['group'][ '@href'] == uri: result = dict(response['group']) tvshowtitle = (dict(result.get('series', '')).get('@name', '') or None) title = (result.get('@name', '') or dict(result['media_title'])['@name'] or dict(result['media'])['@name']) label = title mType = 'movie' if tvshowtitle is not None: if tvshowtitle not in title: label = '%s - %s' % (tvshowtitle, title) season, episode = parseSEinfo(title) infoList['season'] = int(season) infoList['episode'] = int(episode) mType = 'episode' plot = (result.get('@description', '') or dict(result.get('description', '')).get('@name', '') or '') thumb = BASE_URL + (result.get('@art', '') or dict( result.get('media', '')).get('@art', '') or ICON).replace( '&size=tiny', '&size=large') try: aired = (dict(result.get('date', '')).get('@name', '') or datetime.datetime.now().strftime('%m/%d/%Y')) aired = (datetime.datetime.strptime( aired, '%m/%d/%Y')).strftime('%Y-%m-%d') except: aired = datetime.datetime.now().strftime('%Y-%m-%d') timeData = (dict(result.get('time', '')).get('@name', '') or '') playLater = dict(result.get('media_playlater', '')).get('@src', '') contextMenu = contextMenu + [ ('Add to PlayLater', 'XBMC.RunPlugin(%s)' % (self.sysARG[0] + "?url=" + urllib.quote_plus(playLater) + "&mode=" + str(8) + "&name=" + urllib.quote_plus(label.encode("utf-8")))) ] if len(timeData) > 0: timeList = timeData.split(':') hours = int(timeList[0]) mins = int(timeList[1]) secs = int(timeList[2]) duration = ((hours * 60 * 60) + (mins * 60) + secs) else: duration = 0 if URLTYPE == 'm3u8' and 'playlaterrecordings' not in result[ '@href']: url = BASE_VIDEO_URL % (BASE_URL, result['@href'].split('?id=')[1]) elif URLTYPE == 'ext' or 'playlaterrecordings' in result['@href']: url = BASE_URL + '/' + dict(result['media'])['@src'] else: url = BASE_UPNP + '/' + dict( result['media'])['@src'].split('.')[0].split('/')[0] + '/' log('playVideo, url = ' + url) liz = xbmcgui.ListItem(label, path=url) liz.addContextMenuItems(contextMenu) CONTENT_TYPE = mType infoList = { "mediatype": mType, "label": label, "title": label, "tvshowtitle": tvshowtitle, "plot": plot, "duration": duration, "aired": aired } liz.setInfo(type="Video", infoLabels=infoList) liz.setArt({ "thumb": thumb, "poster": thumb, "icon": ICON, "fanart": FANART }) liz.setProperty("IsPlayable", "true") liz.setProperty("IsInternetStream", "true") return liz def addLink(self, name, u, mode, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) contextMenu = [ ('Add to Library', 'XBMC.RunPlugin(%s)' % (self.sysARG[0] + "?url=" + urllib.quote_plus(u) + "&mode=" + str(7) + "&name=" + urllib.quote_plus(name))) ] liz = self.buildListitem(u) u = self.sysARG[0] + "?url=" + urllib.quote_plus(u) + "&mode=" + str( mode) + "&name=" + urllib.quote_plus(name) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, totalItems=total) def addDir(self, name, u, mode, thumb=ICON, strm=False): name = name.encode("utf-8") log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) # if strm: # contextMenu = [('Add to Library','XBMC.RunPlugin(%s)'%(self.sysARG[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(7)+"&name="+urllib.quote_plus(name)))] # liz.addContextMenuItems(contextMenu) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "label": name, "title": name }) liz.setArt({'thumb': thumb, 'fanart': FANART}) u = self.sysARG[0] + "?url=" + urllib.quote_plus(u) + "&mode=" + str( mode) + "&name=" + urllib.quote_plus(name) xbmcplugin.addDirectoryItem(handle=int(self.sysARG[1]), url=u, listitem=liz, isFolder=True) def genSTRMS(self, name, uri): log('genSTRMS, name = ' + name) #todo def getParams(self): return dict(urlparse.parse_qsl(self.sysARG[2][1:])) def run(self): params = self.getParams() 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: self.buildItemMenu() if mode == 1: self.buildItemMenu(url) if mode == 2: self.buildItemMenu(search=True) if mode == 3: self.searchItem(url) if mode == 7: self.genSTRMS(name, url) if mode == 8: self.playLater(name, url) if mode == 9: self.playVideo(name, url) xbmcplugin.setContent(int(self.sysARG[1]), CONTENT_TYPE) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_NONE) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.addSortMethod(int(self.sysARG[1]), xbmcplugin.SORT_METHOD_TITLE) xbmcplugin.endOfDirectory(int(self.sysARG[1]), cacheToDisc=True)
class FanartTv(object): '''get artwork from fanart.tv''' base_url = 'http://webservice.fanart.tv/v3/' api_key = '639191cb0774661597f28a47e7e2bad5' client_key = '' ignore_cache = False def __init__(self, simplecache=None): '''Initialize - optionaly provide simplecache object''' if not simplecache: from simplecache import SimpleCache self.cache = SimpleCache() else: self.cache = simplecache addon = xbmcaddon.Addon(ADDON_ID) self.client_key = addon.getSetting("fanarttv_apikey").strip() del addon def artist(self, artist_id): '''get artist artwork''' data = self.get_data("music/%s" % artist_id) mapping_table = [("artistbackground", "fanart"), ("artistthumb", "thumb"), ("hdmusiclogo", "clearlogo"), ("musiclogo", "clearlogo"), ("musicbanner", "banner")] return self.map_artwork(data, mapping_table) def album(self, album_id): '''get album artwork''' artwork = {} data = self.get_data("music/albums/%s" % album_id) if data: mapping_table = [("cdart", "discart"), ("albumcover", "thumb")] for item in data["albums"].itervalues(): artwork.update(self.map_artwork(item, mapping_table)) return artwork def musiclabel(self, label_id): '''get musiclabel logo''' artwork = {} data = self.get_data("music/labels/%s" % label_id) if data and data.get("musiclabel"): for item in data["musiclabel"]: # we just grab the first logo (as the result is sorted by likes) if item["colour"] == "colour" and "logo_color" not in artwork: artwork["logo_color"] = item["url"] elif item["colour"] == "white" and "logo_white" not in artwork: artwork["logo_white"] = item["url"] return artwork def movie(self, movie_id): '''get movie artwork''' data = self.get_data("movies/%s" % movie_id) mapping_table = [ ("hdmovielogo", "clearlogo"), ("moviedisc", "discart"), ("movielogo", "clearlogo"), ("movieposter", "poster"), ("hdmovieclearart", "clearart"), ("movieart", "clearart"), ("moviebackground", "fanart"), ("moviebanner", "banner"), ("moviethumb", "landscape") ] return self.map_artwork(data, mapping_table) def tvshow(self, tvshow_id): '''get tvshow artwork''' data = self.get_data("tv/%s" % tvshow_id) mapping_table = [("hdtvlogo", "clearlogo"), ("clearlogo", "clearlogo"), ("hdclearart", "clearart"), ("clearart", "clearart"), ("showbackground", "fanart"), ("tvthumb", "landscape"), ("tvbanner", "banner"), ("characterart", "characterart"), ("tvposter", "poster")] return self.map_artwork(data, mapping_table) def tvseason(self, tvshow_id, season): '''get season artwork - banner+landscape only as the seasonposters lacks a season in the json response''' data = self.get_data("tv/%s" % tvshow_id) artwork = {} mapping_table = [("seasonthumb", "landscape"), ("seasonbanner", "banner")] for artwork_mapping in mapping_table: fanarttv_type = artwork_mapping[0] kodi_type = artwork_mapping[1] if fanarttv_type in data: images = [ item for item in data[fanarttv_type] if item["season"] == str(season) ] images = process_method_on_list(self.score_image, data[fanarttv_type]) if images: images = sorted(images, key=itemgetter("score"), reverse=True) images = [item["url"] for item in images] artwork[kodi_type + "s"] = images artwork[kodi_type] = images[0] return artwork def get_data(self, query): '''helper method to get data from fanart.tv json API''' url = '%s%s?api_key=%s' % (self.base_url, query, self.api_key) if self.client_key: url += '&client_key=%s' % self.client_key cache = self.cache.get(url) if cache: result = cache else: result = get_json(url) self.cache.set(url, result) return result def map_artwork(self, data, mapping_table): '''helper method to map the artwork received from fanart.tv to kodi known formats''' artwork = {} if data: for artwork_mapping in mapping_table: fanarttv_type = artwork_mapping[0] kodi_type = artwork_mapping[1] images = [] if fanarttv_type in data and kodi_type not in artwork: # artworktype is found in the data, now do some magic to select the best one images = process_method_on_list(self.score_image, data[fanarttv_type]) # set all images in list and select the item with highest score if images: images = sorted(images, key=itemgetter("score"), reverse=True) images = [item["url"] for item in images] artwork[kodi_type + "s"] = images artwork[kodi_type] = images[0] return artwork @staticmethod def score_image(item): '''score item based on number of likes and the language''' score = 0 item["url"] = item["url"].replace(" ", "%20") score += try_parse_int(item["likes"]) if "lang" in item: if item["lang"] == KODI_LANGUAGE: score += 1000 elif item["lang"] == "en": score += 500 item["score"] = score return item
class Sky(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): log('openURL, url = ' + str(url)) try: cacheResponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheResponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') response = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, response, expiration=datetime.timedelta(hours=6)) return self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMainMenu(self): self.addLink(LANGUAGE(30002),'',0) self.addYoutube(LANGUAGE(30003),'plugin://plugin.video.youtube/user/skynews/') def buildLiveLink(self): try: link = 'http:' + BeautifulSoup(self.openURL(LIVEURL), "html.parser")('div', {'class': 'video-embed'})[0].find('iframe').get('src') print self.resolveYoutube(link) self.playVideo(LANGUAGE(30004),self.resolveYoutube(link)) except: self.playVideo(LANGUAGE(30004),YTURL + 'XOacA3RYrXk') def resolveYoutube(self, link): if len(re.findall('http[s]?://www.youtube.com/watch', link)) > 0: return YTURL + link.split('/watch?v=')[1] elif len(re.findall('http[s]?://www.youtube.com/embed', link)) > 0: return YTURL + link.split('/embed/')[1].split('?autoplay=1')[0] elif len(re.findall('http[s]?://youtu.be/', link)) > 0: return YTURL + link.split('/youtu.be/')[1] def playVideo(self, name, url, liz=None): log('playVideo') if not liz: liz = xbmcgui.ListItem(name, path=url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, title, url): liz=xbmcgui.ListItem(title) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":title,"title":title} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name,"genre":"News"}) 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)
class MM(object): def __init__(self): self.cache = SimpleCache() def sendJSON(self, command, cache=False): log('sendJSON, command = ' + str(command)) cacheresponse = self.cache.get(ADDON_NAME + '.sendJSON, command = %s' % json.dumps(command)) if DEBUG or not cache: cacheresponse = None if not cacheresponse: cacheresponse = uni(xbmc.executeJSONRPC(command)) self.cache.set(ADDON_NAME + '.sendJSON, command = %s' % json.dumps(command), cacheresponse, expiration=datetime.timedelta(hours=12)) return json.loads(cacheresponse) def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if DEBUG: cacheresponse = None if not cacheresponse: request = urllib2.Request(url) request.add_header( 'User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' ) cacheresponse = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, cacheresponse, expiration=datetime.timedelta(minutes=15)) return json.loads(cacheresponse) except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def getMonitored(self, type='series'): log('getMonitored, type = ' + type) if type == 'series': mediaList = self.getTVShows() url = SONARR_URL setSetting = 'ScanSonarr' else: mediaList = self.getMovies() url = RADARR_URL setSetting = 'ScanRadarr' results = self.openURL(url) if not results: return userList = self.getUserList(type) for idx, item in enumerate(results): updateDialogProgress = (idx) * 100 // len(results) REAL_SETTINGS.setSetting( setSetting, 'Scanning... (%d' % (updateDialogProgress) + '%)') match = False show = item["title"] for kodititle in userList: if kodititle.lower() == show.lower(): log('getMonitored, monitor match: show = ' + show + ', kodititle = ' + kodititle) match = True break if match: continue if item["monitored"]: for title in mediaList: title = title.getLabel() if title.lower() == show.lower(): log('getMonitored, kodi match: show = ' + show + ', title = ' + title) userList.append(title) break if type == 'series': REAL_SETTINGS.setSetting( setSetting, LANGUAGE(30011) % (datetime.datetime.now().strftime('%Y-%m-%d'))) else: REAL_SETTINGS.setSetting( setSetting, LANGUAGE(30011) % (datetime.datetime.now().strftime('%Y-%m-%d'))) if len(userList) > 0: self.setUserList(userList, type) def getUserList(self, type='series'): REAL_SETTINGS = xbmcaddon.Addon(id=ADDON_ID) if type == 'series': try: return (REAL_SETTINGS.getSetting('TVShowList').split(',') or []) except: return [] else: try: return (REAL_SETTINGS.getSetting('MoviesList').split(',') or []) except: return [] def setUserList(self, userList, type='series'): msg = "" if type == 'series': setSetting0 = 'ScanSonarr' setSetting1 = 'TVShowList' setSetting2 = 'ViewTVShows' else: setSetting0 = 'ScanRadarr' setSetting1 = 'MoviesList' setSetting2 = 'ViewMovies' if len(userList) > 0: msg = LANGUAGE(30010) % (len(userList)) else: self.notificationDialog(LANGUAGE(30017)) REAL_SETTINGS.setSetting(setSetting0, '') userList = ','.join(userList) log('setUserList, UserList = ' + userList + ', type = ' + type) REAL_SETTINGS.setSetting(setSetting1, userList) REAL_SETTINGS.setSetting(setSetting2, msg) if len(userList) == 0: REAL_SETTINGS.openSettings() def hasMovie(self): return bool(xbmc.getCondVisibility('Library.HasContent(Movies)')) def hasTV(self): return bool(xbmc.getCondVisibility('Library.HasContent(TVShows)')) def removeSeason(self, playingItem): self.notificationDialog('Coming Soon') #todo #fetch tvshowid from seasonid, check season episode total to sonarr, if playcount > 0 on all remove. # {"jsonrpc":"2.0","method":"VideoLibrary.GetSeasonDetails","params":{"seasonid":2075,"properties":["episode","tvshowid"]},"id":6} def splitStack(self, file): path = file.replace(',,', ',').split(' , ') return '|'.join(path).replace( 'stack://', '').split(', media = video')[0].split('|') def removeContent(self, playingItem, silent=False, bypass=False): try: type = playingItem["type"] dbid = playingItem["id"] log("removeContent, type = " + type + ", dbid = " + str(dbid)) param = { 'episode': 'episodeid', 'movie': 'movieid', 'movie': 'movieid', 'tvshow': 'tvshowid', 'season': 'seasonid' }[type] method = { 'episodeid': 'RemoveEpisode', 'movieid': 'RemoveMovie', 'tvshowid': 'RemoveTVShow', 'seasonid': '' }[param] file = playingItem.get("file", "") mediaInfo = playingItem["label"] if type == 'movie': if REAL_SETTINGS.getSetting('Monitor_Movies') == 'false': return elif type == 'season': return self.removeSeason(playingItem) else: tvshow = playingItem["showtitle"] userList = self.getUserList() if tvshow not in userList and not bypass: return mediaInfo = '%s - %sx%s - %s' % (tvshow, playingItem["season"], playingItem["episode"], mediaInfo) if silent == False: if not self.yesnoDialog(mediaInfo, file, header='%s - %s' % (ADDON_NAME, LANGUAGE(30021) % (type)), yes='Remove', no='Keep', autoclose=15000): return if REAL_SETTINGS.getSetting('Enable_Removal') == 'true': json_query = '{"jsonrpc":"2.0","method":"VideoLibrary.%s","params":{"%s":%s},"id":1}' % ( method, param, str(dbid)) self.sendJSON(json_query) # if path.startswith('stack://'): # files = self.splitStack(path) # for file in files: MoviesList.append({'label':label,'label2':file,'thumb':(item['art'].get('poster','') or item['thumbnail'])}) # else: if self.deleteFile(file): self.notificationDialog(LANGUAGE(30023) % mediaInfo) return self.notificationDialog(LANGUAGE(30022)) except Exception as e: log("removeContent Failed! " + str(e), xbmc.LOGERROR) log('removeContent, playingItem = ' + json.dumps(playingItem)) def deleteFile(self, file): log("deleteFile") for i in range(3): try: if xbmcvfs.delete(file): return True except: pass return False def cleanLibrary(self, type="video"): type = { 'video': 'video', 'episode': 'tvshows', 'movie': 'movies' }[type] json_query = '{"jsonrpc":"2.0","method":"VideoLibrary.Clean","params":{"showdialogs":false,"content":"5s"},"id":1}' % type self.sendJSON(json_query) def getActivePlayer(self): json_query = ( '{"jsonrpc":"2.0","method":"Player.GetActivePlayers","params":{},"id":1}' ) json_response = self.sendJSON(json_query) try: id = json_response['result'][0]['playerid'] except: id = 1 log("getActivePlayer, id = " + str(id)) return id def requestItem(self): json_query = ( '{"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":%d,"properties":%s}, "id": 1}' % (self.getActivePlayer(), JSON_IT_ENUMS)) json_response = self.sendJSON(json_query) if 'result' not in json_response: return {} return json_response['result'].get('item', {}) def requestFile(self, file, media='video', fallback={}): log("requestFile, file = " + file + ", media = " + media) json_query = ( '{"jsonrpc":"2.0","method":"Files.GetFileDetails","params":{"file":"%s","media":"%s","properties":%s},"id":1}' % (self.escapeDirJSON(file), media, JSON_FL_ENUMS)) json_response = self.sendJSON(json_query) if 'result' not in json_response: return fallback return json_response['result'].get('filedetails', fallback) def escapeDirJSON(self, mydir): if (mydir.find(":")): mydir = mydir.replace("\\", "\\\\") return mydir def getTVShows(self): if not self.hasTV(): return [] busy = self.progressDialogBG(0, string1=LANGUAGE(30038)) json_query = ( '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","params":{"properties":%s}, "id": 1}' % (JSON_TV_ENUMS)) json_response = self.sendJSON(json_query, cache=True) if not 'result' in json_response: return [] self.progressDialogBG(100, busy) return sorted(json_response['result']['tvshows'], key=lambda x: x['label']) def getMovies(self): if not self.hasMovie(): return [] busy = self.progressDialogBG(0, string1=LANGUAGE(30039)) json_query = ( '{"jsonrpc":"2.0","method":"VideoLibrary.GetMovies","params":{"properties":%s}, "id": 1}' % (JSON_MV_ENUMS)) json_response = self.sendJSON(json_query) self.progressDialogBG(100, busy) if not 'result' in json_response: return [] return sorted(json_response['result']['movies'], key=lambda x: x['label']) def buildListitem(self, list, TV=False): #todo move to window UI and control panel mediaLST = [] msg = LANGUAGE(30039) if TV: msg = LANGUAGE(30038) busy = self.progressDialogBG(0, string1=msg) for idx, item in enumerate(list): try: label = item['label'] path = item['file'] updateDialogProgress = (idx) * 100 // len(list) busy = self.progressDialogBG(updateDialogProgress, busy, string1=label) if TV: mediaLST.append({ 'label': label, 'label2': path, 'thumb': (item['art'].get('poster', '') or item['thumbnail']) }) else: video = item['streamdetails']['video'] audio = item['streamdetails']['audio'] if path.startswith('stack://'): label = ' %s [[B]STACK[/B]]' % label if len(video) > 0: label = '%s - Video [Codec: [B]%s[/B]|Height: [B]%s[/B]|Runtime: [B]%s[/B]]' % ( label, video[0]['codec'].upper(), video[0]['height'], video[0]['duration']) if len(audio) > 0: label = '%s - Audio [Codec: [B]%s[/B]|Channels: [B]%s[/B]|Language: [B]%s[/B]]' % ( label, audio[0]['codec'].upper(), audio[0]['channels'], audio[0]['language'].title()) mediaLST.append({ 'label': label, 'label2': path, 'thumb': (item['art'].get('poster', '') or item['thumbnail']) }) except Exception as e: log("buildListitem Failed! %s , item = %s" % (str(e), item), xbmc.LOGERROR) self.progressDialogBG(100, busy) log("buildListitem, found tvshows " + str(len(mediaLST))) return [ self.getListitem(show['label'], show['label2'], show['thumb']) for show in mediaLST ] def viewTVShows(self): TVShowList = self.buildListitem(self.getTVShows(), TV=True) select = self.selectDialog(TVShowList, LANGUAGE(30037), preselect=self.findItemLens( TVShowList, self.getUserList())) if select is not None: self.setUserList([TVShowList[idx].getLabel() for idx in select]) def getListitem(self, label1="", label2="", iconImage="", thumbnailImage="", path="", offscreen=False): try: return xbmcgui.ListItem(label1, label2, iconImage, thumbnailImage, path, offscreen) except: return xbmcgui.ListItem(label1, label2, iconImage, thumbnailImage, path) def findItemLens(self, tvlist, userlist): return [ idx for idx, tvshow in enumerate(tvlist) for usershow in userlist if tvshow.getLabel() == usershow ] def selectDialog(self, list, header=ADDON_NAME, autoclose=0, preselect=None, multi=True, useDetails=True): if preselect is None: preselect = {True: [], False: -1}[multi] if multi: return xbmcgui.Dialog().multiselect(header, list, autoclose, preselect, useDetails) else: return xbmcgui.Dialog().select(header, list, autoclose, preselect, useDetails) def scanDuplicates(self): dupLST = [] delLST = [] MoviesList = self.getMovies() if len(MoviesList) > 0: duplicates = [ item for item, count in collections.Counter( [movie['label'] for movie in MoviesList]).items() if count > 1 ] for item in duplicates: for movie in MoviesList: title = movie['label'] if item.lower() == title.lower(): dupLST.append(movie) if len(dupLST) > 0: dupLST.sort(key=lambda x: x['label']) items = self.selectDialog(self.buildListitem(dupLST), LANGUAGE(30036)) if items: busy = self.progressDialogBG(0, string1=LANGUAGE(30040)) # delLST = [self.requestFile(dupLST[item].getLabel2()) for item in items if not dupLST[item].getLabel2().startswith('stack://')] delLST = [ self.requestFile(dupLST[item].getLabel2()) for item in items ] for idx, movie in enumerate(delLST): updateDialogProgress = (idx) * 100 // len(delLST) busy = self.progressDialogBG(updateDialogProgress, busy) self.removeContent(movie) self.progressDialogBG(100, busy) else: self.notificationDialog(LANGUAGE(30033)) def buildMenu(self): items = [LANGUAGE(30002), LANGUAGE(30034), LANGUAGE(30035)] { None: sys.exit, -1: sys.exit, 0: self.viewTVShows, 1: self.scanDuplicates, 2: REAL_SETTINGS.openSettings }[self.selectDialog(items, multi=False, useDetails=False)]() def progressDialogBG(self, percent=0, control=None, string1='', header=ADDON_NAME, notice=NOTIFY): if not notice: return if percent == 0 and control is None: control = xbmcgui.DialogProgressBG() control.create(header, ADDON_NAME) control.update(percent, string1) elif percent == 100 and control is not None: return control.close() if control is not None: control.update(percent, string1) return control def notificationDialog(self, message, header=ADDON_NAME, show=NOTIFY, sound=False, time=1000, icon=ICON): log('notificationDialog: ' + message) if not show: return try: xbmcgui.Dialog().notification(header, message, icon, time, sound=False) except: xbmc.executebuiltin("Notification(%s, %s, %d, %s)" % (header, message, time, icon)) def yesnoDialog(self, str1, str2='', str3='', header=ADDON_NAME, yes='', no='', autoclose=0): return xbmcgui.Dialog().yesno(header, str1, str2, str3, no, yes, autoclose)
class CBR(object): def __init__(self): log('__init__') self.cache = SimpleCache() def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s'%url) if not cacheresponse: request = urllib2.Request(url) request.add_header('User-Agent','Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)') cacheresponse = urllib2.urlopen(request, timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s'%url, cacheresponse, expiration=datetime.timedelta(minutes=15)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) self.addYoutube(LANGUAGE(30006), 'plugin://plugin.video.youtube/channel/UCuCk_7b2_4uSr6y5hFmjuMQ/') def browse(self, url): log('browse, url = ' + str(url)) soup = BeautifulSoup(self.openURL(url), "html.parser") videos = soup('div', {'class': 'thumb-wrap'}) videos.extend(soup('article', {'class': 'thumb-wrap'})) for video in videos: link = video('div', {'class': 'img-wrapper'})[0].find('a').attrs['href'] thumb = video('div', {'class': 'responsiveImg'})[0].find('source').attrs['srcset'] try: label = video('strong', {'class': 'title'})[0].find('a').attrs['title'] except: label = (video('div', {'class': 'info-wrapper'})[0].find('a').get_text()) try: airdate = datetime.datetime.strptime(video('div', {'class': 'details'})[0].find('time').get_text(), '%m.%d.%y') except: airdate = datetime.datetime.now() airdate = airdate.strftime('%Y-%m-%d') plot = '%s - %s'%(label,airdate) try: dur = (video('div', {'class': 'img-wrapper'})[0].find('span').get_text()).split(':') if len(dur) == 3: h, m, s = dur duration = int(h) * 3600 + int(m) * 60 + int(s) else: m, s = dur duration = int(m) * 60 + int(s) except: duration = '0' infoLabels = {"mediatype":"episode","label":label ,"title":label,"duration":duration,"plot":plot,"aired":airdate} infoArt = {"thumb":thumb,"poster":thumb,"fanart":FANART,"icon":thumb,"logo":thumb} vidID = ((thumb.split('/')[8]).split('-')[5]).split('_') link = VIDEO_URL%(vidID[2],vidID[0]) self.addLink(label, link, 9, infoLabels, infoArt, len(videos)) next = soup('a', {'class': 'nextpostslink'}) if len(next) == 0: return next_url = next[0].attrs['href'] next_label = soup('span', {'class': 'pages'})[0].get_text() self.addDir(next_label, next_url, 1) def playVideo(self, name, url): log('playVideo') liz = xbmcgui.ListItem(name, path=url) liz.setProperty('inputstreamaddon','inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type','hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addYoutube(self, name, url): liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') liz.setInfo(type="Video", infoLabels={"label":name,"title":name} ) liz.setArt({'thumb':ICON,'fanart':FANART}) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=liz,isFolder=True) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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 = name.encode("utf-8") log('addDir, name = ' + name) liz=xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={"mediatype":"video","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)
class TVCatchup(object): def __init__(self): log('__init__') self.cache = SimpleCache() def getTVCtime(self): return datetime.datetime.now(timezone('Europe/London')) def openURL(self, url): try: log('openURL, url = ' + str(url)) cacheresponse = self.cache.get(ADDON_NAME + '.openURL, url = %s' % url) if not cacheresponse: cacheresponse = urllib2.urlopen(urllib2.Request(url), timeout=TIMEOUT).read() self.cache.set(ADDON_NAME + '.openURL, url = %s' % url, cacheresponse, expiration=datetime.timedelta(minutes=5)) return cacheresponse except Exception as e: log("openURL Failed! " + str(e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return '' def buildMenu(self, items): for item in items: self.addDir(*item) def buildLive(self): soup = BeautifulSoup(self.openURL(LIVE_URL), "html.parser") results = soup('div', {'class': 'channelsHolder'}) for channel in results: chname = cleanString( channel.find_all('img')[1].attrs['alt']).replace('Watch ', '') label = '%s - %s' % (chname, cleanString(channel.get_text())) link = channel.find_all('a')[0].attrs['href'] thumb = LOGO % chname infoLabels = { "mediatype": "episode", "label": label, "title": label } infoArt = { "thumb": thumb, "poster": thumb, "fanart": FANART, "icon": thumb, "logo": thumb } self.addLink(label, link, 9, infoLabels, infoArt, len(results)) def buildLineup(self, name=None): log('buildLineup, name = ' + str(name)) soup = BeautifulSoup(self.openURL(GUIDE_URL), "html.parser") results = soup('div', {'class': 'row'}) for channel in results: chname = cleanString(channel.find_all('img')[0].attrs['alt']) link = cleanString(channel.find_all('a')[0].attrs['href']) thumb = LOGO % chname if name is None: infoLabels = { "mediatype": "episode", "label": chname, "title": chname } infoArt = { "thumb": thumb, "poster": thumb, "fanart": FANART, "icon": thumb, "logo": thumb } self.addDir(chname, chname, 2, infoLabels, infoArt) elif name.lower() == chname.lower(): try: date = soup('a', {'class': 'last'})[0].attrs['href'] aired = re.findall('/tv-guide/(.+?)/00', date, flags=re.DOTALL)[0] except: aired = self.getTVCtime().strftime('%Y-%m-%d') items = channel('div', {'class': 'hide'}) for item in items: try: time = trimString(item.find_all('span')[0].get_text()) dur = int( (abs(eval(time.replace(':', '.'))) * 60) * 60) start = datetime.datetime.strptime( time.split('-')[0], '%H:%M').strftime('%I:%M %p') except: continue label = '%s: %s - %s' % ( start, chname, cleanString( item.get_text()).split('\n')[0]) try: desc = trimString(item.find_all('br')[0].get_text()) except: desc = '' infoLabels = { "mediatype": "episode", "label": label, "title": label, "plot": desc, "duration": dur, "aired": aired } infoArt = { "thumb": thumb, "poster": thumb, "fanart": FANART, "icon": thumb, "logo": thumb } self.addLink(label, link, 9, infoLabels, infoArt, len(items)) break def uEPG(self): log('uEPG') #support for upcoming uEPG universal epg framework module, module will be available from the Kodi repository. #https://github.com/Lunatixz/KODI_Addons/tree/master/script.module.uepg soup = BeautifulSoup(self.openURL(GUIDE_URL), "html.parser") results = soup('div', {'class': 'row'}) return (self.buildGuide(idx, channel) for idx, channel in enumerate(results)) def buildGuide(self, idx, channel): log('buildGuide') chname = cleanString(channel.find_all('img')[0].attrs['alt']) link = cleanString(channel.find_all('a')[0].attrs['href']) chlogo = LOGO % chname chnum = idx + 1 isFavorite = False newChannel = {} guidedata = [] newChannel['channelname'] = chname newChannel['channelnumber'] = chnum newChannel['channellogo'] = chlogo newChannel['isfavorite'] = isFavorite try: date = soup('a', {'class': 'last'})[0].attrs['href'] aired = re.findall('/tv-guide/(.+?)/00', date, flags=re.DOTALL)[0] except: aired = self.getTVCtime().strftime('%Y-%m-%d') items = channel('div', {'class': 'hide'}) for item in items: try: ttime = trimString(item.find_all('span')[0].get_text()) dur = int((abs(eval(ttime.replace(':', '.'))) * 60) * 60) start = datetime.datetime.strptime( ttime.split('-')[0], '%H:%M').strftime('%I:%M %p') title = cleanString(item.get_text()).split('\n')[0] label = '%s - %s' % (chname, title) starttime = (datetime.datetime.strptime( '%s - %s' % (aired, start), '%Y-%m-%d - %I:%M %p')) starttime = time.mktime(starttime.timetuple()) except: continue try: desc = trimString(item.find_all('br')[0].get_text()) except: desc = '' tmpdata = { "mediatype": "episode", "label": title, "title": label, "originaltitle": label, "plot": desc, "duration": dur } tmpdata['starttime'] = starttime tmpdata['url'] = sys.argv[0] + '?mode=9&name=%s&url=%s' % (label, link) tmpdata['art'] = { "thumb": chlogo, "clearart": chlogo, "fanart": FANART, "icon": chlogo, "clearlogo": chlogo } guidedata.append(tmpdata) newChannel['guidedata'] = guidedata return newChannel def resolverURL(self, url): return re.compile( '<source src="(.+?)" type="application/x-mpegURL">').findall( self.openURL(BASE_URL + url))[0] def playVideo(self, name, url, liz=None): log('playVideo') liz = xbmcgui.ListItem(name, path=self.resolverURL(url)) liz.setMimeType('application/x-mpegURL') liz.setProperty('inputstreamaddon', 'inputstream.adaptive') liz.setProperty('inputstream.adaptive.manifest_type', 'hls') xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def addLink(self, name, u, mode, infoList=False, infoArt=False, total=0): name = name.encode("utf-8") log('addLink, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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 = name.encode("utf-8") log('addDir, name = ' + name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList == False: liz.setInfo(type="Video", infoLabels={ "mediatype": "video", "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)
class NewsOn(object): def __init__(self, sysARG=sys.argv): log('__init__, sysARG = %s' % (sysARG)) self.sysARG = sysARG self.cache = SimpleCache() self.region = self.getCoordinates() self.states = collections.Counter() self.cities = collections.Counter() def buildMenu(self): log('buildMenu') MENU = [(LANGUAGE(30004), (buildNow, )), (LANGUAGE(30005), (buildBreaking, )), (LANGUAGE(30006), (buildLocal, )), (LANGUAGE(30007), (buildStates, ))] for item in MENU: self.addDir(*item) def browse(self, opt='now'): log('browse, opt = %s' % (opt)) items = { 'now': self.getLiveNow, 'new': self.getBreakingNews, 'local': self.getLocalStations, 'states': self.getStates }[opt]() if opt in ['states', 'cities']: if opt == 'states': if self.poolList(self.buildStates, items.get('states', [])): for state in self.states.keys(): self.addDir(state, (buildCities, state), infoArt={ "thumb": FAN_URL % (state), "poster": LOGO_URL % (state), "fanart": FANART, "icon": ICON, "logo": ICON }) else: if not (list(set(self.poolList(self.buildChannel, items, opt)))): self.addDir(LANGUAGE(30008), (buildMenu, )) def browseCities(self, state): log('browseCities, state = %s' % (state)) if self.poolList(self.buildStates, self.getStates().get('states', [])): if self.poolList(self.buildCities, self.states): for city in self.cities.get(state, []): self.addDir(city, (buildCity, state, encodeString(city)), infoArt={ "thumb": FAN_URL % (city), "poster": LOGO_URL % (city), "fanart": self.getMAP(city), "icon": ICON, "logo": ICON }) def browseChannels(self, state, city): city = decodeString(city) log('browseChannels, state = %s, city = %s' % (state, city)) if self.poolList(self.buildStates, self.getStates().get('states', [])): if self.poolList(self.buildCities, self.states): self.poolList(self.buildChannel, self.cities.get(state, {}).get(city, []), 'channels') def browseStation(self, chid, opt=None): log('browseStation, chid = %s' % (chid)) data = self.getStationDetails(chid) channel = data.get('channel', {}) chname = channel.get('name', '') chlogo = (channel.get('icon', '') or LOGO_URL % (chname)) if opt is None: for key in data.keys(): self.addDir(key.title(), (browseDetails, chid, key), infoArt={ "thumb": chlogo, "poster": chlogo, "fanart": FANART, "icon": ICON, "logo": ICON }) else: items = data.get(opt, []) if opt != 'programs': items = [items] self.poolList(self.buildChannel, items, opt) def buildStates(self, state): for city in state.get('cities', []): for channel in city.get('channels', []): locations = channel.get('configValue', {}).get('locations', []) if not locations: continue self.states[locations[0]['state']] = state.get('cities', []) def buildCities(self, state): self.cities[state] = {} cities = self.states[state] for city in cities: for channel in city.get('channels', []): locations = channel.get('configValue', {}).get('locations', []) if not locations: continue self.cities[state][locations[0]['city']] = city.get( 'channels', []) def buildChannel(self, data): item, opt = data if not item.get('live', False) and opt in ['now']: return None chid = item['id'] chname = item['name'] chdesc = (item.get('description', '') or xbmc.getLocalizedString(161)) chlogo = (item.get('icon', '') or ICON) configValue = item.get('configValue', {}) latest = item.get('latest', {} or item) name = (latest.get('name', '' or chname)) if chname != name: label = '%s: [B]%s[/B]' % (chname, name) else: label = chname plot = (latest.get('description', '') or chdesc) url = (latest.get('streamUrl', '') or chid) thumb = (latest.get('thumbnailUrl', '') or chlogo) try: starttime = datetime.datetime.fromtimestamp( float(latest['startTime'])) endtime = datetime.datetime.fromtimestamp(float(latest['endTime'])) duration = (endtime - starttime).seconds except: duration = 0 infoLabel = { "mediatype": "video", "label": label, "title": label, "plot": plot, "duration": duration, "genre": ['News'] } infoArt = { "thumb": thumb, "poster": thumb, "fanart": self.getMAP((configValue.get('latitude', 'undefined'), configValue.get('longitude', 'undefined'))), "icon": chlogo, "logo": chlogo } if opt == 'local': infoArt['fanart'] = thumb if opt == 'channels': self.addDir(chname, (buildStation, chid), infoArt={ "thumb": chlogo, "poster": chlogo, "fanart": FANART, "icon": ICON, "logo": ICON }) return True else: self.addLink(label, (playURL, encodeString(url)), infoList=infoLabel, infoArt=infoArt) return True @use_cache(1) def getCoordinates(self): log('getCoordinates') return (self.openURL(BASE_API + '/getCoordinates')) def getLiveNow(self): log('getLiveNow') return (self.openURL(BASE_API + '/liveNow/999/{lat}/{lon}'.format( lat=self.region.get('latitude', 'undefined'), lon=self.region.get('longitude', 'undefined')))) def getBreakingNews(self): log('getBreakingNews') return (self.openURL(BASE_API + '/breakingNews/{lat}/{lon}'.format( lat=self.region.get('latitude', 'undefined'), lon=self.region.get('longitude', 'undefined'))))[0] def getStates(self): log('getStates') return (self.openURL(BASE_API + '/getStates/{lat}/{lon}'.format( lat=self.region.get('latitude', 'undefined'), lon=self.region.get('longitude', 'undefined')))) def getLocalStations(self, page=25): log('getLocalStations') return ( self.openURL(BASE_API + '/localStations/{page}/{lat}/{lon}'.format( page=page, lat=self.region.get('latitude', 'undefined'), lon=self.region.get('longitude', 'undefined')))) def getStationDetails(self, id): log('getStationDetails') return ( self.openURL(BASE_API + '/stationDetails/{chid}/{lat}/{lon}'.format( chid=id, lat=self.region.get('latitude', 'undefined'), lon=self.region.get('longitude', 'undefined')))) def getMAP(self, args): try: map = self.cache.get(ADDON_NAME + '.getMAPs, args = %s' % str(args)) if not map: if len(args) == 2: map = MAP_URL % (APIKEY, '%s,%s' % (tuple(args))) else: map = MAP_URL % (APIKEY, urllib.parse.quote(args)) self.cache.set(ADDON_NAME + '.getMAPs, args = %s' % str(args), map, expiration=datetime.timedelta(days=28)) return map except: return FANART def openURL(self, url, param={}, life=datetime.timedelta(minutes=15)): try: log('openURL, url = %s' % (url)) cacheName = '%s.openURL, url = %s.%s' % (ADDON_NAME, url, json.dumps(param)) cacheresponse = self.cache.get(cacheName) if not cacheresponse: req = requests.get( url, param, headers={ 'Accept-Encoding': 'gzip', 'User-Agent': 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' }) try: cacheresponse = json.loads( gzip.GzipFile(fileobj=StringIO(req.content))) except: cacheresponse = req.json() req.close() self.cache.set(cacheName, json.dumps(cacheresponse), checksum=len(json.dumps(cacheresponse)), expiration=life) return cacheresponse else: return json.loads(cacheresponse) except Exception as e: log("openURL Failed! %s" % (e), xbmc.LOGERROR) xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30001), ICON, 4000) return {} def playVideo(self, url, opt='live'): url = decodeString(url) log('playVideo, url = %s, opt = %s' % (url, opt)) liz = xbmcgui.ListItem(path=url) liz.setProperty('IsPlayable', 'true') liz.setProperty('IsInternetStream', 'true') xbmcplugin.setResolvedUrl(ROUTER.handle, True, liz) def poolList(self, method, items=None, args=None, chunk=25): log("poolList") results = [] if ENABLE_POOL: pool = ThreadPool(CORES) if args is not None: results = pool.map(method, zip(items, repeat(args))) elif items: results = pool.map(method, items) #, chunksize=chunk) pool.close() pool.join() else: if args is not None: results = [method((item, args)) for item in items] elif items: results = [method(item) for item in items] return filter(None, results) def addPlaylist(self, name, path='', infoList={}, infoArt={}, infoVideo={}, infoAudio={}, infoType='video'): log('addPlaylist, name = %s' % name) def addLink(self, name, uri=(''), infoList={}, infoArt={}, infoVideo={}, infoAudio={}, infoType='video', total=0): log('addLink, name = %s' % name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'true') liz.setProperty('IsInternetStream', 'true') if infoList: liz.setInfo(type=infoType, infoLabels=infoList) else: liz.setInfo(type=infoType, infoLabels={ "mediatype": infoType, "label": name, "title": name }) if infoArt: liz.setArt(infoArt) else: liz.setArt({'thumb': ICON, 'fanart': FANART}) if infoVideo: liz.addStreamInfo('video', infoVideo) if infoAudio: liz.addStreamInfo('audio', infoAudio) if infoList.get('favorite', None) is not None: liz = self.addContextMenu(liz, infoList) xbmcplugin.addDirectoryItem(ROUTER.handle, ROUTER.url_for(*uri), liz, isFolder=False, totalItems=total) def addDir(self, name, uri=(''), infoList={}, infoArt={}, infoType='video'): log('addDir, name = %s' % name) liz = xbmcgui.ListItem(name) liz.setProperty('IsPlayable', 'false') if infoList: liz.setInfo(type=infoType, infoLabels=infoList) else: liz.setInfo(type=infoType, infoLabels={ "mediatype": infoType, "label": name, "title": name }) if infoArt: liz.setArt(infoArt) else: liz.setArt({'thumb': ICON, 'fanart': FANART}) if infoList.get('favorite', None) is not None: liz = self.addContextMenu(liz, infoList) xbmcplugin.addDirectoryItem(ROUTER.handle, ROUTER.url_for(*uri), liz, isFolder=True) def addContextMenu(self, liz, infoList={}): log('addContextMenu') return liz def run(self): ROUTER.run() xbmcplugin.setContent(ROUTER.handle, CONTENT_TYPE) xbmcplugin.addSortMethod(ROUTER.handle, xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(ROUTER.handle, xbmcplugin.SORT_METHOD_NONE) xbmcplugin.addSortMethod(ROUTER.handle, xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.addSortMethod(ROUTER.handle, xbmcplugin.SORT_METHOD_TITLE) xbmcplugin.endOfDirectory(ROUTER.handle, cacheToDisc=DISC_CACHE)