Add recommendations and categories (#76)

This commit is contained in:
Michaël Arnauts 2021-02-17 07:42:24 +01:00 committed by GitHub
parent 9ddc73094d
commit 88e1bbc4d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 360 additions and 194 deletions

View File

@ -18,6 +18,18 @@ msgctxt "#30003"
msgid "Catalogue"
msgstr ""
msgctxt "#30004"
msgid "Browse the catalogue"
msgstr ""
msgctxt "#30005"
msgid "Recommendations"
msgstr ""
msgctxt "#30006"
msgid "Show the recommendations"
msgstr ""
msgctxt "#30007"
msgid "Channels"
msgstr ""
@ -64,14 +76,6 @@ msgctxt "#30056"
msgid "Browse the Catalog for [B]{channel}[/B]"
msgstr ""
msgctxt "#30057"
msgid "Categories for [B]{channel}[/B]"
msgstr ""
msgctxt "#30058"
msgid "Browse the Categories for [B]{channel}[/B]"
msgstr ""
msgctxt "#30059"
msgid "Clips of [B]{program}[/B]"
msgstr ""
@ -186,6 +190,14 @@ msgctxt "#30803"
msgid "Password"
msgstr ""
msgctxt "#30820"
msgid "Interface"
msgstr ""
msgctxt "#30821"
msgid "Show unavailable programs"
msgstr ""
msgctxt "#30840"
msgid "Integration"
msgstr ""

View File

@ -19,6 +19,18 @@ msgctxt "#30003"
msgid "Catalogue"
msgstr "Catalogus"
msgctxt "#30004"
msgid "Browse the catalogue"
msgstr "Doorblader de catalogus"
msgctxt "#30005"
msgid "Recommendations"
msgstr "Aanbevelingen"
msgctxt "#30006"
msgid "Show the recommendations"
msgstr "Doorblader de aanbevelingen"
msgctxt "#30007"
msgid "Channels"
msgstr "Kanalen"
@ -65,14 +77,6 @@ msgctxt "#30056"
msgid "Browse the Catalog for [B]{channel}[/B]"
msgstr "Doorblader de catalogus voor [B]{channel}[/B]"
msgctxt "#30057"
msgid "Categories for [B]{channel}[/B]"
msgstr "Categoriën voor [B]{channel}[/B]"
msgctxt "#30058"
msgid "Browse the Categories for [B]{channel}[/B]"
msgstr "Doorblader de categoriën van [B]{channel}[/B]"
msgctxt "#30059"
msgid "Clips of [B]{program}[/B]"
msgstr "Clips van [B]{program}[/B]"
@ -187,6 +191,14 @@ msgctxt "#30803"
msgid "Password"
msgstr "Wachtwoord"
msgctxt "#30820"
msgid "Interface"
msgstr "Interface"
msgctxt "#30821"
msgid "Show unavailable programs"
msgstr "Toon onbeschikbare programma's"
msgctxt "#30840"
msgid "Integration"
msgstr "Integratie"

View File

@ -9,6 +9,11 @@ from routing import Plugin
from resources.lib import kodilogging
try: # Python 3
from urllib.parse import unquote
except ImportError: # Python 2
from urllib import unquote
routing = Plugin() # pylint: disable=invalid-name
_LOGGER = logging.getLogger(__name__)
@ -34,20 +39,6 @@ def show_channel_menu(channel):
Channels().show_channel_menu(channel)
# @routing.route('/channels/<channel>/categories')
# def show_channel_categories(channel):
# """ Shows TV Channel categories """
# from resources.lib.modules.channels import Channels
# Channels().show_channel_categories(channel)
# @routing.route('/channels/<channel>/categories/<category>')
# def show_channel_category(channel, category):
# """ Shows TV Channel categories """
# from resources.lib.modules.channels import Channels
# Channels().show_channel_category(channel, category)
@routing.route('/channels/<channel>/tvguide')
def show_channel_tvguide(channel):
""" Shows the dates in the tv guide """
@ -97,6 +88,34 @@ def show_catalog_program_season(program, season):
Catalog().show_program_season(program, season)
@routing.route('/category')
def show_categories():
""" Show the catalog by category """
from resources.lib.modules.catalog import Catalog
Catalog().show_categories()
@routing.route('/category/<category>')
def show_category(category):
""" Show the catalog by category """
from resources.lib.modules.catalog import Catalog
Catalog().show_category(category)
@routing.route('/recommendations')
def show_recommendations():
""" Show my list """
from resources.lib.modules.catalog import Catalog
Catalog().show_recommendations()
@routing.route('/recommendations/<category>')
def show_recommendations_category(category):
""" Show my list """
from resources.lib.modules.catalog import Catalog
Catalog().show_recommendations_category(category)
@routing.route('/mylist')
def show_mylist():
""" Show my list """
@ -150,11 +169,6 @@ def play_catalog(uuid):
@routing.route('/play/page/<page>')
def play_from_page(page):
""" Play the requested item """
try: # Python 3
from urllib.parse import unquote
except ImportError: # Python 2
from urllib import unquote
from resources.lib.modules.player import Player
Player().play_from_page(unquote(page))

View File

@ -45,6 +45,7 @@ HTML_MAPPING = [
(re.compile(r'</?(li|ul|ol)(|\s[^>]+)>', re.I), '\n'),
(re.compile(r'</?(code|div|p|pre|span)(|\s[^>]+)>', re.I), ''),
(re.compile('(&nbsp;\n){2,}', re.I), '\n'), # Remove repeating non-blocking spaced newlines
(re.compile(' +', re.I), ' '), # Remove double spaces
]
STREAM_HLS = 'hls'
@ -57,8 +58,7 @@ class TitleItem:
""" This helper object holds all information to be used with Kodi xbmc's ListItem object """
def __init__(self, title, path=None, art_dict=None, info_dict=None, prop_dict=None, stream_dict=None,
context_menu=None, subtitles_path=None,
is_playable=False):
context_menu=None, subtitles_path=None, is_playable=False, visible=True):
""" The constructor for the TitleItem class
:type title: str
:type path: str
@ -69,6 +69,7 @@ class TitleItem:
:type context_menu: list[tuple[str, str]]
:type subtitles_path: list[str]
:type is_playable: bool
:type visible: bool
"""
self.title = title
self.path = path
@ -79,6 +80,7 @@ class TitleItem:
self.context_menu = context_menu
self.subtitles_path = subtitles_path
self.is_playable = is_playable
self.visible = visible
def __repr__(self):
return "%r" % self.__dict__
@ -189,6 +191,9 @@ def show_listing(title_items, category=None, sort=None, content=None, cache=True
# Add the listings
listing = []
for title_item in title_items:
if not title_item.visible:
continue
# Three options:
# - item is a virtual directory/folder (not playable, path)
# - item is a playable file (playable, path)

View File

@ -178,6 +178,73 @@ class Catalog:
# Sort like we get our results back.
kodiutils.show_listing(listing, 30003, content='episodes')
def show_categories(self):
""" Shows the categories """
categories = self._api.get_categories()
listing = []
for category in categories:
listing.append(TitleItem(title=category.title,
path=kodiutils.url_for('show_category', category=category.uuid),
info_dict={
'title': category.title,
}))
kodiutils.show_listing(listing, 30003, sort=['title'])
def show_category(self, uuid):
""" Shows a category """
programs = self._api.get_category_content(int(uuid))
listing = [
Menu.generate_titleitem(program) for program in programs
]
kodiutils.show_listing(listing, 30003, content='tvshows')
def show_recommendations(self):
""" Shows the recommendations """
# "Meest bekeken" has a specific API endpoint, the other categories are scraped from the website.
listing = [
TitleItem(title='Meest bekeken',
path=kodiutils.url_for('show_recommendations_category', category='meest-bekeken'),
info_dict={
'title': 'Meest bekeken',
})
]
recommendations = self._api.get_recommendation_categories()
for category in recommendations:
listing.append(TitleItem(title=category.title,
path=kodiutils.url_for('show_recommendations_category', category=category.uuid),
info_dict={
'title': category.title,
}))
kodiutils.show_listing(listing, 30005, content='tvshows')
def show_recommendations_category(self, uuid):
""" Shows the a category of the recommendations """
if uuid == 'meest-bekeken':
programs = self._api.get_popular_programs()
episodes = []
else:
recommendations = self._api.get_recommendation_categories()
category = next(category for category in recommendations if category.uuid == uuid)
programs = category.programs
episodes = category.episodes
listing = []
for episode in episodes:
title_item = Menu.generate_titleitem(episode)
title_item.info_dict['title'] = episode.program_title + ' - ' + title_item.title
listing.append(title_item)
for program in programs:
listing.append(Menu.generate_titleitem(program))
kodiutils.show_listing(listing, 30005, content='tvshows')
def show_mylist(self):
""" Show all the programs of all channels """
try:

View File

@ -103,20 +103,6 @@ class Channels:
)
)
# listing.append(
# TitleItem(
# title=kodiutils.localize(30057, channel=channel_info.get('name')), # Categories for {channel}
# path=kodiutils.url_for('show_channel_categories', channel=channel),
# art_dict={
# 'icon': 'DefaultGenre.png',
# 'fanart': fanart,
# },
# info_dict={
# 'plot': kodiutils.localize(30058, channel=channel_info.get('name')), # Browse the Categories for {channel}
# }
# )
# )
# Add YouTube channels
if kodiutils.get_cond_visibility('System.HasAddon(plugin.video.youtube)') != 0:
for youtube in channel_info.get('youtube', []):
@ -131,58 +117,3 @@ class Channels:
)
kodiutils.show_listing(listing, 30007, sort=['unsorted'])
# def show_channel_categories(self, channel):
# """ Shows the categories of a channel
# :type channel: str
# """
# categories = self._api.get_categories(channel)
#
# listing = [
# TitleItem(
# title=category.title,
# path=kodiutils.url_for('show_channel_category', channel=category.channel, category=category.uuid),
# art_dict={
# 'icon': 'DefaultGenre.png',
# },
# ) for category in categories
# ]
#
# kodiutils.show_listing(listing, 30007, sort=['unsorted'])
# def show_channel_category(self, channel, category_id):
# """ Shows a selected category of a channel
# :type channel: str
# :type category_id: str
# """
# categories = self._api.get_categories(channel)
#
# # Extract selected category
# category = next(category for category in categories if category.uuid == category_id)
# if not category:
# raise Exception('Unknown category')
#
# # Add programs
# listing_programs = []
# for item in category.programs:
# program = self._api.get_program(item.path, CACHE_ONLY) # Get program details, but from cache only
#
# if program:
# listing_programs.append(Menu.generate_titleitem(program))
# else:
# listing_programs.append(Menu.generate_titleitem(item))
#
# # Add episodes
# listing_episodes = []
# for item in category.episodes:
# # We don't have the Program Name without making a request to the page, so we use CACHE_AUTO instead of CACHE_ONLY.
# # This will make a request for each item in this view (about 12 items), but it goes quite fast.
# # Results are cached, so this will only happen once.
# episode = self._api.get_episode(item.path, CACHE_AUTO)
#
# if episode:
# listing_episodes.append(Menu.generate_titleitem(episode))
# else:
# listing_episodes.append(Menu.generate_titleitem(item))
#
# kodiutils.show_listing(listing_programs + listing_episodes, 30007, content='tvshows', sort=['unsorted'])

