Skip to content

Commit

Permalink
direct URI with htmlscraper #108
Browse files Browse the repository at this point in the history
bundesland streams working but need more testing
uhd added
  • Loading branch information
s0faking committed Dec 6, 2022
1 parent 73d72b9 commit 84396b0
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 59 deletions.
36 changes: 32 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,46 @@ Simple IPTV Integration

Playlist Content
```
#EXTINF:-1 tvg-name="ORF 1" tvg-id="ORF 1" group-title="ORF",orf1
#EXTINF:-1 tvg-name="ORF 1" tvg-id="orf1" group-title="ORF",ORF 1
plugin://plugin.video.orftvthek/?channel=orf1&mode=pvr
#EXTINF:-1 tvg-name="ORF 2" tvg-id="ORF 2" group-title="ORF",orf2
#EXTINF:-1 tvg-name="ORF 2" tvg-id="orf2" group-title="ORF",ORF 2
plugin://plugin.video.orftvthek/?channel=orf2&mode=pvr
#EXTINF:-1 tvg-name="ORF 3" tvg-id="ORF 3" group-title="ORF",orf3
#EXTINF:-1 tvg-name="ORF 3" tvg-id="orf3" group-title="ORF",ORF 3
plugin://plugin.video.orftvthek/?channel=orf3&mode=pvr
#EXTINF:-1 tvg-name="ORF Sport+" tvg-id="ORF Sport+" group-title="ORF",orfs
#EXTINF:-1 tvg-name="ORF Sport+" tvg-id="orfs" group-title="ORF",ORF Sport+
plugin://plugin.video.orftvthek/?channel=orfs&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Burgenland" tvg-id="orf2b" group-title="ORF",ORF 2 Burgenland
plugin://plugin.video.orftvthek/?channel=orf2b&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Steiermark" tvg-id="orf2stmk" group-title="ORF",ORF 2 Steiermark
plugin://plugin.video.orftvthek/?channel=orf2stmk&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Wien" tvg-id="orf2w" group-title="ORF",ORF 2 Wien
plugin://plugin.video.orftvthek/?channel=orf2w&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Oberösterreich" tvg-id="orf2ooe" group-title="ORF",ORF 2 Oberösterreich
plugin://plugin.video.orftvthek/?channel=orf2ooe&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Kärnten" tvg-id="orf2k" group-title="ORF",ORF 2 Kärnten
plugin://plugin.video.orftvthek/?channel=orf2k&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Niederösterreich" tvg-id="orf2n" group-title="ORF",ORF 2 Niederösterreich
plugin://plugin.video.orftvthek/?channel=orf2n&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Salzburg" tvg-id="orf2s" group-title="ORF",ORF 2 Salzburg
plugin://plugin.video.orftvthek/?channel=orf2s&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Vorarlberg" tvg-id="orf2v" group-title="ORF",ORF 2 Vorarlberg
plugin://plugin.video.orftvthek/?channel=orf2v&mode=pvr
#EXTINF:-1 tvg-name="ORF 2 Tirol" tvg-id="orf2t" group-title="ORF",ORF 2 Tirol
plugin://plugin.video.orftvthek/?channel=orf2t&mode=pvr
```


Expand Down
7 changes: 3 additions & 4 deletions addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.orftvthek" name="ORF TVthek" version="0.12.7+matrix.1" provider-name="sofaking">
<addon id="plugin.video.orftvthek" name="ORF TVthek" version="0.12.8+matrix.1" provider-name="sofaking">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.future" version="0.16.0"/>
Expand All @@ -26,9 +26,8 @@
<icon>resources/icon.png</icon>
<fanart>resources/fanart.jpg</fanart>
</assets>
<news>v0.12.7 (25/11/2022)
[fix] fixed url parameters for drm live streams
[feature] added UHD 50fps option for livestreams
<news>v0.12.8 (06/12/2022)
[feature] added direct uri for simple iptv (livestreams)
</news>
</extension>
</addon>
3 changes: 3 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
* 0.12.8
- added direct uri for simple iptv (livestreams) #108

* 0.12.7
- fixed url parameters for drm livestreams
- added UHD 50fps option for livestreams
Expand Down
65 changes: 41 additions & 24 deletions resources/lib/Addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,33 +269,50 @@ def run():
channel = params.get('channel')
debugLog("Loading channel %s" % channel)
data = scraper.getLivestreamByChannel(channel)
video_url = "%s|User-Agent=%s" % (data['url'], Settings.userAgent())
if data:
video_url = "%s|User-Agent=%s" % (data['url'], Settings.userAgent())

