Skip to content

Commit ca450b7

Browse files
committed
[plugin.video.cbc] 4.0.20+matrix.1
1 parent f4f748e commit ca450b7

File tree

6 files changed

+228
-229
lines changed

6 files changed

+228
-229
lines changed

plugin.video.cbc/addon.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22
<addon id="plugin.video.cbc"
33
name="Canadian Broadcasting Corp (CBC)"
4-
version="4.0.19+matrix.1"
4+
version="4.0.20+matrix.1"
55
provider-name="micahg,t1m,smf007,oshanrube">
66
<requires>
77
<import addon="xbmc.python" version="3.0.0"/>
@@ -28,6 +28,6 @@
2828
<forum>https://forum.kodi.tv/showthread.php?tid=328421</forum>
2929
<website>https://watch.cbc.ca/</website>
3030
<source>https://github.com/micahg/plugin.video.cbc</source>
31-
<news>- Fix live channels and IPTV</news>
31+
<news>- Updates for new CBC Gem API</news>
3232
</extension>
3333
</addon>

plugin.video.cbc/default.py

Lines changed: 71 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@
1919

2020
getString = xbmcaddon.Addon().getLocalizedString
2121
LIVE_CHANNELS = getString(30004)
22-
GEMS = {
23-
'featured': getString(30005),
24-
'shows': getString(30006),
25-
'documentaries': getString(30024),
26-
'kids': getString(30025)
27-
}
2822
SEARCH = getString(30026)
2923

3024

@@ -56,38 +50,64 @@ def authorize():
5650
return True
5751

5852

59-
def play(labels, image, url):
53+
def play(labels, image, data):
6054
"""Play the stream using the configured player."""
61-
item = xbmcgui.ListItem(labels['title'], path=url)
62-
if image:
63-
item.setArt({'thumb': image, 'poster': image})
64-
item.setInfo(type="Video", infoLabels=labels)
65-
helper = inputstreamhelper.Helper('hls')
66-
if not xbmcaddon.Addon().getSettingBool("ffmpeg") and helper.check_inputstream():
67-
item.setProperty('inputstream', 'inputstream.adaptive')
68-
item.setProperty('inputstream.adaptive.manifest_type', 'hls')
69-
xbmcplugin.setResolvedUrl(plugin.handle, True, item)
70-
if url is None:
55+
if not 'url' in data:
7156
xbmcgui.Dialog().ok(getString(30010), getString(30011))
72-
57+
return
58+
59+
(lic, tok) = GemV2.get_stream_drm(data)
60+
is_helper = None
61+
mime = None
62+
drm = None
63+
if data['type'] == 'hls':
64+
is_helper = inputstreamhelper.Helper('hls')
65+
elif data['type'] == 'dash':
66+
drm = 'com.widevine.alpha'
67+
is_helper = inputstreamhelper.Helper('mpd', drm=drm)
68+
mime = 'application/dash+xml'
69+
70+
if is_helper is None:
71+
xbmcgui.Dialog().ok(getString(30027), getString(30027))
72+
return
73+
74+
if is_helper.check_inputstream():
75+
url = data['url']
76+
play_item = xbmcgui.ListItem(path=url)
77+
play_item.setInfo(type="Video", infoLabels=labels)
78+
if mime:
79+
play_item.setMimeType(mime)
80+
play_item.setContentLookup(False)
81+
82+
if int(xbmc.getInfoLabel('System.BuildVersion').split('.')[0]) >= 19:
83+
play_item.setProperty('inputstream', is_helper.inputstream_addon)
84+
else:
85+
play_item.setProperty('inputstreamaddon', is_helper.inputstream_addon)
86+
87+
if drm:
88+
play_item.setProperty('inputstream.adaptive.license_type', drm)
89+
license_headers = {
90+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0',
91+
'Content-Type': 'application/octet-stream',
92+
'Origin': 'https://gem.cbc.ca',
93+
'x-dt-auth-token': tok, # string containing "Bearer eyJ...."
94+
}
95+
license_config = [ lic, urlencode(license_headers), 'R{SSM}', 'R']
96+
license_key = '|'.join(license_config)
97+
play_item.setProperty('inputstream.adaptive.license_key', license_key)
98+
xbmcplugin.setResolvedUrl(plugin.handle, True, play_item)
7399