View File

@ -8,6 +8,11 @@ from resources.lib.kodiutils import TitleItem
from resources.lib.viervijfzes import STREAM_DICT
from resources.lib.viervijfzes.content import Episode, Program
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
class Menu:
""" Menu code """
@ -41,6 +46,28 @@ class Menu:
plot=kodiutils.localize(30008),
)
),
TitleItem(
title=kodiutils.localize(30003), # Catalog
path=kodiutils.url_for('show_categories'),
art_dict=dict(
icon='DefaultGenre.png',
fanart=kodiutils.get_addon_info('fanart'),
),
info_dict=dict(
plot=kodiutils.localize(30004),
)
),
TitleItem(
title=kodiutils.localize(30005), # Recommendations
path=kodiutils.url_for('show_recommendations'),
art_dict=dict(
icon='DefaultFavourites.png',
fanart=kodiutils.get_addon_info('fanart'),
),
info_dict=dict(
plot=kodiutils.localize(30006),
)
),
TitleItem(
title=kodiutils.localize(30011), # My List
path=kodiutils.url_for('show_mylist'),
@ -94,9 +121,12 @@ class Menu:
'season': len(item.seasons) if item.seasons else None,
})
visible = True
if isinstance(item.episodes, list) and not item.episodes:
# We know that we don't have episodes
title = '[COLOR gray]' + item.title + '[/COLOR]'
visible = kodiutils.get_setting_bool('interface_show_unavailable')
else:
# We have episodes, or we don't know it
title = item.title
@ -126,7 +156,8 @@ class Menu:
path=kodiutils.url_for('show_catalog_program', program=item.path),
context_menu=context_menu,
art_dict=art_dict,
info_dict=info_dict)
info_dict=info_dict,
visible=visible)
#
# Episode
@ -146,11 +177,6 @@ class Menu:
})
if item.path:
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
# We don't have an UUID, and first need to fetch the video information from the page
path = kodiutils.url_for('play_from_page', page=quote(item.path, safe=''))
else:

View File

@ -12,6 +12,11 @@ from resources.lib.viervijfzes.auth import AuthApi
from resources.lib.viervijfzes.aws.cognito_idp import AuthenticationException, InvalidLoginException
from resources.lib.viervijfzes.content import ContentApi, GeoblockedException, UnavailableException
try: # Python 3
from urllib.parse import quote, urlencode
except ImportError: # Python 2
from urllib import quote, urlencode
_LOGGER = logging.getLogger(__name__)
@ -142,11 +147,6 @@ class Player:
:param str key_value:
:rtype: str
"""
try: # Python 3
from urllib.parse import quote, urlencode
except ImportError: # Python 2
from urllib import quote, urlencode
header = ''
if key_headers:
header = urlencode(key_headers)

View File

@ -13,6 +13,11 @@ from resources.lib.viervijfzes import STREAM_DICT
from resources.lib.viervijfzes.content import UnavailableException
from resources.lib.viervijfzes.epg import EpgApi
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
_LOGGER = logging.getLogger(__name__)
@ -104,11 +109,6 @@ class TvGuide:
kodiutils.end_of_directory()
return
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
listing = []
for program in programs:
if program.program_url:

View File

@ -3,6 +3,7 @@
from __future__ import absolute_import, division, unicode_literals
import hashlib
import json
import logging
import os
@ -208,7 +209,7 @@ class ContentApi:
return data
# Fetch listing from cache or update if needed
data = self._handle_cache(key=['programs'], cache_mode=cache, update=update, ttl=5 * 60)
data = self._handle_cache(key=['programs'], cache_mode=cache, update=update, ttl=30 * 60) # 30 minutes
if not data:
return []
@ -381,75 +382,145 @@ class ContentApi:
stream_type=STREAM_HLS,
)
# def get_categories(self):
# """ Get a list of all categories.
# :rtype list[Category]
# """
# # Load webpage
# raw_html = self._get_url(self.SITE_URL)
#
# # Categories regexes
# regex_articles = re.compile(r'<article([^>]+)>(.*?)</article>', re.DOTALL)
# regex_submenu_id = re.compile(r'data-submenu-id="([^"]*)"') # splitted since the order might change
# regex_submenu_title = re.compile(r'data-submenu-title="([^"]*)"')
#
# categories = []
# for result in regex_articles.finditer(raw_html):
# article_info_html = result.group(1)
# article_html = result.group(2)
# category_title = regex_submenu_title.search(article_info_html).group(1)
# category_id = regex_submenu_id.search(article_info_html).group(1)
#
# # Skip empty categories or 'All programs'
# if not category_id or category_id == 'programmas':
# continue
#
# # Extract items
# programs = self._extract_programs(article_html, channel)
# episodes = self._extract_videos(article_html)
# categories.append(Category(uuid=category_id, channel=channel, title=category_title, programs=programs, episodes=episodes))
#
# return categories
def get_program_tree(self, cache=CACHE_AUTO):
""" Get a content tree with information about all the programs.
:type cache: str
:rtype dict
"""
# @staticmethod
# def _extract_programs(html, channel):
# """ Extract Programs from HTML code """
# # Item regexes
# regex_item = re.compile(r'<a[^>]+?href="(?P<path>[^"]+)"[^>]+?>'
# r'.*?<h3 class="poster-teaser__title"><span>(?P<title>[^<]*)</span></h3>.*?'
# r'</a>', re.DOTALL)
#
# # Extract items
# programs = []
# for item in regex_item.finditer(html):
# path = item.group('path')
# if path.startswith('/video'):
# continue
#
# title = unescape(item.group('title'))
#
# # Program
# programs.append(Program(
# path=path.lstrip('/'),
# channel=channel,
# title=title,
# ))
#
# return programs
def update():
""" Fetch the content tree """
response = self._get_url(self.SITE_URL + '/api/content_tree')
return json.loads(response)
# Fetch listing from cache or update if needed
data = self._handle_cache(key=['content_tree'], cache_mode=cache, update=update, ttl=5 * 60) # 5 minutes
return data
def get_popular_programs(self, brand=None):
""" Get a list of popular programs.
:rtype list[Program]
"""
if brand:
response = self._get_url(self.SITE_URL + '/api/programs/popular/%s' % brand)
else:
response = self._get_url(self.SITE_URL + '/api/programs/popular')
data = json.loads(response)
programs = []
for program in data:
programs.append(self._parse_program_data(program))
return programs
def get_categories(self):
""" Return a list of categories.
:rtype list[Category]
"""
content_tree = self.get_program_tree()
categories = []
for category_id, category_name in content_tree.get('categories').items():
categories.append(Category(uuid=category_id,
title=category_name))
return categories
def get_category_content(self, category_id):
""" Return a category.
:type category_id: int
:rtype list[Program]
"""
content_tree = self.get_program_tree()
# Find out all the program_id's of the requested category
program_ids = [key for key, value in content_tree.get('programs').items() if value.get('category') == category_id]
# Filter out the list of all programs to only keep the one of the requested category
return [program for program in self.get_programs() if program.uuid in program_ids]
def get_recommendation_categories(self):
""" Get a list of all categories.
:rtype list[Category]
"""
# Load all programs
all_programs = self.get_programs()
# Load webpage
raw_html = self._get_url(self.SITE_URL)
# Categories regexes
regex_articles = re.compile(r'<article[^>]+>(.*?)</article>', re.DOTALL)
regex_category = re.compile(r'<h1.*?>(.*?)</h1>(?:.*?<div class="visually-hidden">(.*?)</div>)?', re.DOTALL)
categories = []
for result in regex_articles.finditer(raw_html):
article_html = result.group(1)
match_category = regex_category.search(article_html)
category_title = match_category.group(1).strip()
if match_category.group(2):
category_title += ' [B]%s[/B]' % match_category.group(2).strip()
# Extract programs and lookup in all_programs so we have more metadata
programs = []
for program in self._extract_programs(article_html):
try:
rich_program = next(rich_program for rich_program in all_programs if rich_program.path == program.path)
programs.append(rich_program)
except StopIteration:
programs.append(program)
episodes = self._extract_videos(article_html)
categories.append(
Category(uuid=hashlib.md5(category_title.encode('utf-8')).hexdigest(), title=category_title, programs=programs, episodes=episodes))
return categories
@staticmethod
def _extract_programs(html):
""" Extract Programs from HTML code
:type html: str
:rtype list[Program]
"""
# Item regexes
regex_item = re.compile(r'<a[^>]+?href="(?P<path>[^"]+)"[^>]+?>'
r'.*?<h3 class="poster-teaser__title">(?P<title>[^<]*)</h3>.*?data-background-image="(?P<image>.*?)".*?'
r'</a>', re.DOTALL)
# Extract items
programs = []
for item in regex_item.finditer(html):
path = item.group('path')
if path.startswith('/video'):
continue
# Program
programs.append(Program(
path=path.lstrip('/'),
title=unescape(item.group('title')),
cover=unescape(item.group('image')),
))
return programs
@staticmethod
def _extract_videos(html):
""" Extract videos from HTML code """
""" Extract videos from HTML code
:type html: str
:rtype list[Episode]
"""
# Item regexes
regex_item = re.compile(r'<a[^>]+?href="(?P<path>[^"]+)"[^>]+?>.*?</a>', re.DOTALL)
# Episode regexes
regex_episode_title = re.compile(r'<h3 class="(?:poster|card|image)-teaser__title">(?:<span>)?([^<]*)(?:</span>)?</h3>')
regex_episode_program = re.compile(r'<div class="card-teaser__label">([^<]*)</div>')
regex_episode_program = re.compile(r'<h3 class="episode-teaser__subtitle">([^<]*)</h3>')
regex_episode_title = re.compile(r'<(?:div|h3) class="(?:poster|card|image|episode)-teaser__title">(?:<span>)?([^<]*)(?:</span>)?</(?:div|h3)>')
regex_episode_duration = re.compile(r'data-duration="([^"]*)"')
regex_episode_video_id = re.compile(r'data-videoid="([^"]*)"')
regex_episode_video_id = re.compile(r'data-video-id="([^"]*)"')
regex_episode_image = re.compile(r'data-background-image="([^"]*)"')
regex_episode_timestamp = re.compile(r'data-timestamp="([^"]*)"')
regex_episode_badge = re.compile(r'<div class="(?:poster|card|image|episode)-teaser__badge badge">([^<]*)</div>')
# Extract items
episodes = []
@ -463,7 +534,7 @@ class ContentApi:
except AttributeError:
continue
# This is not a episode
# This is not a video
if not path.startswith('/video'):
continue
@ -472,35 +543,42 @@ class ContentApi:
except AttributeError:
_LOGGER.warning('Found no episode_program for %s', title)
episode_program = None
try:
episode_duration = int(regex_episode_duration.search(item_html).group(1))
except AttributeError:
_LOGGER.warning('Found no episode_duration for %s', title)
episode_duration = None
try:
episode_video_id = regex_episode_video_id.search(item_html).group(1)
except AttributeError:
_LOGGER.warning('Found no episode_video_id for %s', title)
episode_video_id = None
try:
episode_image = unescape(regex_episode_image.search(item_html).group(1))
except AttributeError:
_LOGGER.warning('Found no episode_image for %s', title)
episode_image = None
try:
episode_timestamp = int(regex_episode_timestamp.search(item_html).group(1))
episode_badge = unescape(regex_episode_badge.search(item_html).group(1))
except AttributeError:
_LOGGER.warning('Found no episode_timestamp for %s', title)
episode_timestamp = None
episode_badge = None
description = title
if episode_badge:
description += "\n\n[B]%s[/B]" % episode_badge
# Episode
episodes.append(Episode(
path=path.lstrip('/'),
channel='', # TODO
title=title,
description=html_to_kodi(description),
duration=episode_duration,
uuid=episode_video_id,
aired=datetime.fromtimestamp(episode_timestamp) if episode_timestamp else None,
cover=episode_image,
program_title=episode_program,
))
@ -519,7 +597,7 @@ class ContentApi:
path=data['link'].lstrip('/'),
channel=data['pageInfo']['brand'],
title=data['title'],
description=data['description'],
description=html_to_kodi(data['description']),
aired=datetime.fromtimestamp(data.get('pageInfo', {}).get('publishDate')),
cover=data['images']['poster'],
background=data['images']['hero'],