if 'license' in data:
import inputstreamhelper
license = data['license']
print(data)
is_helper = inputstreamhelper.Helper(input_stream_protocol, drm=input_stream_drm_version)
if is_helper.check_inputstream():
debugLog("Video Url: %s" % video_url)
debugLog("DRM License Url: %s" % license)
play_item = xbmcgui.ListItem(path=video_url)
play_item.setProperty('IsPlayable', 'true')
headers = "User-Agent=%s&Content-Type=%s" % (Settings.userAgent(), input_stream_lic_content_type)
if 'license' in data:
import inputstreamhelper
license = data['license']

play_item.setContentLookup(False)
play_item.setMimeType(input_stream_mime)
play_item.setProperty('inputstream.adaptive.stream_headers', headers)
play_item.setProperty('inputstream', is_helper.inputstream_addon)
play_item.setProperty('inputstream.adaptive.manifest_type', input_stream_protocol)
play_item.setProperty('inputstream.adaptive.license_type', input_stream_drm_version)
play_item.setProperty('inputstream.adaptive.license_key', license + '|' + headers + '|R{SSM}|')
xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=play_item)
is_helper = inputstreamhelper.Helper(input_stream_protocol, drm=input_stream_drm_version)
if is_helper.check_inputstream():
debugLog("Video Url: %s" % video_url)
debugLog("DRM License Url: %s" % license)
play_item = xbmcgui.ListItem(path=video_url)
play_item.setLabel(data['title'])
play_item.setLabel2(channel)
play_item.setProperty('IsPlayable', 'true')
item_infos = {
'title': data['title'],
'plot': data['description'],
'plotoutline': data['description'],
}
play_item.setInfo(type="Video", infoLabels=item_infos)

if 'logo' in data:
item_art = {
'clearlogo': data['logo'],
'icon': data['logo'],
}
play_item.setArt(item_art)

headers = "User-Agent=%s&Content-Type=%s" % (Settings.userAgent(), input_stream_lic_content_type)

play_item.setContentLookup(False)
play_item.setMimeType(input_stream_mime)
play_item.setProperty('inputstream.adaptive.stream_headers', headers)
play_item.setProperty('inputstream', is_helper.inputstream_addon)
play_item.setProperty('inputstream.adaptive.manifest_type', input_stream_protocol)
play_item.setProperty('inputstream.adaptive.license_type', input_stream_drm_version)
play_item.setProperty('inputstream.adaptive.license_key', license + '|' + headers + '|R{SSM}|')
xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=play_item)
else:
userNotification((translation(30066)).encode("utf-8"))
else:
userNotification((translation(30066)).encode("utf-8"))
else:
play_item = xbmcgui.ListItem(path=video_url)
xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=play_item)
play_item = xbmcgui.ListItem(path=video_url)
xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=play_item)
elif sys.argv[2] == '':
getMainMenu()
else:
Expand Down
57 changes: 57 additions & 0 deletions resources/lib/HtmlScraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ class htmlScraper(Scraper):

__videoQualities = ["Q1A", "Q4A", "Q6A", "Q8C", "QXB", "QXA"]

_bundeslandMap = {
'orf2b': 'Burgenland',
'orf2stmk': 'Steiermark',
'orf2w': 'Wien',
'orf2ooe': 'Oberösterreich',
'orf2k': 'Kärnten',
'orf2n': 'Niederösterreich',
'orf2s': 'Salzburg',
'orf2v': 'Vorarlberg',
'orf2t': 'Tirol',
}