74100
def add_items(handle, items):
75101
for item in items:
76102
list_item = xbmcgui.ListItem(item['title'])
77103
list_item.setInfo(type="Video", infoLabels=CBC.get_labels(item))
78-
image = item['image'].replace('(Size)', '224')
104+
image = item['image']['url'].replace('(Size)', '224')
79105
list_item.setArt({'thumb': image, 'poster': image})
80106
item_type = item['type']
81107
is_folder = True
82-
if item_type == 'SHOW':
83-
url = plugin.url_for(gem_show_menu, item['id'])
84-
elif item_type == 'ASSET':
85-
url = plugin.url_for(gem_asset, item['id'])
86-
list_item.setProperty('IsPlayable', 'true')
87-
is_folder = False
88-
elif item_type == 'SEASON':
89-
# ignore the season and go to the show (its what the web UI does)
90-
url = plugin.url_for(gem_show_menu, item['id'].split('/')[0])
108+
if item_type.lower() == 'show' or item_type.lower() == 'media':
109+
p = GemV2.normalized_format_path(item)
110+
url = plugin.url_for(layout_menu, p)
91111
else:
92112
log(f'Unable to handle shelf item type "{item_type}".', True)
93113
url = None
@@ -143,9 +163,12 @@ def live_channels_add_only(station):
143163
@plugin.route('/channels/play')
144164
def play_live_channel():
145165
labels = dict(parse_qsl(plugin.args['labels'][0])) if 'labels' in plugin.args else None
146-
chans = LiveChannels()
147-
url = chans.get_channel_stream(plugin.args['id'][0])
148-
return play(labels, plugin.args['image'][0], url)
166+
data = GemV2.get_stream(plugin.args['id'][0], plugin.args['app_code'][0])
167+
if not 'url' in data:
168+
log('Failed to get stream URL, attempting to authorize.')
169+
if authorize():
170+
data = GemV2.get_stream(plugin.args['id'][0], plugin.args['app_code'][0])
171+
return play(labels, plugin.args['image'][0] if 'image' in plugin.args else None, data)
149172