View File

@ -5,6 +5,10 @@
<setting label="30802" type="text" id="username"/>
<setting label="30803" type="text" id="password" option="hidden"/>
</category>
<category label="30820"> <!-- Interface -->
<setting label="30820" type="lsep"/> <!-- Interface -->
<setting label="30821" type="bool" id="interface_show_unavailable" default="true"/>
</category>
<category label="30840"> <!-- Integrations -->
<setting label="30841" type="lsep"/> <!-- IPTV Manager -->
<setting label="30842" type="action" action="InstallAddon(service.iptv.manager)" option="close" visible="!System.HasAddon(service.iptv.manager)"/> <!-- Install IPTV Manager add-on -->

View File

@ -11,7 +11,7 @@ import unittest
import resources.lib.kodiutils as kodiutils
from resources.lib.viervijfzes import ResolvedStream
from resources.lib.viervijfzes.auth import AuthApi
from resources.lib.viervijfzes.content import ContentApi, Program, Episode, CACHE_PREVENT
from resources.lib.viervijfzes.content import ContentApi, Program, Episode, CACHE_PREVENT, Category
_LOGGER = logging.getLogger(__name__)
@ -27,10 +27,24 @@ class TestApi(unittest.TestCase):
self.assertIsInstance(programs, list)
self.assertIsInstance(programs[0], Program)
# def test_categories(self):
# categories = self._api.get_categories()
# self.assertIsInstance(categories, list)
# self.assertIsInstance(categories[0], Category)
def test_popular_programs(self):
for brand in [None, 'vier', 'vijf', 'zes', 'goplay']:
programs = self._api.get_popular_programs(brand)
self.assertIsInstance(programs, list)
self.assertIsInstance(programs[0], Program)
def test_recommendations(self):
categories = self._api.get_recommendation_categories()
self.assertIsInstance(categories, list)
def test_categories(self):
categories = self._api.get_categories()
self.assertIsInstance(categories, list)
self.assertIsInstance(categories[0], Category)
programs = self._api.get_category_content(int(categories[0].uuid))
self.assertIsInstance(programs, list)
self.assertIsInstance(programs[0], Program)
def test_episodes(self):
for program in ['auwch', 'zo-man-zo-vrouw']:

View File

@ -34,6 +34,9 @@ class TestRouting(unittest.TestCase):
def test_catalog_menu(self):
routing.run([routing.url_for(addon.show_catalog), '0', ''])
def test_recommendations_menu(self):
routing.run([routing.url_for(addon.show_recommendations), '0', ''])
def test_catalog_channel_menu(self):
routing.run([routing.url_for(addon.show_channel_catalog, channel='Play4'), '0', ''])