Skip to content

Commit

Permalink
Merge pull request #4391 from mediaminister/plugin.video.vrt.nu@krypton
Browse files Browse the repository at this point in the history
[plugin.video.vrt.nu@krypton] 2.5.26
  • Loading branch information
basrieter authored Sep 23, 2023
2 parents 460ad2b + a8db662 commit 53899a5
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 51 deletions.
4 changes: 4 additions & 0 deletions plugin.video.vrt.nu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ leave a message at [our Facebook page](https://facebook.com/kodivrtnu/).
</table>

## Releases
### v2.5.26 (2023-09-22)
- Fix program listings (@mediaminister)
- Add extra content to program listings (@mediaminister)

### v2.5.25 (2023-09-14)
- Fix episode listings and featured menu (@mediaminister)

Expand Down
6 changes: 5 additions & 1 deletion plugin.video.vrt.nu/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.vrt.nu" name="VRT MAX" version="2.5.25" provider-name="Martijn Moreel, dagwieers, mediaminister">
<addon id="plugin.video.vrt.nu" name="VRT MAX" version="2.5.26" provider-name="Martijn Moreel, dagwieers, mediaminister">
<requires>
<import addon="resource.images.studios.white" version="0.0.22"/>
<import addon="script.module.beautifulsoup4" version="4.6.2"/>
Expand Down Expand Up @@ -42,6 +42,10 @@
<website>https://github.com/add-ons/plugin.video.vrt.nu/wiki</website>
<source>https://github.com/add-ons/plugin.video.vrt.nu</source>
<news>
v2.5.26 (2023-09-22)
- Fix program listings
- Add extra content to program listings

v2.5.25 (2023-09-14)
- Fix episode listings and featured menu

Expand Down
234 changes: 201 additions & 33 deletions plugin.video.vrt.nu/resources/lib/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,54 @@ def get_next_info(episode_id):
return next_info


def get_stream_id_data(vrtmax_url):
"""Get stream_id from from GraphQL API"""
from tokenresolver import TokenResolver
access_token = TokenResolver().get_token('vrtnu-site_profile_at')
data_json = {}
if access_token:
headers = {
'Authorization': 'Bearer ' + access_token,
'Content-Type': 'application/json',
}
page_id = vrtmax_url.split('www.vrt.be')[1].replace('/vrtmax/', '/vrtnu/').rstrip('/') + '.model.json'
graphql_query = """
query StreamId($pageId: ID!) {
page(id: $pageId) {
... on IPage {
... on LivestreamPage {
player {
watchAction {
... on LiveWatchAction {
streamId
}
}
}
}
}
... on EpisodePage {
episode {
watchAction {
streamId
}
}
}
}
}
"""
payload = {
'operationName': 'StreamId',
'variables': {
'pageId': page_id
},
'query': graphql_query,
}
from json import dumps
data = dumps(payload).encode('utf-8')
data_json = get_url_json(url=GRAPHQL_URL, cache=None, headers=headers, data=data, raise_errors='all')
return data_json


def get_single_episode_data(episode_id):
"""Get single episode data from GraphQL API"""
from tokenresolver import TokenResolver
Expand Down Expand Up @@ -326,6 +374,110 @@ def get_latest_episode_data(program_name):
from tokenresolver import TokenResolver
access_token = TokenResolver().get_token('vrtnu-site_profile_at')
data_json = {}
if access_token:
headers = {
'Authorization': 'Bearer ' + access_token,
'Content-Type': 'application/json',
}
graphql_query = """
query VideoProgramPage($pageId: ID!, $lazyItemCount: Int = 500, $after: ID) {
page(id: $pageId) {
... on ProgramPage {
components {
__typename
... on PageHeader {
mostRelevantEpisodeTile {
__typename
title
tile {
...episodeTile
__typename
}
__typename
}
__typename
}
... on ContainerNavigation {
items {
title
components {
__typename
... on PaginatedTileList {
__typename
paginatedItems(first: $lazyItemCount, after: $after) {
__typename
edges {
__typename
cursor
node {
__typename
... on EpisodeTile {
id
description
...episodeTile
}
}
}
}
}
... on ContainerNavigation {
items {
title
components {
__typename
... on PaginatedTileList {
__typename
paginatedItems(first: $lazyItemCount, after: $after) {
__typename
edges {
__typename
cursor
node {
__typename
... on EpisodeTile {
id
description
...episodeTile
}
}
}
}
}
}
}
__typename
}
}
__typename
}
__typename
}
}
__typename
}
__typename
}
}
%s
""" % EPISODE_TILE
payload = {
'operationName': 'VideoProgramPage',
'variables': {
'pageId': '/vrtnu/a-z/{}.model.json'.format(program_name),
},
'query': graphql_query,
}
from json import dumps
data = dumps(payload).encode('utf-8')
data_json = get_url_json(url=GRAPHQL_URL, cache=None, headers=headers, data=data, raise_errors='all')
return data_json


def get_seasons_data(program_name):
"""Get seasons data from GraphQL API"""
from tokenresolver import TokenResolver
access_token = TokenResolver().get_token('vrtnu-site_profile_at')
data_json = {}
if access_token:
headers = {
'Authorization': 'Bearer ' + access_token,
Expand Down Expand Up @@ -1006,7 +1158,7 @@ def get_offline_programs(end_cursor='', use_favorites=False):

def get_episodes(program_name, season_name=None, end_cursor=''):
"""Get episodes"""
sort = 'label'
sort = 'unsorted'
ascending = True
content = 'files'
page_size = get_setting_int('itemsperpage', default=50)
Expand All @@ -1021,7 +1173,10 @@ def get_episodes(program_name, season_name=None, end_cursor=''):
return seasons, sort, ascending, content

if program_name and season_name:
list_id = 'static:/vrtnu/a-z/{}/{}.episodes-list.json'.format(program_name, season_name)
if season_name.startswith('parsys'):
list_id = 'static:/vrtnu/a-z/{}.model.json@{}'.format(program_name, season_name)
else:
list_id = 'static:/vrtnu/a-z/{}/{}.episodes-list.json'.format(program_name, season_name)
api_data = get_paginated_episodes(list_id=list_id, page_size=page_size, end_cursor=end_cursor)
episodes, sort, ascending = convert_episodes(api_data, destination='programs', program_name=program_name, season_name=season_name)
return episodes, sort, ascending, 'episodes'
Expand All @@ -1032,69 +1187,81 @@ def convert_seasons(api_data, program_name):
"""Convert seasons"""
seasons = []
for season in api_data:
season_title = season.get('title')
season_name = season.get('name')
path = url_for('programs', program_name=program_name, season_name=season_name)
seasons.append(
TitleItem(
label=season_title,
path=path,
info_dict={
'title': season_title,
'mediatype': 'season',
},
is_playable=False,
if season.get('name') == 'mostRelevantEpisode':
_, _, _, title_item = convert_episode(season.get('episode'))
title_item.label = '[B]{}:[/B] {}'.format(season.get('title'), title_item.label)
title_item.info_dict['title'] = '[B]{}:[/B] {}'.format(season.get('title'), title_item.info_dict.get('title'))
seasons.append(title_item)
else:
season_title = season.get('title')
season_name = season.get('name')
path = url_for('programs', program_name=program_name, season_name=season_name)
seasons.append(
TitleItem(
label=season_title,
path=path,
info_dict={
'title': season_title,
'mediatype': 'season',
},
is_playable=False,
)
)
)
return seasons


def create_season_dict(data_json):
"""Create season dictionary"""
season_title = data_json.get('title')
season_dict = {}
# title
season_dict['title'] = data_json.get('title') or data_json.get('mostRelevantEpisodeTile').get('title')

# list_id
if data_json.get('components'):
list_id = data_json.get('components')[0].get('listId')
elif data_json.get('mostRelevantEpisodeTile'):
list_id = 'mostRelevantEpisode'
season_dict['episode'] = data_json.get('mostRelevantEpisodeTile')
else:
list_id = data_json.get('listId')

# season name
if '.episodes-list.json' in list_id:
season_name = list_id.split('.episodes-list.json')[0].split('/')[-1]
season_dict['name'] = list_id.split('.episodes-list.json')[0].split('/')[-1]
else:
season_name = list_id.split('_')[-1]
return {'title': season_title, 'name': season_name}
season_dict['name'] = list_id.split('@')[-1]
return season_dict


def get_seasons(program_name):
"""Get seasons"""
seasons = []
# FIXME: The current codebase only supports seasons and not the extra content. So we need to select seasons using a whitelist.
whitelist = ['Afleveringen', 'Alle seizoenen', 'Journaals', 'Reeksen', 'Docu']
components = get_latest_episode_data(program_name).get('data').get('page').get('components')
components = get_seasons_data(program_name).get('data').get('page').get('components')
# Extract season data from components
for component in components:
# Check component type
if component.get('navigationType') == 'bar':
# Get items
for item in component.get('items'):
# Select whitelist item
if item.get('title') in whitelist:
# Get components
for nested_component in item.get('components'):
# Append component
components.append(nested_component)
# Get components
for nested_component in item.get('components'):
# Append component
components.append(nested_component)
elif component.get('navigationType') == 'select':
# Get items
for item in component.get('items'):
# Store season
seasons.append(create_season_dict(item))
# Extraction done, remove component
components.remove(component)
if item.get('title'):
seasons.append(create_season_dict(item))
elif component.get('__typename') == 'PaginatedTileList' and component.get('tileContentType') == 'episode':
# Store season
if component.get('title'):
seasons.append(create_season_dict(component))
elif component.get('__typename') == 'PageHeader' and component.get('mostRelevantEpisodeTile'):
seasons.append(create_season_dict(component))
# Extraction done, remove component
components.remove(component)
return seasons


def get_featured_data():
"""Get featured data"""
from tokenresolver import TokenResolver
Expand Down Expand Up @@ -1178,6 +1345,7 @@ def get_featured_data():
data_json = get_url_json(url=GRAPHQL_URL, cache=None, headers=headers, data=data, raise_errors='all')
return data_json


def get_featured(feature=None, end_cursor=''):
"""Get featured menu items"""
content = 'files'
Expand Down
6 changes: 3 additions & 3 deletions plugin.video.vrt.nu/resources/lib/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@
{
'id': '1H',
'name': 'canvas',
'label': 'Canvas',
'label': 'VRT Canvas',
'studio': 'Canvas',
'live_stream': 'https://www.vrt.be/vrtmax/livestream/video/canvas/',
'live_stream': 'https://www.vrt.be/vrtmax/livestream/video/vrt-canvas/',
'live_stream_id': 'vualto_canvas_geo',
'youtube': [
{'label': 'Canvas', 'url': 'https://www.youtube.com/user/CanvasTV'},
{'label': 'VRT Canvas', 'url': 'https://www.youtube.com/user/CanvasTV'},
{'label': 'Sporza', 'url': 'https://www.youtube.com/user/SporzaOfficial'},
{'label': 'Terzake', 'url': 'https://www.youtube.com/user/terzaketv'},
],
Expand Down
25 changes: 11 additions & 14 deletions plugin.video.vrt.nu/resources/lib/streamservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,17 @@ def _get_api_data(self, video):
elif video_id and not video_url:
api_data = ApiData(self._CLIENT, self._VUALTO_API_URL, video_id, '', True)
elif video_url:
model_url = video_url.strip('/') + '.model.json'
data_json = get_url_json(model_url)
if data_json:
# Get streamId
stream_id = None
for action in data_json.get('details').get('actions'):
if action.get('type') == 'watch-episode':
if action.get('videoType') == 'live':
is_live_stream = True
stream_id = action.get('streamId')
else:
is_live_stream = False
stream_id = action.get('episodePublicationId') + quote('$') + action.get('episodeVideoId')
api_data = ApiData(self._CLIENT, self._VUALTO_API_URL, stream_id, '', is_live_stream)
from api import get_stream_id_data
data_json = get_stream_id_data(video_url)
episode_data = data_json.get('data').get('page')
stream_id = ''
is_live_stream = False
if episode_data and episode_data.get('episode'):
stream_id = episode_data.get('episode').get('watchAction').get('streamId')
elif episode_data and episode_data.get('player'):
stream_id = episode_data.get('player').get('watchAction').get('streamId')
is_live_stream = True
api_data = ApiData(self._CLIENT, self._VUALTO_API_URL, stream_id, '', is_live_stream)
return api_data

def _get_stream_json(self, api_data, roaming=False):
Expand Down

0 comments on commit 53899a5

Please sign in to comment.