150173
@plugin.route('/channels')
151174
def live_channels_menu():
@@ -169,131 +192,11 @@ def live_channels_menu():
169192
(getString(30017), 'RunPlugin({})'.format(plugin.url_for(live_channels_add_only, callsign))),
170193
])
171194
xbmcplugin.addDirectoryItem(plugin.handle,
172-
plugin.url_for(play_live_channel, id=channel['idMedia'],
195+
plugin.url_for(play_live_channel, id=channel['idMedia'], app_code='medianetlive',
173196
labels=urlencode(labels), image=image), item, False)
174197
xbmcplugin.endOfDirectory(plugin.handle)
175198

176199

177-
@plugin.route('/gem/show/episode')
178-
def gem_episode():
179-
"""Play an episode."""
180-
json_str = plugin.args['query'][0]
181-
episode = json.loads(json_str)
182-
183-
# get the url, and failing that, attempt authorization, then retry
184-
resp = GemV2().get_episode(episode['url'])
185-
url = None if not resp else resp['url'] if 'url' in resp else None
186-
if not url:
187-
log('Failed to get stream URL, attempting to authorize.')
188-
if authorize():
189-
resp = GemV2().get_episode(episode['url'])
190-
url = resp['url'] if 'url' in resp else None
191-
192-
labels = episode['labels']
193-
play(labels, None, url)
194-
195-
196-
@plugin.route('/gem/show/season')
197-
def gem_show_season():
198-
"""Create a menu for a show season."""
199-
xbmcplugin.setContent(plugin.handle, 'videos')
200-
json_str = plugin.args['query'][0]
201-
# remember show['season'] is season details but there is general show info in show as well
202-
show = json.loads(json_str)
203-
for episode in show['season']['assets']:
204-
item = xbmcgui.ListItem(episode['title'])
205-
image = episode['image'].replace('(Size)', '224')
206-
item.setArt({'thumb': image, 'poster': image})
207-
item.setProperty('IsPlayable', 'true')
208-
labels = GemV2.get_labels(show, episode)
209-
item.setInfo(type="Video", infoLabels=labels)
210-
episode_info = {'url': episode['playSession']['url'], 'labels': labels}
211-
url = plugin.url_for(gem_episode, query=json.dumps(episode_info))
212-
xbmcplugin.addDirectoryItem(plugin.handle, url, item, False)
213-
xbmcplugin.endOfDirectory(plugin.handle)
214-
215-
216-
@plugin.route('/gem/asset/<path:asset>')
217-
def gem_asset(asset):
218-
asset_layout = GemV2.get_asset_by_id(asset)
219-
resp = GemV2.get_episode(asset_layout['playSession']['url'])
220-
url = None if not resp else resp['url'] if 'url' in resp else None
221-
if not url:
222-
log('Failed to get stream URL, attempting to authorize.')
223-
if authorize():
224-
resp = GemV2().get_episode(asset_layout['playSession']['url'])
225-
url = resp['url'] if 'url' in resp else None
226-
labels = GemV2.get_labels({'title': asset_layout['series']}, asset_layout)
227-
image = asset_layout['image']
228-
play(labels, image, url)
229-
230-
231-
def gem_add_film_assets(assets):
232-
for asset in assets:
233-
labels = GemV2.get_labels({'title': asset['series']}, asset)
234-
image = asset['image']
235-
item = xbmcgui.ListItem(labels['title'])
236-
item.setInfo(type="Video", infoLabels=labels)
237-
item.setArt({'thumb': image, 'poster': image})
238-
item.setProperty('IsPlayable', 'true')
239-
episode_info = {'url': asset['playSession']['url'], 'labels': labels}
240-
url = plugin.url_for(gem_episode, query=json.dumps(episode_info))
241-
xbmcplugin.addDirectoryItem(plugin.handle, url, item, False)
242-
243-
244-
@plugin.route('/gem/show/<show_id>')
245-
def gem_show_menu(show_id):
246-
"""Create a menu for a shelfs items."""
247-
xbmcplugin.setContent(plugin.handle, 'videos')
248-
show_layout = GemV2.get_show_layout_by_id(show_id)
249-
show = {k: v for (k, v) in show_layout.items() if k not in ['sponsors', 'seasons']}
250-
for season in show_layout['seasons']:
251-
252-
# films seem to have been shoe-horned (with teeth) into the structure oddly -- compensate
253-
if season['title'] == 'Film':
254-
gem_add_film_assets(season['assets'])
255-
else:
256-
labels = GemV2.get_labels(season, season)
257-
item = xbmcgui.ListItem(season['title'])
258-
item.setInfo(type="Video", infoLabels=labels)
259-
image = season['image'].replace('(Size)', '224')
260-
item.setArt({'thumb': image, 'poster': image})
261-
show['season'] = season
262-
url = plugin.url_for(gem_show_season, query=json.dumps(show))
263-
xbmcplugin.addDirectoryItem(plugin.handle, url, item, True)
264-
265-
xbmcplugin.endOfDirectory(plugin.handle)
266-
267-
268-
@plugin.route('/gem/shelf')
269-
def gem_shelf_menu():
270-
"""Create a menu item for each shelf."""
271-
handle = plugin.handle
272-
xbmcplugin.setContent(handle, 'videos')
273-
json_str = plugin.args['query'][0]
274-
shelf_items = json.loads(json_str)
275-
add_items(handle, shelf_items)
276-
xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE)
277-
xbmcplugin.endOfDirectory(handle)
278-
279-
280-
@plugin.route('/gem/categories/<category_id>')
281-
def gem_category_menu(category_id):
282-
"""Populate a menu with categorical content."""
283-
handle = plugin.handle
284-
xbmcplugin.setContent(handle, 'videos')
285-
category = GemV2.get_category(category_id)
286-
for show in category['items']:
287-
item = xbmcgui.ListItem(show['title'])
288-
item.setInfo(type="Video", infoLabels=CBC.get_labels(show))
289-
image = show['image'].replace('(Size)', '224')
290-
item.setArt({'thumb': image, 'poster': image})
291-
url = plugin.url_for(gem_show_menu, show['id'])
292-
xbmcplugin.addDirectoryItem(handle, url, item, True)
293-
xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE)
294-
xbmcplugin.endOfDirectory(handle)
295-
296-
297200
@plugin.route('/gem/search')
298201
def search():
299202
handle = plugin.handle
@@ -303,23 +206,24 @@ def search():
303206
xbmcplugin.endOfDirectory(handle)
304207