def __init__(self, xbmc, settings, pluginhandle, quality, protocol, delivery, defaultbanner, defaultbackdrop, usePlayAllPlaylist):
self.translation = settings.getLocalizedString
self.xbmc = xbmc
Expand All @@ -38,6 +50,51 @@ def __init__(self, xbmc, settings, pluginhandle, quality, protocol, delivery, de
debugLog('HTML Scraper - Init done')

def getLivestreamByChannel(self, channel):
html = fetchPage({'link': self.__urlLive})
wrapper = parseDOM(html.get("content"), name='div', attrs={'class': 'b-livestream-per-channel'})
channels = parseDOM(wrapper, name='div', attrs={'class': 'b-lane.*?'})

for result in channels:
channel_detail_url = False
channel_logo = parseDOM(result, name='img', attrs={'class': 'channel-logo'}, ret='src')
channel_id = re.findall(r'[^\/]+(?=\.)', str(channel_logo))[0].replace('-logo', '')
channel_program = parseDOM(result, name='a', attrs={'class': 'js-link-box'}, ret='title')[0]

if channel in self._bundeslandMap:
channel = 'orf2'
bundesland_article = parseDOM(result, name='li', attrs={'class': '.*?is-bundesland-heute.*?'}, ret='data-jsb')
if len(bundesland_article):
bundesland_data = replaceHTMLCodes(bundesland_article[0])
bundesland_data = json.loads(bundesland_data)
for bundesland_item_key in bundesland_data:
bundesland_item = bundesland_data.get(bundesland_item_key)
if bundesland_item and bundesland_item is not True and len(bundesland_item):
if self._bundeslandMap[channel] == bundesland_item.get('bundesland'):
channel_detail_url = bundesland_item.get('url')

if not channel_detail_url and channel_id == channel:
channel_detail_url = parseDOM(result, name='a', attrs={'class': 'js-link-box'}, ret='href')[0]

if channel_detail_url:
channel_html = fetchPage({'link': channel_detail_url})
player = parseDOM(channel_html.get("content"), name='div', attrs={'class': "player_viewport.*?"})
player_meta = parseDOM(channel_html.get("content"), name='section', attrs={'class': "b-video-details.*?"})
if len(player):
data = parseDOM(player[0], name='div', attrs={}, ret="data-jsb")
channel_description = parseDOM(player_meta[0], name='p', attrs={'class': "description-text.*?"})[0]
license_url = self.getLivestreamDRM(data)
video_url = self.getLivestreamUrl(data, self.videoQuality)
# Remove Get Parameters because InputStream Adaptive cant handle it.
video_url = re.sub(r"\?[\S]+", '', video_url, 0)

uhd_25_video_url = self.getLivestreamUrl(data, 'UHD', True)
if uhd_25_video_url:
uhd_50_video_url = uhd_25_video_url.replace('_uhd_25/', '_uhd_50/')
video_url = uhd_50_video_url
if license_url:
return {'title': channel_program, 'description': channel_description, 'url': video_url,'license': license_url}
else:
return {'title': channel_program, 'description': channel_description, 'url': video_url}
return []

def getMostViewed(self):
Expand Down
78 changes: 51 additions & 27 deletions resources/lib/ServiceApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ class serviceAPI(Scraper):
__urlChannel = 'channel/'
__urlDRMLic = 'https://drm.ors.at/acquire-license/widevine'
__brandIdDRM = '13f2e056-53fe-4469-ba6d-999970dbe549'
__bundeslandMap = {
'orf2b': 'Burgenland',
'orf2stmk': 'Steiermark',
'orf2w': 'Wien',
'orf2ooe': 'Oberösterreich',
'orf2k': 'Kärnten',
'orf2n': 'Niederösterreich',
'orf2s': 'Salzburg',
'orf2v': 'Vorarlberg',
'orf2t': 'Tirol',
}

serviceAPIEpisode = 'episode/%s'
serviceAPIDate = 'schedule/%s?limit=1000'
Expand Down Expand Up @@ -58,27 +69,33 @@ def getLivestreamByChannel(self, channel):
response_raw = response.read().decode('UTF-8')
channels = json.loads(response_raw)

live_link = False
for result in channels:
if result == channel:
if channel in self.__bundeslandMap:
channel_items = channels[result].get('items')
for channel_item in channel_items:
if channel_item.get('title') == "%s heute" % self.__bundeslandMap[channel]:
live_link = channel_item.get('_links').get('self').get('href')
if result == channel or channel in self.__bundeslandMap and result == channel[0:4] and not live_link:
live_link = channels[result].get('items')[0].get('_links').get('self').get('href')
response = url_get_request(live_link, self.httpauth)
response_raw = response.read().decode('UTF-8')
live_json = json.loads(response_raw)
print("##################")
print(live_json)
print("##################")
if live_json.get('is_drm_protected'):
video_url = self.JSONStreamingDrmURL(live_json)
license_url = self.JSONLicenseDrmURL(live_json)
print(video_url)
print(license_url)
print("########################################")
return {'url': video_url,'license': license_url}
else:
video_url = self.JSONStreamingURL(live_json.get('sources'))
print(video_url)
print("########################################")
return {'url': video_url}

if live_link:
response = url_get_request(live_link, self.httpauth)
response_raw = response.read().decode('UTF-8')
live_json = json.loads(response_raw)
if live_json.get('is_drm_protected'):
video_url = self.JSONStreamingDrmURL(live_json)
uhd_25_video_url = self.JSONStreamingDrmURL(live_json, 'uhdbrowser')
if uhd_25_video_url:
video_url = uhd_25_video_url;
uhd_50_video_url = self.JSONStreamingDrmURL(live_json, 'uhdsmarttv')
if uhd_50_video_url:
video_url = uhd_50_video_url
license_url = self.JSONLicenseDrmURL(live_json)
return {'title': live_json.get('title'), 'description': live_json.get('share_subject'), 'url': video_url,'license': license_url}
else:
video_url = self.JSONStreamingURL(live_json.get('sources'))
return {'title': live_json.get('title'), 'description': live_json.get('share_subject'), 'url': video_url}

def getHighlights(self):
try:
Expand Down Expand Up @@ -175,10 +192,17 @@ def JSONLicenseDrmURL(self, jsonData):
debugLog("DRM License Url %s" % license_url)
return license_url

def JSONStreamingDrmURL(self, jsonData):
def JSONStreamingDrmURL(self, jsonData, uhd_profile = False):
if jsonData.get('drm_token') is not None:
license_url = self.JSONLicenseDrmURL(jsonData)
jsonVideos = jsonData.get('sources')

if uhd_profile:
for streamingUrl in jsonVideos.get('dash'):
if streamingUrl.get('is_uhd') and streamingUrl.get('quality_key').lower() == uhd_profile:
source = re.sub(r"\?[\S]+", '', streamingUrl.get('src'), 0)
return generateDRMVideoUrl(source, license_url)

for streamingUrl in jsonVideos.get('dash'):
if streamingUrl.get('quality_key').lower()[0:3] == self.videoQuality:
return generateDRMVideoUrl(streamingUrl.get('src'), license_url)
Expand Down Expand Up @@ -373,13 +397,13 @@ def getLiveStreams(self):

banner = self.JSONImage(result.get('_embedded').get('image'))

for stream in result.get('sources').get('dash'):
if stream.get('is_uhd') and stream.get('quality_key').lower() == 'uhdbrowser':
uhd_video_url = generateDRMVideoUrl(stream.get('src'), drm_lic_url)
createListItem("[UHD] %s" % title, banner, description, duration,time.strftime('%Y-%m-%d', livestreamStart), programName, uhd_video_url, True, False, self.defaultbackdrop, self.pluginhandle)
if stream.get('is_uhd') and stream.get('quality_key').lower() == 'uhdsmarttv':
uhd_video_url = generateDRMVideoUrl(stream.get('src'), drm_lic_url)
createListItem("[UHD 50fps] %s" % title, banner, description, duration,time.strftime('%Y-%m-%d', livestreamStart), programName, uhd_video_url, True, False, self.defaultbackdrop, self.pluginhandle)
uhd_25_video_url = self.JSONStreamingDrmURL(result, 'uhdbrowser')
if uhd_25_video_url:
createListItem("[UHD] %s" % title, banner, description, duration,time.strftime('%Y-%m-%d', livestreamStart), programName, uhd_25_video_url, True, False, self.defaultbackdrop, self.pluginhandle)

uhd_50_video_url = self.JSONStreamingDrmURL(result, 'uhdsmarttv')
if uhd_50_video_url:
createListItem("[UHD 50fps] %s" % title, banner, description, duration,time.strftime('%Y-%m-%d', livestreamStart), programName, uhd_50_video_url, True, False, self.defaultbackdrop, self.pluginhandle)

createListItem(title, banner, description, duration, time.strftime('%Y-%m-%d', livestreamStart), programName, link, True, False, self.defaultbackdrop, self.pluginhandle,
contextMenuItems=contextMenuItems)
Expand Down

0 comments on commit 84396b0

Please sign in to comment.