Repository URL to install this package:
|
Version:
2+git3694+d6785c5-r4 ▾
|
enigma2-plugin-extensions-e2iplayer
/
usr
/
lib
/
enigma2
/
python
/
Plugins
/
Extensions
/
IPTVPlayer
/
hosts
/
hostspiegeltv.py
|
|---|
# -*- coding: utf-8 -*-
###################################################
# LOCAL import
###################################################
from Plugins.Extensions.IPTVPlayer.components.iptvplayerinit import TranslateTXT as _
from Plugins.Extensions.IPTVPlayer.components.ihost import CHostBase, CBaseHostClass
from Plugins.Extensions.IPTVPlayer.tools.iptvtools import printDBG, printExc, MergeDicts
from Plugins.Extensions.IPTVPlayer.tools.iptvtypes import strwithmeta
from Plugins.Extensions.IPTVPlayer.libs.urlparserhelper import getDirectM3U8Playlist, getMPDLinksWithMeta
from Plugins.Extensions.IPTVPlayer.tools.e2ijs import js_execute
from Plugins.Extensions.IPTVPlayer.libs.e2ijson import loads as json_loads
###################################################
###################################################
# FOREIGN import
###################################################
import urlparse
import time
import re
import hashlib
import urllib
import random
from datetime import datetime
###################################################
###################################################
# Config options for HOST
###################################################
def GetConfigList():
optionList = []
return optionList
###################################################
def gettytul():
return 'http://spiegel.tv/'
class SpiegelTv(CBaseHostClass):
def __init__(self):
CBaseHostClass.__init__(self, {'history':'spiegel.tv', 'cookie':'spiegel.tv.cookie'})
self.USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0'
self.MAIN_URL = 'http://www.spiegel.tv/'
self.DEFAULT_ICON_URL = 'https://images-na.ssl-images-amazon.com/images/I/31bnL4xLAkL.png'
self.HTTP_HEADER = {'User-Agent': self.USER_AGENT, 'DNT':'1', 'Accept': 'text/html', 'Accept-Encoding':'gzip, deflate', 'Referer':self.getMainUrl(), 'Origin':self.getMainUrl()}
self.AJAX_HEADER = dict(self.HTTP_HEADER)
self.AJAX_HEADER.update( {'X-Requested-With': 'XMLHttpRequest', 'Accept-Encoding':'gzip, deflate', 'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8', 'Accept':'application/json, text/javascript, */*; q=0.01'} )
self.cacheLinks = {}
self.defaultParams = {'header':self.HTTP_HEADER, 'use_cookie': True, 'load_cookie': True, 'save_cookie': True, 'cookiefile': self.COOKIE_FILE}
self.oneconfig = {'client_id':'748'}
def getPage(self, baseUrl, addParams = {}, post_data = None):
if addParams == {}: addParams = dict(self.defaultParams)
return self.cm.getPage(baseUrl, addParams, post_data)
def getFullIconUrl(self, icon, baseUrl=None):
return CBaseHostClass.getFullIconUrl(self, icon.replace('.webp', '.jpg'), baseUrl)
def listMainMenu(self, cItem):
printDBG("SpiegelTv.listMainMenu")
sts, data = self.getPage(self.getMainUrl())
if sts:
data = self.cm.ph.getDataBeetwenNodes(data, ('<nav', '>', 'innerheader'), ('</nav', '>'))[1]
data = re.compile('(<li[^>]*?>|</li>|<ul[^>]*?>|</ul>)').split(data)
if len(data) > 1:
try:
cTree = self.listToDir(data[1:-1], 0)[0]
params = dict(cItem)
params['c_tree'] = cTree['list'][0]
params['category'] = 'list_categories'
self.listCategories(params, 'list_items')
except Exception:
printExc()
MAIN_CAT_TAB = [{'category':'search', 'title': _('Search'), 'search_item':True},
{'category':'search_history', 'title': _('Search history')},]
params = dict(cItem)
params.update({'type':'category', 'good_for_fav':False, 'category':'list_main_items', 'title':_('Main'), 'url':self.getMainUrl()})
self.currList.insert(0, params)
self.listsTab(MAIN_CAT_TAB, cItem)
def listCategories(self, cItem, nextCategory):
printDBG("SpiegelTv.listCategories")
try:
cTree = cItem['c_tree']
for item in cTree['list']:
title = self.cleanHtmlStr(item['dat']) #self.cm.ph.getDataBeetwenNodes(item['dat'], ('<div', '>', 'title'), ('</div', '>'))[1]
url = self.getFullUrl(self.cm.ph.getSearchGroups(item['dat'], '''href=['"]([^'^"]+?)['"]''')[0])
if 'livestreams' in url:
params = dict(cItem)
params.update({'good_for_fav':False, 'category':nextCategory, 'title':title, 'url':url})
self.addDir(params)
elif 'list' not in item:
if self.cm.isValidUrl(url) and title != '':
params = dict(cItem)
params.update({'good_for_fav':False, 'category':nextCategory, 'title':title, 'url':url})
self.addDir(params)
elif len(item['list']) == 1 and title != '':
params = dict(cItem)
params.update({'good_for_fav':False, 'c_tree':item['list'][0], 'title':title, 'url':url})
self.addDir(params)
except Exception:
printExc()
def listMainItems(self, cItem, nextCategory):
printDBG("SpiegelTv.listMainItems [%s]" % cItem)
sts, data = self.getPage(cItem['url'])
if not sts: return
data = self.cm.ph.getAllItemsBeetwenNodes(data, ('<h2', '>', 'h1'), ('<div', '>', 'cleared'))
for item in data:
icon = self.getFullIconUrl(self.cm.ph.getSearchGroups(item, '''\ssrc=['"]([^'^"]+?)['"]''', 1, True)[0])
title = self.cleanHtmlStr(self.cm.ph.getDataBeetwenNodes(item, ('<div', '>', 'header-text'), ('</div', '>'))[1])
method = self.cm.ph.getSearchGroups(item, '''data\-navigateto=['"]([^'^"]+?)['"]''')[0]
param = self.cm.ph.getSearchGroups(item, '''data\-navigateparam=['"]([^'^"]+?)['"]''')[0]
url = self.getFullUrl('/%s/%s' % (method, param))
params = dict(cItem)
params.update({'good_for_fav':True, 'category':nextCategory, 'title':title, 'url':url, 'icon':icon, 'f_method':method, 'f_param':param})
self.addDir(params)
def _fillOneConfig(self, cItem, data=None):
if data == None:
sts, data = self.getPage(cItem['url'])
if not sts: return
jscode = []
data = self.cm.ph.getAllItemsBeetwenNodes(data, ('<script', '>'), ('</script', '>'), False)
for item in data:
if '_oneconfig' in item:
jscode.append(item)
break
jscode.append('print(JSON.stringify(_oneconfig));')
ret = js_execute( '\n'.join(jscode) )
if ret['sts'] and 0 == ret['code']:
try:
self.oneconfig.update(json_loads(ret['data'].strip(), '', True))
except Exception:
printExc()
def _initiSession(self, cItem):
try:
deviceId = '%d:%d:%d%d' % (random.randint(1, 4), int(time.time()), random.randint(1e4, 99999), random.randint(1, 9))
urlParams = dict(self.defaultParams)
urlParams['header'] = dict(self.AJAX_HEADER)
urlParams['header'].update({'Referer':cItem['url'], 'X-Request-Enable-Auth-Fallback':'1'})
post_data = {'nxp_devh': deviceId,
'nxp_userh': '',
'precid': '0',
'playlicense': '0',
'screenx': '1920',
'screeny': '1080',
'playerversion': '6.0.00',
'gateway': self.oneconfig['gw'],
'adGateway': '',
'explicitlanguage': self.oneconfig['language'],
'addTextTemplates': '1',
'addDomainData': '1',
'addAdModel': '1',
}
url = 'https://api.nexx.cloud/v3/%s/session/init' % (self.oneconfig['client_id'], )
sts, data = self.getPage(url, urlParams, post_data)
if not sts: return
self.oneconfig['session_data'] = json_loads(data, '', True)['result']
self.oneconfig['session_data']['device_id'] = deviceId
except Exception:
printExc()
def listLiveVideos(self, cItem):
playlistId = cItem['url'].split('/livestreams/', 1)[-1].split('-')[0]
self._fillOneConfig(cItem)
self._initiSession(cItem)
if playlistId == '':
return
try:
cid = self.oneconfig['session_data']['general']['cid']
clientToken = self.oneconfig['session_data']['device']['clienttoken']
clientId = self.oneconfig['session_data']['general']['clid']
deviceId = self.oneconfig['session_data']['device_id']
secret = clientToken[int(deviceId[0]):]
secret = secret[0:len(secret) - int(deviceId[-1])]
op = 'byid'
requestToken = hashlib.md5(''.join((op, clientId, secret))).hexdigest()
urlParams = dict(self.defaultParams)
urlParams['header'] = dict(self.AJAX_HEADER)
urlParams['header'].update({'Referer':cItem['url'], 'X-Request-Enable-Auth-Fallback':'1', 'X-Request-CID':cid, 'X-Request-Token':requestToken})
post_data = {'additionalfields': 'language,channel,actors,studio,licenseby,slug,fileversion,subtitle,teaser,description,releasedate',
'addInteractionOptions': '1',
'addStatusDetails': '1',
'addStreamDetails': '1',
'addFeatures': '1',
'addCaptions': '1',
'addScenes': '1',
'addHotSpots': '1',
'addBumpers': '1',
'captionFormat': 'data',
'addItemData': '1',}
url = 'https://api.nexx.cloud/v3/%s/playlists/%s/%s' % (self.oneconfig['client_id'], op, playlistId)
sts, data = self.getPage(url, urlParams, post_data)
data = json_loads(data)
for item in data['result']['itemdata']:
icon = item['imagedata']['thumb']
url = self.getFullUrl('/videos/%s-%s' % (item['general']['ID'], item['general']['slug']))
title = self.cleanHtmlStr(item['general']['title'])
# item['general']['year']
date = datetime.fromtimestamp(int(item['general']['releasedate'])).strftime('%d.%m.%Y')
desc = ['%s | %s | %s' % (item['general']['runtime'], item['general']['studio'], date)]
desc.append(self.cleanHtmlStr(item['general']['subtitle']))
desc.append(self.cleanHtmlStr(item['general']['teaser']))
desc.append(self.cleanHtmlStr(item['general']['description']))
params = {'good_for_fav':True, 'title':title, 'url':url, 'icon':icon, 'desc':'[/br]'.join(desc)}
self.addVideo(params)
except Exception:
printExc()
def listItems(self, cItem):
printDBG("SpiegelTv.listItems [%s]" % cItem)
if '/videos/' in cItem.get('url', ''):
sts, data = self.getPage(cItem['url'])
if sts and 'playerholder' in data:
data = self.cm.ph.getDataBeetwenNodes(data, ('<section', '>', 'innercontent'), ('<span', '>', 'vtiledetails'))[1]
title = self.cleanHtmlStr(self.cm.ph.getDataBeetwenMarkers(data, '<h1', '</h1>')[1])
icon = self.getFullIconUrl(self.cm.ph.getSearchGroups(data, '''<img[^>]*?\ssrc=['"]([^'^"]+?)['"]''', 1, True)[0])
if icon:
desc = []
tmp = self.cleanHtmlStr(self.cm.ph.getDataBeetwenMarkers(data, '<h2', '</h2>')[1])
if tmp: desc.append(tmp)
tmp = self.cm.ph.getAllItemsBeetwenNodes(data, ('<div', '>', 'vreddesc'), ('</div', '>'), False)
for t in tmp:
t = self.cleanHtmlStr(t)
if t: desc.append(t)
self.addVideo(MergeDicts(cItem, {'title':title, 'icon':icon, 'desc':'[/br]'.join(desc)}))
return
page = cItem.get('page', 0)
if page == 0:
if cItem.get('url', '').endswith('-livestream'):
self.listLiveVideos(cItem)
self._fillOneConfig(cItem)
urlPath = urlparse.urlparse(cItem['url']).path[1:].split('/')
method = cItem.get('f_method', urlPath[0])
param = cItem.get('f_param', urlPath[-1])
start = 0
else:
method = cItem.get('f_method', '')
param = cItem.get('f_param', '')
start = cItem.get('f_start', page*20)
try:
url = self.getFullUrl('/gateway/service.php')
post_data = {'cid':self.oneconfig['cid'], 'client':self.oneconfig['client_id'], 'method':method, 'param':param, 'start':start, 'cgw':self.oneconfig['gw'], 'isu':'0', 'uhs':'0', 'agc':'0', 'wbp':'0', 'cdlang':self.oneconfig['language']}
sts, data = self.getPage(url, post_data=post_data)
if not sts: return
data = json_loads(data)['contents']
printDBG(data)
nextPageParams = []
tmp = self.cm.ph.getDataBeetwenMarkers(data, 'navigatemore(', ')', False)[1].split(',')
for item in tmp:
nextPageParams.append(item.strip()[1:-1])
data = self.cm.ph.getAllItemsBeetwenMarkers(data.split('</h1>', 1)[-1], '<a', '</a>')
for item in data:
url = self.getFullUrl(self.cm.ph.getSearchGroups(item, '''\shref=['"]([^'^"]+?)['"]''', 1, True)[0])
icon = self.getFullIconUrl(self.cm.ph.getSearchGroups(item, '''\ssrc=['"]([^'^"]+?)['"]''', 1, True)[0])
title = ''
desc = []
tmp = self.cm.ph.getAllItemsBeetwenMarkers(item, '<div', '</div>')
for it in tmp:
t = self.cleanHtmlStr(it)
if t == '': continue
if 'cardtitle' in it: title = t
if 'tholderbottom' in it: desc.insert(0, t)
else: desc.append(t)
params = {'good_for_fav':True, 'category':cItem['category'], 'title':title, 'url':url, 'icon':icon, 'desc':'[/br]'.join(desc)}
if '/videos/' in url: self.addVideo(params)
else: self.addDir(params)
if 3 == len(nextPageParams):
params = dict(cItem)
params.update({'good_for_fav':False, 'title':_("Next page"), 'page':page+1, 'f_method':nextPageParams[0], 'f_param':nextPageParams[1], 'f_start':nextPageParams[2]})
self.addDir(params)
except Exception:
printExc()
def listSearchResult(self, cItem, searchPattern, searchType):
printDBG("SpiegelTv.listSearchResult cItem[%s], searchPattern[%s] searchType[%s]" % (cItem, searchPattern, searchType))
cItem = dict(cItem)
cItem['url'] = self.getFullUrl('/search/') + urllib.quote_plus(searchPattern)
cItem['category'] = 'list_items'
cItem['f_method'] = 'search'
cItem['f_param'] = searchPattern
self.listItems(cItem)
def getLinksForVideo(self, cItem):
printDBG("SpiegelTv.getLinksForVideo [%s]" % cItem)
cacheKey = cItem['url']
cacheTab = self.cacheLinks.get(cacheKey, [])
if len(cacheTab): return cacheTab
self.cacheLinks = {}
retTab = []
sts, data = self.getPage(cItem['url'])
if not sts: return
self._fillOneConfig(cItem, data)
self._initiSession(cItem)
videoId = self.cm.ph.getDataBeetwenMarkers(data, 'video.start(', ')', False)[1].split(',')[0].strip()
if videoId == '':
videoId = cItem['url'].split('/videos/', 1)[-1].split('-')[0]
try:
cid = self.oneconfig['session_data']['general']['cid']
clientToken = self.oneconfig['session_data']['device']['clienttoken']
clientId = self.oneconfig['session_data']['general']['clid']
deviceId = self.oneconfig['session_data']['device_id']
secret = clientToken[int(deviceId[0]):]
secret = secret[0:len(secret) - int(deviceId[-1])]
op = 'byid'
requestToken = hashlib.md5(''.join((op, clientId, secret))).hexdigest()
urlParams = dict(self.defaultParams)
urlParams['header'] = dict(self.AJAX_HEADER)
urlParams['header'].update({'Referer':cItem['url'], 'X-Request-Enable-Auth-Fallback':'1', 'X-Request-CID':cid, 'X-Request-Token':requestToken})
post_data = {'additionalfields': 'language,channel,actors,studio,licenseby,slug,subtitle,teaser,description',
'addInteractionOptions': '1',
'addStatusDetails': '1',
'addStreamDetails': '1',
'addCaptions': '1',
'addScenes': '1',
'addHotSpots': '1',
'addBumpers': '1',
'captionFormat': 'data',}
url = 'https://api.nexx.cloud/v3/%s/videos/%s/%s' % (clientId, op, videoId)
sts, data = self.getPage(url, urlParams, post_data)
if not sts: return
data = json_loads(data, '', True)['result']
try: protectionToken = data['protectiondata']['token']
except Exception: protectionToken = None
language = data['general'].get('language_raw') or ''
printDBG(data)
cdn = data['streamdata']['cdnType']
if cdn == 'azure':
data = data['streamdata']
azureLocator = data['azureLocator']
AZURE_URL = 'http://nx%s%02d.akamaized.net/'
def getCdnShieldBase(shieldType='', prefix='-p'):
for secure in ('', 's'):
cdnShield = data.get('cdnShield%sHTTP%s' % (shieldType, secure.upper()))
if cdnShield:
return 'http%s://%s' % (secure, cdnShield)
else:
return AZURE_URL % (prefix, int(data['azureAccount'].replace('nexxplayplus', '')))
azureStreamBase = getCdnShieldBase()
isML = ',' in language
azureManifestUrl = '%s%s/%s_src%s.ism/Manifest' % (azureStreamBase, azureLocator, videoId, ('_manifest' if isML else '')) + '%s'
if protectionToken:
azureManifestUrl += '?hdnts=%s' % protectionToken
try:
azureProgressiveBase = getCdnShieldBase('Prog', '-d')
azureFileDistribution = data.get('azureFileDistribution')
if azureFileDistribution:
fds = azureFileDistribution.split(',')
if fds:
for fd in fds:
ss = fd.split(':')
if len(ss) != 2: continue
tbr = int(ss[0] or 0)
if not tbr: continue
retTab.append({'name':'[%s] %s' % (tbr, ss[1]), 'tbr':tbr, 'url': '%s%s/%s_src_%s_%d.mp4' % (azureProgressiveBase, azureLocator, videoId, ss[1], tbr)})
except Exception:
printExc()
retTab.sort(key=lambda item: item['tbr'], reverse=True)
if len(retTab) == 0:
retTab = getMPDLinksWithMeta(azureManifestUrl % '(format=mpd-time-csf)', checkExt=False, sortWithMaxBandwidth=999999999)
if len(retTab) == 0:
retTab = getDirectM3U8Playlist(azureManifestUrl % '(format=m3u8-aapl)', checkExt=False, checkContent=True, sortWithMaxBitrate=999999999)
else:
streamData = data['streamdata']
hash = data['general']['hash']
ps = streamData['originalDomain']
if streamData['applyFolderHierarchy'] == '1':
s = ('%04d' % int(videoId))[::-1]
ps += '/%s/%s' % (s[0:2], s[2:4])
ps += '/%s/%s_' % (videoId, hash)
t = 'http://%s' + ps
azureFileDistribution = streamData['azureFileDistribution'].split(',')
cdnProvider = streamData['cdnProvider']
def p0(p):
return '_%s' % p if streamData['applyAzureStructure'] == '1' else ''
formats = []
if cdnProvider == 'ak':
t += ','
for i in azureFileDistribution:
p = i.split(':')
t += p[1] + p0(int(p[0])) + ','
t += '.mp4.csmil/master.%s'
elif cdnProvider == 'ce':
k = t.split('/')
h = k.pop()
httpBase = t = '/'.join(k)
httpBase = httpBase % streamData['cdnPathHTTP']
t += '/asset.ism/manifest.%s?dcp_ver=aos4&videostream='
for i in azureFileDistribution:
p = i.split(':')
tbr = int(p[0])
filename = '%s%s%s.mp4' % (h, p[1], p0(tbr))
retTab.append({'name':'[%s] %s' % (tbr, p[1]), 'tbr':tbr, 'url': httpBase + '/' + filename})
a = filename + ':%s' % (tbr * 1000)
t += a + ','
t = t[:-1] + '&audiostream=' + a.split(':')[0]
else:
printDBG("Unknwon cdnProvider [%s]" % cdnProvider)
assert False
retTab.sort(key=lambda item: item['tbr'], reverse=True)
if len(retTab) == 0:
retTab.extend( getMPDLinksWithMeta(t % (streamData['cdnPathDASH'], 'mpd'), checkExt=False, sortWithMaxBandwidth=999999999) )
if len(retTab) == 0:
retTab.extend( getDirectM3U8Playlist(t % (streamData['cdnPathHLS'], 'm3u8'), checkExt=False, checkContent=True, sortWithMaxBitrate=999999999) )
except Exception:
printExc()
if len(retTab):
self.cacheLinks[cacheKey] = retTab
for idx in range(len(retTab)):
retTab[idx]['need_resolve'] = 1
return retTab
def getVideoLinks(self, baseUrl):
printDBG("SpiegelTv.getVideoLinks [%s]" % baseUrl)
videoUrl = strwithmeta(baseUrl)
urlTab = []
# mark requested link as used one
if len(self.cacheLinks.keys()):
for key in self.cacheLinks:
for idx in range(len(self.cacheLinks[key])):
if videoUrl in self.cacheLinks[key][idx]['url']:
if not self.cacheLinks[key][idx]['name'].startswith('*'):
self.cacheLinks[key][idx]['name'] = '*' + self.cacheLinks[key][idx]['name'] + '*'
break
return [{'name':'sel', 'url':videoUrl}]
def handleService(self, index, refresh = 0, searchPattern = '', searchType = ''):
printDBG('handleService start')
CBaseHostClass.handleService(self, index, refresh, searchPattern, searchType)
name = self.currItem.get("name", '')
category = self.currItem.get("category", '')
mode = self.currItem.get("mode", '')
printDBG( "handleService: |||| name[%s], category[%s] " % (name, category) )
self.cacheLinks = {}
self.currList = []
#MAIN MENU
if name == None:
self.listMainMenu({'name':'category'})
elif category == 'list_main_items':
self.listMainItems(self.currItem, 'list_items')
elif category == 'list_categories':
self.listCategories(self.currItem, 'list_items')
elif category == 'list_items':
self.listItems(self.currItem)
#SEARCH
elif category in ["search", "search_next_page"]:
cItem = dict(self.currItem)
cItem.update({'search_item':False, 'name':'category'})
self.listSearchResult(cItem, searchPattern, searchType)
#HISTORIA SEARCH
elif category == "search_history":
self.listsHistory({'name':'history', 'category': 'search'}, 'desc', _("Type: "))
else:
printExc()
CBaseHostClass.endHandleService(self, index, refresh)
class IPTVHost(CHostBase):
def __init__(self):
CHostBase.__init__(self, SpiegelTv(), True, [])