305208

306-
@plugin.route('/gem/layout/<layout>')
209+
@plugin.route('/gem/layout/<path:layout>')
307210
def layout_menu(layout):
308211
"""Populate the menu with featured items."""
309212
handle = plugin.handle
310213
xbmcplugin.setContent(handle, 'videos')
311-
layout = GemV2.get_layout(layout)
312-
if 'categories' in layout:
313-
for category in layout['categories']:
314-
item = xbmcgui.ListItem(category['title'])
315-
url = plugin.url_for(gem_category_menu, category['id'])
316-
xbmcplugin.addDirectoryItem(handle, url, item, True)
317-
if 'shelves' in layout:
318-
for shelf in layout['shelves']:
319-
item = xbmcgui.ListItem(shelf['title'])
320-
shelf_items = json.dumps(shelf['items'])
321-
url = plugin.url_for(gem_shelf_menu, query=shelf_items)
322-
xbmcplugin.addDirectoryItem(handle, url, item, True)
214+
for f in GemV2.get_format(layout):
215+
n = GemV2.normalized_format_item(f)
216+
p = GemV2.normalized_format_path(f)
217+
item = xbmcgui.ListItem(n['label'])
218+
if 'art' in n:
219+
item.setArt(n['art'])
220+
item.setInfo(type="Video", infoLabels=n['info_labels'])
221+
if n['playable']:
222+
item.setProperty('IsPlayable', 'true')
223+
url = plugin.url_for(play_live_channel, id=p, app_code=n['app_code'])
224+
else:
225+
url = plugin.url_for(layout_menu, p)
226+
xbmcplugin.addDirectoryItem(handle, url, item, not n['playable'])
323227
xbmcplugin.endOfDirectory(handle)
324228

325229

@@ -334,8 +238,8 @@ def main_menu():
334238

335239
handle = plugin.handle
336240
xbmcplugin.setContent(handle, 'videos')
337-
for key, value in GEMS.items():
338-
xbmcplugin.addDirectoryItem(handle, plugin.url_for(layout_menu, key), xbmcgui.ListItem(value), True)
241+
for c in GemV2.get_browse():
242+
xbmcplugin.addDirectoryItem(handle, plugin.url_for(layout_menu, c['url']), xbmcgui.ListItem(c['title']), True)
339243
xbmcplugin.addDirectoryItem(handle, plugin.url_for(live_channels_menu), xbmcgui.ListItem(LIVE_CHANNELS), True)
340244
xbmcplugin.addDirectoryItem(handle, plugin.url_for(search), xbmcgui.ListItem(SEARCH), True)
341245
xbmcplugin.endOfDirectory(handle)

plugin.video.cbc/resources/language/resource.language.en_gb/strings.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,7 @@ msgstr ""
119119
msgctxt "#30026"
120120
msgid "Search"
121121
msgstr ""
122+
123+
msgctxt "#30027"
124+
msgid "Unsupported DRM"
125+
msgstr ""

plugin.video.cbc/resources/lib/cbc.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -325,21 +325,6 @@ def get_labels(item):
325325
return labels
326326

327327

328-
def parse_smil(self, smil):
329-
"""Parse a SMIL file for the video."""
330-
resp = self.session.get(smil)
331-
332-
if not resp.status_code == 200:
333-
log(f'ERROR: {smil} returns status of {resp.status_code}', True)
334-
return None
335-
save_cookies(self.session.cookies)
336-
337-
dom = parseString(resp.content)
338-
seq = dom.getElementsByTagName('seq')[0]
339-
video = seq.getElementsByTagName('video')[0]
340-
src = video.attributes['src'].value
341-
return src
342-
343328
@staticmethod
344329
def get_session():
345330
"""Get a requests session object with CBC cookies."""

0 commit comments

Comments
 (0)