3
.gitignore
vendored
@ -15,3 +15,6 @@ Thumbs.db
|
|||||||
# Testing
|
# Testing
|
||||||
tests/home/userdata/addon_data
|
tests/home/userdata/addon_data
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
Pipfile
|
||||||
|
Pipfile.lock
|
||||||
|
10
README.md
@ -4,18 +4,18 @@
|
|||||||
[![License: GPLv3](https://img.shields.io/badge/License-GPLv3-yellow.svg)](https://opensource.org/licenses/GPL-3.0)
|
[![License: GPLv3](https://img.shields.io/badge/License-GPLv3-yellow.svg)](https://opensource.org/licenses/GPL-3.0)
|
||||||
[![Contributors](https://img.shields.io/github/contributors/add-ons/plugin.video.viervijfzes.svg)](https://github.com/add-ons/plugin.video.viervijfzes/graphs/contributors)
|
[![Contributors](https://img.shields.io/github/contributors/add-ons/plugin.video.viervijfzes.svg)](https://github.com/add-ons/plugin.video.viervijfzes/graphs/contributors)
|
||||||
|
|
||||||
# VIER-VIJF-ZES Kodi add-on
|
# GoPlay Kodi add-on
|
||||||
|
|
||||||
*plugin.video.viervijfzes* is een Kodi add-on om de video-on-demand content van [vier.be](https://www.vier.be/), [vijf.be](https://www.vijf.be/) en [zestv.be](https://www.zestv.be/) te bekijken.
|
*plugin.video.viervijfzes* is een Kodi add-on om de video-on-demand content van [goplay.be](https://www.goplay.be/) te bekijken.
|
||||||
|
|
||||||
> Note: Je moet eerst een account aanmaken op één van bovenstaande websites.
|
> Note: Je moet eerst een account aanmaken.
|
||||||
|
|
||||||
Meer informatie kan je vinden op de [Wiki pagina](https://github.com/add-ons/plugin.video.viervijfzes/wiki).
|
Meer informatie kan je vinden op de [Wiki pagina](https://github.com/add-ons/plugin.video.viervijfzes/wiki).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
De volgende features worden ondersteund:
|
De volgende features worden ondersteund:
|
||||||
* Bekijk on-demand content van VIER, VIJF en ZES
|
* Bekijk on-demand content van Play4, Play5 en Play6
|
||||||
* Programma's rechtstreeks afspelen vanuit de tv-gids
|
* Programma's rechtstreeks afspelen vanuit de tv-gids
|
||||||
* Doorzoeken van alle programma's
|
* Doorzoeken van alle programma's
|
||||||
* Afspelen van gerelateerde Youtube content
|
* Afspelen van gerelateerde Youtube content
|
||||||
@ -34,4 +34,4 @@ De volgende features worden ondersteund:
|
|||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
Deze add-on wordt niet ondersteund door SBS Belgium, en wordt aangeboden 'as is', zonder enige garantie.
|
Deze add-on wordt niet ondersteund door SBS Belgium, en wordt aangeboden 'as is', zonder enige garantie.
|
||||||
De logo's van VIER, VIJF en ZES zijn eigendom van SBS België.
|
De logo's van GoPlay, Play4, Play5 en Play6 zijn eigendom van SBS België.
|
||||||
|
14
addon.xml
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.viervijfzes" name="VIER-VIJF-ZES" version="0.3.1" provider-name="Michaël Arnauts">
|
<addon id="plugin.video.viervijfzes" name="GoPlay" version="0.3.1" provider-name="Michaël Arnauts">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.26.0"/>
|
<import addon="xbmc.python" version="2.26.0"/>
|
||||||
<import addon="script.module.dateutil" version="2.6.0"/>
|
<import addon="script.module.dateutil" version="2.6.0"/>
|
||||||
@ -14,12 +14,12 @@
|
|||||||
</extension>
|
</extension>
|
||||||
<extension point="xbmc.service" library="service_entry.py"/>
|
<extension point="xbmc.service" library="service_entry.py"/>
|
||||||
<extension point="xbmc.addon.metadata">
|
<extension point="xbmc.addon.metadata">
|
||||||
<summary lang="nl_NL">Bekijk programma's van VIER, VIJF en ZES.</summary>
|
<summary lang="nl_NL">Bekijk programma's van Play4, Play5 en Play6.</summary>
|
||||||
<description lang="nl_NL">Deze add-on geeft toegang tot de programma's die aangeboden worden op de websites van VIER, VIJF en ZES.</description>
|
<description lang="nl_NL">Deze add-on geeft toegang tot de programma's die aangeboden worden op de websites van Play4, Play5 en Play6.</description>
|
||||||
<disclaimer lang="nl_NL">Deze add-on wordt niet ondersteund door SBS België, en wordt aangeboden 'as is', zonder enige garantie. De logo's van VIER, VIJF en ZES zijn eigendom van SBS België.</disclaimer>
|
<disclaimer lang="nl_NL">Deze add-on wordt niet ondersteund door SBS België, en wordt aangeboden 'as is', zonder enige garantie. De logo's van Play4, Play5 en Play6 zijn eigendom van SBS België.</disclaimer>
|
||||||
<summary lang="en_GB">Watch content from VIER, VIJF and ZES.</summary>
|
<summary lang="en_GB">Watch content from Play4, Play5 and Play6.</summary>
|
||||||
<description lang="en_GB">This add-on gives access to video-on-demand content available on the websites of VIER, VIJF and ZES.</description>
|
<description lang="en_GB">This add-on gives access to video-on-demand content available on the websites of Play4, Play5 and Play6.</description>
|
||||||
<disclaimer lang="en_GB">This add-on is not officially commissioned/supported by SBS Belgium and is provided 'as is' without any warranty of any kind. The VIER, VIJF and ZES logos are property of SBS Belgium.</disclaimer>
|
<disclaimer lang="en_GB">This add-on is not officially commissioned/supported by SBS Belgium and is provided 'as is' without any warranty of any kind. The Play4, Play5 and Play6 logos are property of SBS Belgium.</disclaimer>
|
||||||
<platform>all</platform>
|
<platform>all</platform>
|
||||||
<license>GPL-3.0-only</license>
|
<license>GPL-3.0-only</license>
|
||||||
<news>v0.3.1 (2020-11-28)
|
<news>v0.3.1 (2020-11-28)
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 21 KiB |
@ -35,18 +35,18 @@ def show_channel_menu(channel):
|
|||||||
Channels().show_channel_menu(channel)
|
Channels().show_channel_menu(channel)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/channels/<channel>/categories')
|
# @routing.route('/channels/<channel>/categories')
|
||||||
def show_channel_categories(channel):
|
# def show_channel_categories(channel):
|
||||||
""" Shows TV Channel categories """
|
# """ Shows TV Channel categories """
|
||||||
from resources.lib.modules.channels import Channels
|
# from resources.lib.modules.channels import Channels
|
||||||
Channels().show_channel_categories(channel)
|
# Channels().show_channel_categories(channel)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/channels/<channel>/categories/<category>')
|
# @routing.route('/channels/<channel>/categories/<category>')
|
||||||
def show_channel_category(channel, category):
|
# def show_channel_category(channel, category):
|
||||||
""" Shows TV Channel categories """
|
# """ Shows TV Channel categories """
|
||||||
from resources.lib.modules.channels import Channels
|
# from resources.lib.modules.channels import Channels
|
||||||
Channels().show_channel_category(channel, category)
|
# Channels().show_channel_category(channel, category)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/channels/<channel>/tvguide')
|
@routing.route('/channels/<channel>/tvguide')
|
||||||
@ -63,6 +63,13 @@ def show_channel_tvguide_detail(channel=None, date=None):
|
|||||||
TvGuide().show_detail(channel, date)
|
TvGuide().show_detail(channel, date)
|
||||||
|
|
||||||
|
|
||||||
|
@routing.route('/channels/<channel>/catalog')
|
||||||
|
def show_channel_catalog(channel):
|
||||||
|
""" Show the catalog of a channel """
|
||||||
|
from resources.lib.modules.catalog import Catalog
|
||||||
|
Catalog().show_catalog_channel(channel)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/catalog')
|
@routing.route('/catalog')
|
||||||
def show_catalog():
|
def show_catalog():
|
||||||
""" Show the catalog """
|
""" Show the catalog """
|
||||||
@ -70,32 +77,25 @@ def show_catalog():
|
|||||||
Catalog().show_catalog()
|
Catalog().show_catalog()
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/catalog/<channel>')
|
@routing.route('/catalog/<program>')
|
||||||
def show_channel_catalog(channel):
|
def show_catalog_program(program):
|
||||||
""" Show a category in the catalog """
|
|
||||||
from resources.lib.modules.catalog import Catalog
|
|
||||||
Catalog().show_catalog_channel(channel)
|
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/catalog/<channel>/<program>')
|
|
||||||
def show_catalog_program(channel, program):
|
|
||||||
""" Show a program from the catalog """
|
""" Show a program from the catalog """
|
||||||
from resources.lib.modules.catalog import Catalog
|
from resources.lib.modules.catalog import Catalog
|
||||||
Catalog().show_program(channel, program)
|
Catalog().show_program(program)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/catalog/<channel>/<program>/clips')
|
@routing.route('/catalog/<program>/clips')
|
||||||
def show_catalog_program_clips(channel, program):
|
def show_catalog_program_clips(program):
|
||||||
""" Show the clips from a program """
|
""" Show the clips from a program """
|
||||||
from resources.lib.modules.catalog import Catalog
|
from resources.lib.modules.catalog import Catalog
|
||||||
Catalog().show_program_clips(channel, program)
|
Catalog().show_program_clips(program)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/catalog/<channel>/<program>/season/<season>')
|
@routing.route('/catalog/<program>/season/<season>')
|
||||||
def show_catalog_program_season(channel, program, season):
|
def show_catalog_program_season(program, season):
|
||||||
""" Show a season from a program """
|
""" Show a season from a program """
|
||||||
from resources.lib.modules.catalog import Catalog
|
from resources.lib.modules.catalog import Catalog
|
||||||
Catalog().show_program_season(channel, program, season)
|
Catalog().show_program_season(program, season)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/search')
|
@routing.route('/search')
|
||||||
@ -127,8 +127,8 @@ def play_catalog(uuid):
|
|||||||
Player().play(uuid)
|
Player().play(uuid)
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/play/page/<channel>/<page>')
|
@routing.route('/play/page/<page>')
|
||||||
def play_from_page(channel, page):
|
def play_from_page(page):
|
||||||
""" Play the requested item """
|
""" Play the requested item """
|
||||||
try: # Python 3
|
try: # Python 3
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
@ -136,7 +136,7 @@ def play_from_page(channel, page):
|
|||||||
from urllib import unquote
|
from urllib import unquote
|
||||||
|
|
||||||
from resources.lib.modules.player import Player
|
from resources.lib.modules.player import Player
|
||||||
Player().play_from_page(channel, unquote(page))
|
Player().play_from_page(unquote(page))
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/metadata/update')
|
@routing.route('/metadata/update')
|
||||||
|
@ -8,7 +8,6 @@ import logging
|
|||||||
from resources.lib import kodiutils
|
from resources.lib import kodiutils
|
||||||
from resources.lib.kodiutils import TitleItem
|
from resources.lib.kodiutils import TitleItem
|
||||||
from resources.lib.modules.menu import Menu
|
from resources.lib.modules.menu import Menu
|
||||||
from resources.lib.viervijfzes import CHANNELS
|
|
||||||
from resources.lib.viervijfzes.auth import AuthApi
|
from resources.lib.viervijfzes.auth import AuthApi
|
||||||
from resources.lib.viervijfzes.content import CACHE_PREVENT, ContentApi, UnavailableException
|
from resources.lib.viervijfzes.content import CACHE_PREVENT, ContentApi, UnavailableException
|
||||||
|
|
||||||
@ -26,9 +25,7 @@ class Catalog:
|
|||||||
def show_catalog(self):
|
def show_catalog(self):
|
||||||
""" Show all the programs of all channels """
|
""" Show all the programs of all channels """
|
||||||
try:
|
try:
|
||||||
items = []
|
items = self._api.get_programs()
|
||||||
for channel in list(CHANNELS):
|
|
||||||
items.extend(self._api.get_programs(channel))
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
kodiutils.notification(message=str(ex))
|
kodiutils.notification(message=str(ex))
|
||||||
raise
|
raise
|
||||||
@ -57,13 +54,12 @@ class Catalog:
|
|||||||
# Used for A-Z listing or when movies and episodes are mixed.
|
# Used for A-Z listing or when movies and episodes are mixed.
|
||||||
kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')
|
kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')
|
||||||
|
|
||||||
def show_program(self, channel, program_id):
|
def show_program(self, program_id):
|
||||||
""" Show a program from the catalog
|
""" Show a program from the catalog
|
||||||
:type channel: str
|
|
||||||
:type program_id: str
|
:type program_id: str
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
program = self._api.get_program(channel, program_id, extract_clips=True, cache=CACHE_PREVENT) # Use CACHE_PREVENT since we want fresh data
|
program = self._api.get_program(program_id, extract_clips=True, cache=CACHE_PREVENT) # Use CACHE_PREVENT since we want fresh data
|
||||||
except UnavailableException:
|
except UnavailableException:
|
||||||
kodiutils.ok_dialog(message=kodiutils.localize(30717)) # This program is not available in the catalogue.
|
kodiutils.ok_dialog(message=kodiutils.localize(30717)) # This program is not available in the catalogue.
|
||||||
kodiutils.end_of_directory()
|
kodiutils.end_of_directory()
|
||||||
@ -76,11 +72,9 @@ class Catalog:
|
|||||||
|
|
||||||
# Go directly to the season when we have only one season and no clips
|
# Go directly to the season when we have only one season and no clips
|
||||||
if not program.clips and len(program.seasons) == 1:
|
if not program.clips and len(program.seasons) == 1:
|
||||||
self.show_program_season(channel, program_id, list(program.seasons.values())[0].uuid)
|
self.show_program_season(program_id, list(program.seasons.values())[0].uuid)
|
||||||
return
|
return
|
||||||
|
|
||||||
studio = CHANNELS.get(program.channel, {}).get('studio_icon')
|
|
||||||
|
|
||||||
listing = []
|
listing = []
|
||||||
|
|
||||||
# Add an '* All seasons' entry when configured in Kodi
|
# Add an '* All seasons' entry when configured in Kodi
|
||||||
@ -88,7 +82,7 @@ class Catalog:
|
|||||||
listing.append(
|
listing.append(
|
||||||
TitleItem(
|
TitleItem(
|
||||||
title='* %s' % kodiutils.localize(30204), # * All seasons
|
title='* %s' % kodiutils.localize(30204), # * All seasons
|
||||||
path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season='-1'),
|
path=kodiutils.url_for('show_catalog_program_season', program=program_id, season='-1'),
|
||||||
art_dict={
|
art_dict={
|
||||||
'fanart': program.background,
|
'fanart': program.background,
|
||||||
},
|
},
|
||||||
@ -97,7 +91,6 @@ class Catalog:
|
|||||||
'title': kodiutils.localize(30204), # All seasons
|
'title': kodiutils.localize(30204), # All seasons
|
||||||
'plot': program.description,
|
'plot': program.description,
|
||||||
'set': program.title,
|
'set': program.title,
|
||||||
'studio': studio,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -107,7 +100,7 @@ class Catalog:
|
|||||||
listing.append(
|
listing.append(
|
||||||
TitleItem(
|
TitleItem(
|
||||||
title=season.title, # kodiutils.localize(30205, season=season.number), # Season {season}
|
title=season.title, # kodiutils.localize(30205, season=season.number), # Season {season}
|
||||||
path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season=season.uuid),
|
path=kodiutils.url_for('show_catalog_program_season', program=program_id, season=season.uuid),
|
||||||
art_dict={
|
art_dict={
|
||||||
'fanart': program.background,
|
'fanart': program.background,
|
||||||
},
|
},
|
||||||
@ -116,7 +109,6 @@ class Catalog:
|
|||||||
'title': kodiutils.localize(30205, season=season.number), # Season {season}
|
'title': kodiutils.localize(30205, season=season.number), # Season {season}
|
||||||
'plot': season.description,
|
'plot': season.description,
|
||||||
'set': program.title,
|
'set': program.title,
|
||||||
'studio': studio,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -126,7 +118,7 @@ class Catalog:
|
|||||||
listing.append(
|
listing.append(
|
||||||
TitleItem(
|
TitleItem(
|
||||||
title=kodiutils.localize(30059, program=program.title), # Clips for {program}
|
title=kodiutils.localize(30059, program=program.title), # Clips for {program}
|
||||||
path=kodiutils.url_for('show_catalog_program_clips', channel=channel, program=program_id),
|
path=kodiutils.url_for('show_catalog_program_clips', program=program_id),
|
||||||
art_dict={
|
art_dict={
|
||||||
'fanart': program.background,
|
'fanart': program.background,
|
||||||
},
|
},
|
||||||
@ -135,7 +127,6 @@ class Catalog:
|
|||||||
'title': kodiutils.localize(30059, program=program.title), # Clips for {program}
|
'title': kodiutils.localize(30059, program=program.title), # Clips for {program}
|
||||||
'plot': kodiutils.localize(30060, program=program.title), # Watch short clips of {program}
|
'plot': kodiutils.localize(30060, program=program.title), # Watch short clips of {program}
|
||||||
'set': program.title,
|
'set': program.title,
|
||||||
'studio': studio,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -143,14 +134,13 @@ class Catalog:
|
|||||||
# Sort by label. Some programs return seasons unordered.
|
# Sort by label. Some programs return seasons unordered.
|
||||||
kodiutils.show_listing(listing, 30003, content='tvshows')
|
kodiutils.show_listing(listing, 30003, content='tvshows')
|
||||||
|
|
||||||
def show_program_season(self, channel, program_id, season_uuid):
|
def show_program_season(self, program_id, season_uuid):
|
||||||
""" Show the episodes of a program from the catalog
|
""" Show the episodes of a program from the catalog
|
||||||
:type channel: str
|
|
||||||
:type program_id: str
|
:type program_id: str
|
||||||
:type season_uuid: str
|
:type season_uuid: str
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
program = self._api.get_program(channel, program_id)
|
program = self._api.get_program(program_id)
|
||||||
except UnavailableException:
|
except UnavailableException:
|
||||||
kodiutils.ok_dialog(message=kodiutils.localize(30717)) # This program is not available in the catalogue.
|
kodiutils.ok_dialog(message=kodiutils.localize(30717)) # This program is not available in the catalogue.
|
||||||
kodiutils.end_of_directory()
|
kodiutils.end_of_directory()
|
||||||
@ -168,14 +158,13 @@ class Catalog:
|
|||||||
# Sort by episode number by default. Takes seasons into account.
|
# Sort by episode number by default. Takes seasons into account.
|
||||||
kodiutils.show_listing(listing, 30003, content='episodes', sort=['episode', 'duration'])
|
kodiutils.show_listing(listing, 30003, content='episodes', sort=['episode', 'duration'])
|
||||||
|
|
||||||
def show_program_clips(self, channel, program_id):
|
def show_program_clips(self, program_id):
|
||||||
""" Show the clips of a program from the catalog
|
""" Show the clips of a program from the catalog
|
||||||
:type channel: str
|
|
||||||
:type program_id: str
|
:type program_id: str
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# We need to query the backend, since we don't cache clips.
|
# We need to query the backend, since we don't cache clips.
|
||||||
program = self._api.get_program(channel, program_id, extract_clips=True, cache=CACHE_PREVENT)
|
program = self._api.get_program(program_id, extract_clips=True, cache=CACHE_PREVENT)
|
||||||
except UnavailableException:
|
except UnavailableException:
|
||||||
kodiutils.ok_dialog(message=kodiutils.localize(30717)) # This program is not available in the catalogue.
|
kodiutils.ok_dialog(message=kodiutils.localize(30717)) # This program is not available in the catalogue.
|
||||||
kodiutils.end_of_directory()
|
kodiutils.end_of_directory()
|
||||||
|
@ -7,10 +7,9 @@ import logging
|
|||||||
|
|
||||||
from resources.lib import kodiutils
|
from resources.lib import kodiutils
|
||||||
from resources.lib.kodiutils import TitleItem
|
from resources.lib.kodiutils import TitleItem
|
||||||
from resources.lib.modules.menu import Menu
|
|
||||||
from resources.lib.viervijfzes import CHANNELS, STREAM_DICT
|
from resources.lib.viervijfzes import CHANNELS, STREAM_DICT
|
||||||
from resources.lib.viervijfzes.auth import AuthApi
|
from resources.lib.viervijfzes.auth import AuthApi
|
||||||
from resources.lib.viervijfzes.content import CACHE_AUTO, CACHE_ONLY, ContentApi
|
from resources.lib.viervijfzes.content import ContentApi
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -55,7 +54,6 @@ class Channels:
|
|||||||
'plot': None,
|
'plot': None,
|
||||||
'playcount': 0,
|
'playcount': 0,
|
||||||
'mediatype': 'video',
|
'mediatype': 'video',
|
||||||
'studio': channel.get('studio_icon'),
|
|
||||||
},
|
},
|
||||||
stream_dict=STREAM_DICT,
|
stream_dict=STREAM_DICT,
|
||||||
context_menu=context_menu
|
context_menu=context_menu
|
||||||
@ -74,7 +72,10 @@ class Channels:
|
|||||||
# Lookup the high resolution logo based on the channel name
|
# Lookup the high resolution logo based on the channel name
|
||||||
fanart = '{path}/resources/logos/{logo}'.format(path=kodiutils.addon_path(), logo=channel_info.get('background'))
|
fanart = '{path}/resources/logos/{logo}'.format(path=kodiutils.addon_path(), logo=channel_info.get('background'))
|
||||||
|
|
||||||
listing = [
|
listing = []
|
||||||
|
|
||||||
|
if channel_info.get('epg_id'):
|
||||||
|
listing.append(
|
||||||
TitleItem(
|
TitleItem(
|
||||||
title=kodiutils.localize(30053, channel=channel_info.get('name')), # TV Guide for {channel}
|
title=kodiutils.localize(30053, channel=channel_info.get('name')), # TV Guide for {channel}
|
||||||
path=kodiutils.url_for('show_channel_tvguide', channel=channel),
|
path=kodiutils.url_for('show_channel_tvguide', channel=channel),
|
||||||
@ -85,7 +86,10 @@ class Channels:
|
|||||||
info_dict={
|
info_dict={
|
||||||
'plot': kodiutils.localize(30054, channel=channel_info.get('name')), # Browse the TV Guide for {channel}
|
'plot': kodiutils.localize(30054, channel=channel_info.get('name')), # Browse the TV Guide for {channel}
|
||||||
}
|
}
|
||||||
),
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
listing.append(
|
||||||
TitleItem(
|
TitleItem(
|
||||||
title=kodiutils.localize(30055, channel=channel_info.get('name')), # Catalog for {channel}
|
title=kodiutils.localize(30055, channel=channel_info.get('name')), # Catalog for {channel}
|
||||||
path=kodiutils.url_for('show_channel_catalog', channel=channel),
|
path=kodiutils.url_for('show_channel_catalog', channel=channel),
|
||||||
@ -96,19 +100,22 @@ class Channels:
|
|||||||
info_dict={
|
info_dict={
|
||||||
'plot': kodiutils.localize(30056, channel=channel_info.get('name')), # Browse the Catalog for {channel}
|
'plot': kodiutils.localize(30056, channel=channel_info.get('name')), # Browse the Catalog for {channel}
|
||||||
}
|
}
|
||||||
),
|
)
|
||||||
TitleItem(
|
)
|
||||||
title=kodiutils.localize(30057, channel=channel_info.get('name')), # Categories for {channel}
|
|
||||||
path=kodiutils.url_for('show_channel_categories', channel=channel),
|
# listing.append(
|
||||||
art_dict={
|
# TitleItem(
|
||||||
'icon': 'DefaultGenre.png',
|
# title=kodiutils.localize(30057, channel=channel_info.get('name')), # Categories for {channel}
|
||||||
'fanart': fanart,
|
# path=kodiutils.url_for('show_channel_categories', channel=channel),
|
||||||
},
|
# art_dict={
|
||||||
info_dict={
|
# 'icon': 'DefaultGenre.png',
|
||||||
'plot': kodiutils.localize(30058, channel=channel_info.get('name')), # Browse the Categories for {channel}
|
# 'fanart': fanart,
|
||||||
}
|
# },
|
||||||
),
|
# info_dict={
|
||||||
]
|
# 'plot': kodiutils.localize(30058, channel=channel_info.get('name')), # Browse the Categories for {channel}
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
# Add YouTube channels
|
# Add YouTube channels
|
||||||
if kodiutils.get_cond_visibility('System.HasAddon(plugin.video.youtube)') != 0:
|
if kodiutils.get_cond_visibility('System.HasAddon(plugin.video.youtube)') != 0:
|
||||||
@ -125,57 +132,57 @@ class Channels:
|
|||||||
|
|
||||||
kodiutils.show_listing(listing, 30007, sort=['unsorted'])
|
kodiutils.show_listing(listing, 30007, sort=['unsorted'])
|
||||||
|
|
||||||
def show_channel_categories(self, channel):
|
# def show_channel_categories(self, channel):
|
||||||
""" Shows the categories of a channel
|
# """ Shows the categories of a channel
|
||||||
:type channel: str
|
# :type channel: str
|
||||||
"""
|
# """
|
||||||
categories = self._api.get_categories(channel)
|
# 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'])
|
||||||
|
|
||||||
listing = [
|
# def show_channel_category(self, channel, category_id):
|
||||||
TitleItem(
|
# """ Shows a selected category of a channel
|
||||||
title=category.title,
|
# :type channel: str
|
||||||
path=kodiutils.url_for('show_channel_category', channel=category.channel, category=category.uuid),
|
# :type category_id: str
|
||||||
art_dict={
|
# """
|
||||||
'icon': 'DefaultGenre.png',
|
# categories = self._api.get_categories(channel)
|
||||||
},
|
#
|
||||||
) for category in categories
|
# # Extract selected category
|
||||||
]
|
# category = next(category for category in categories if category.uuid == category_id)
|
||||||
|
# if not category:
|
||||||
kodiutils.show_listing(listing, 30007, sort=['unsorted'])
|
# raise Exception('Unknown category')
|
||||||
|
#
|
||||||
def show_channel_category(self, channel, category_id):
|
# # Add programs
|
||||||
""" Shows a selected category of a channel
|
# listing_programs = []
|
||||||
:type channel: str
|
# for item in category.programs:
|
||||||
:type category_id: str
|
# program = self._api.get_program(item.path, CACHE_ONLY) # Get program details, but from cache only
|
||||||
"""
|
#
|
||||||
categories = self._api.get_categories(channel)
|
# if program:
|
||||||
|
# listing_programs.append(Menu.generate_titleitem(program))
|
||||||
# Extract selected category
|
# else:
|
||||||
category = next(category for category in categories if category.uuid == category_id)
|
# listing_programs.append(Menu.generate_titleitem(item))
|
||||||
if not category:
|
#
|
||||||
raise Exception('Unknown category')
|
# # Add episodes
|
||||||
|
# listing_episodes = []
|
||||||
# Add programs
|
# for item in category.episodes:
|
||||||
listing_programs = []
|
# # We don't have the Program Name without making a request to the page, so we use CACHE_AUTO instead of CACHE_ONLY.
|
||||||
for item in category.programs:
|
# # This will make a request for each item in this view (about 12 items), but it goes quite fast.
|
||||||
program = self._api.get_program(channel, item.path, CACHE_ONLY) # Get program details, but from cache only
|
# # Results are cached, so this will only happen once.
|
||||||
|
# episode = self._api.get_episode(item.path, CACHE_AUTO)
|
||||||
if program:
|
#
|
||||||
listing_programs.append(Menu.generate_titleitem(program))
|
# if episode:
|
||||||
else:
|
# listing_episodes.append(Menu.generate_titleitem(episode))
|
||||||
listing_programs.append(Menu.generate_titleitem(item))
|
# else:
|
||||||
|
# listing_episodes.append(Menu.generate_titleitem(item))
|
||||||
# Add episodes
|
#
|
||||||
listing_episodes = []
|
# kodiutils.show_listing(listing_programs + listing_episodes, 30007, content='tvshows', sort=['unsorted'])
|
||||||
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(channel, 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'])
|
|
||||||
|
@ -41,7 +41,8 @@ class IPTVManager:
|
|||||||
"""Return JSON-STREAMS formatted information to IPTV Manager"""
|
"""Return JSON-STREAMS formatted information to IPTV Manager"""
|
||||||
streams = []
|
streams = []
|
||||||
for key, channel in CHANNELS.items():
|
for key, channel in CHANNELS.items():
|
||||||
item = dict(
|
if channel.get('iptv_id'):
|
||||||
|
streams.append(dict(
|
||||||
id=channel.get('iptv_id'),
|
id=channel.get('iptv_id'),
|
||||||
name=channel.get('name'),
|
name=channel.get('name'),
|
||||||
logo='special://home/addons/{addon}/resources/logos/{logo}'.format(addon=kodiutils.addon_id(),
|
logo='special://home/addons/{addon}/resources/logos/{logo}'.format(addon=kodiutils.addon_id(),
|
||||||
@ -49,9 +50,7 @@ class IPTVManager:
|
|||||||
preset=channel.get('iptv_preset'),
|
preset=channel.get('iptv_preset'),
|
||||||
stream='plugin://plugin.video.viervijfzes/play/live/{channel}'.format(channel=key),
|
stream='plugin://plugin.video.viervijfzes/play/live/{channel}'.format(channel=key),
|
||||||
vod='plugin://plugin.video.viervijfzes/play/epg/{channel}/{{date}}'.format(channel=key)
|
vod='plugin://plugin.video.viervijfzes/play/epg/{channel}/{{date}}'.format(channel=key)
|
||||||
)
|
))
|
||||||
|
|
||||||
streams.append(item)
|
|
||||||
|
|
||||||
return dict(version=1, streams=streams)
|
return dict(version=1, streams=streams)
|
||||||
|
|
||||||
@ -69,6 +68,7 @@ class IPTVManager:
|
|||||||
for key, channel in CHANNELS.items():
|
for key, channel in CHANNELS.items():
|
||||||
iptv_id = channel.get('iptv_id')
|
iptv_id = channel.get('iptv_id')
|
||||||
|
|
||||||
|
if channel.get('iptv_id'):
|
||||||
results[iptv_id] = []
|
results[iptv_id] = []
|
||||||
for date in ['yesterday', 'today', 'tomorrow']:
|
for date in ['yesterday', 'today', 'tomorrow']:
|
||||||
epg = epg_api.get_epg(key, date)
|
epg = epg_api.get_epg(key, date)
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import absolute_import, division, unicode_literals
|
|||||||
|
|
||||||
from resources.lib import kodiutils
|
from resources.lib import kodiutils
|
||||||
from resources.lib.kodiutils import TitleItem
|
from resources.lib.kodiutils import TitleItem
|
||||||
from resources.lib.viervijfzes import CHANNELS, STREAM_DICT
|
from resources.lib.viervijfzes import STREAM_DICT
|
||||||
from resources.lib.viervijfzes.content import Episode, Program
|
from resources.lib.viervijfzes.content import Episode, Program
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +70,6 @@ class Menu:
|
|||||||
info_dict = {
|
info_dict = {
|
||||||
'title': item.title,
|
'title': item.title,
|
||||||
'plot': item.description,
|
'plot': item.description,
|
||||||
'studio': CHANNELS.get(item.channel, {}).get('studio_icon'),
|
|
||||||
'aired': item.aired.strftime('%Y-%m-%d') if item.aired else None,
|
'aired': item.aired.strftime('%Y-%m-%d') if item.aired else None,
|
||||||
}
|
}
|
||||||
prop_dict = {}
|
prop_dict = {}
|
||||||
|
@ -35,13 +35,12 @@ class Player:
|
|||||||
kodiutils.ok_dialog(message=kodiutils.localize(30718, channel=channel_name.get('name'))) # There is no live stream available for {channel}.
|
kodiutils.ok_dialog(message=kodiutils.localize(30718, channel=channel_name.get('name'))) # There is no live stream available for {channel}.
|
||||||
kodiutils.end_of_directory()
|
kodiutils.end_of_directory()
|
||||||
|
|
||||||
def play_from_page(self, channel, path):
|
def play_from_page(self, path):
|
||||||
""" Play the requested item.
|
""" Play the requested item.
|
||||||
:type channel: string
|
|
||||||
:type path: string
|
:type path: string
|
||||||
"""
|
"""
|
||||||
# Get episode information
|
# Get episode information
|
||||||
episode = self._api.get_episode(channel, path)
|
episode = self._api.get_episode(path)
|
||||||
resolved_stream = None
|
resolved_stream = None
|
||||||
|
|
||||||
if episode is None:
|
if episode is None:
|
||||||
|
@ -181,4 +181,4 @@ class TvGuide:
|
|||||||
kodiutils.end_of_directory()
|
kodiutils.end_of_directory()
|
||||||
return
|
return
|
||||||
|
|
||||||
Player().play_from_page(channel, broadcast.video_url)
|
Player().play_from_page(broadcast.video_url)
|
||||||
|
@ -1,50 +1,70 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
""" SBS API """
|
""" GoPlay API """
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
CHANNELS = OrderedDict([
|
CHANNELS = OrderedDict([
|
||||||
('vier', dict(
|
('Play4', dict(
|
||||||
name='VIER',
|
name='Play4',
|
||||||
url='https://www.vier.be',
|
epg_id='vier',
|
||||||
logo='vier.png',
|
logo='play4.png',
|
||||||
background='vier-background.jpg',
|
background='play4-background.png',
|
||||||
studio_icon='vier',
|
|
||||||
iptv_preset=4,
|
iptv_preset=4,
|
||||||
iptv_id='vier.be',
|
iptv_id='play4.be',
|
||||||
youtube=[
|
youtube=[
|
||||||
dict(
|
dict(
|
||||||
label='VIER / VIJF',
|
label='GoPlay',
|
||||||
logo='vier.png',
|
logo='goplay.png',
|
||||||
path='plugin://plugin.video.youtube/user/viertv/',
|
path='plugin://plugin.video.youtube/user/viertv/',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
('vijf', dict(
|
('Play5', dict(
|
||||||
name='VIJF',
|
name='Play5',
|
||||||
url='https://www.vijf.be',
|
epg_id='vijf',
|
||||||
logo='vijf.png',
|
logo='play5.png',
|
||||||
background='vijf-background.jpg',
|
background='play5-background.png',
|
||||||
studio_icon='vijf',
|
|
||||||
iptv_preset=5,
|
iptv_preset=5,
|
||||||
iptv_id='vijf.be',
|
iptv_id='play5.be',
|
||||||
youtube=[
|
youtube=[
|
||||||
dict(
|
dict(
|
||||||
label='VIER / VIJF',
|
label='GoPlay',
|
||||||
logo='vijf.png',
|
logo='goplay.png',
|
||||||
path='plugin://plugin.video.youtube/user/viertv/',
|
path='plugin://plugin.video.youtube/user/viertv/',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
('zes', dict(
|
('Play6', dict(
|
||||||
name='ZES',
|
name='Play6',
|
||||||
url='https://www.zestv.be',
|
epg_id='zes',
|
||||||
logo='zes.png',
|
logo='play6.png',
|
||||||
background='zes-background.jpg',
|
background='play6-background.png',
|
||||||
studio_icon='zes',
|
|
||||||
iptv_preset=6,
|
iptv_preset=6,
|
||||||
iptv_id='zes.be',
|
iptv_id='play6.be',
|
||||||
|
youtube=[
|
||||||
|
dict(
|
||||||
|
label='GoPlay',
|
||||||
|
logo='goplay.png',
|
||||||
|
path='plugin://plugin.video.youtube/user/viertv/',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
# ('Play7', dict(
|
||||||
|
# name='Play7',
|
||||||
|
# epg_id='zeven',
|
||||||
|
# url='https://www.goplay.be',
|
||||||
|
# logo='play7.png',
|
||||||
|
# background='play7-background.png',
|
||||||
|
# iptv_preset=7,
|
||||||
|
# iptv_id='play7.be',
|
||||||
|
# youtube=[],
|
||||||
|
# )),
|
||||||
|
('GoPlay', dict(
|
||||||
|
name='Go Play',
|
||||||
|
url='https://www.goplay.be',
|
||||||
|
logo='goplay.png',
|
||||||
|
background='goplay-background.png',
|
||||||
youtube=[],
|
youtube=[],
|
||||||
))
|
))
|
||||||
])
|
])
|
||||||
|
@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class AuthApi:
|
class AuthApi:
|
||||||
""" VIER/VIJF/ZES Authentication API """
|
""" GoPlay Authentication API """
|
||||||
COGNITO_REGION = 'eu-west-1'
|
COGNITO_REGION = 'eu-west-1'
|
||||||
COGNITO_POOL_ID = 'eu-west-1_dViSsKM5Y'
|
COGNITO_POOL_ID = 'eu-west-1_dViSsKM5Y'
|
||||||
COGNITO_CLIENT_ID = '6s1h851s8uplco5h6mqh1jac8m'
|
COGNITO_CLIENT_ID = '6s1h851s8uplco5h6mqh1jac8m'
|
||||||
|
@ -13,7 +13,7 @@ from datetime import datetime
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from resources.lib.kodiutils import STREAM_DASH, STREAM_HLS
|
from resources.lib.kodiutils import STREAM_DASH, STREAM_HLS
|
||||||
from resources.lib.viervijfzes import CHANNELS, ResolvedStream
|
from resources.lib.viervijfzes import ResolvedStream
|
||||||
|
|
||||||
try: # Python 3
|
try: # Python 3
|
||||||
from html import unescape
|
from html import unescape
|
||||||
@ -169,14 +169,10 @@ class Category:
|
|||||||
|
|
||||||
|
|
||||||
class ContentApi:
|
class ContentApi:
|
||||||
""" VIER/VIJF/ZES Content API"""
|
""" GoPlay Content API"""
|
||||||
API_ENDPOINT = 'https://api.viervijfzes.be'
|
SITE_URL = 'https://www.goplay.be'
|
||||||
API2_ENDPOINT = 'https://api2.viervijfzes.be'
|
API_VIERVIJFZES = 'https://api.viervijfzes.be'
|
||||||
SITE_APIS = {
|
API_GOPLAY = 'https://api.goplay.be'
|
||||||
'vier': 'https://www.vier.be/api',
|
|
||||||
'vijf': 'https://www.vijf.be/api',
|
|
||||||
'zes': 'https://www.zestv.be/api',
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, auth=None, cache_path=None):
|
def __init__(self, auth=None, cache_path=None):
|
||||||
""" Initialise object """
|
""" Initialise object """
|
||||||
@ -184,64 +180,53 @@ class ContentApi:
|
|||||||
self._auth = auth
|
self._auth = auth
|
||||||
self._cache_path = cache_path
|
self._cache_path = cache_path
|
||||||
|
|
||||||
def get_programs(self, channel, cache=CACHE_AUTO):
|
def get_programs(self, channel=None, cache=CACHE_AUTO):
|
||||||
""" Get a list of all programs of the specified channel.
|
""" Get a list of all programs of the specified channel.
|
||||||
:type channel: str
|
|
||||||
:type cache: str
|
:type cache: str
|
||||||
:rtype list[Program]
|
:rtype list[Program]
|
||||||
"""
|
"""
|
||||||
if channel not in CHANNELS:
|
|
||||||
raise Exception('Unknown channel %s' % channel)
|
|
||||||
|
|
||||||
def update():
|
def update():
|
||||||
""" Fetch the program listing by scraping """
|
""" Fetch the program listing by scraping """
|
||||||
# Load webpage
|
# Load webpage
|
||||||
raw_html = self._get_url(CHANNELS[channel]['url'])
|
raw_html = self._get_url(self.SITE_URL + '/programmas')
|
||||||
|
|
||||||
# Parse programs
|
# Parse programs
|
||||||
regex_programs = re.compile(r'<a class="program-overview__link" href="(?P<path>[^"]+)">\s+'
|
regex_programs = re.compile(r'data-program="(?P<json>[^"]+)"', re.DOTALL)
|
||||||
r'<span class="program-overview__title">\s+(?P<title>[^<]+)</span>.*?'
|
|
||||||
r'</a>', re.DOTALL)
|
data = [
|
||||||
data = {
|
json.loads(unescape(item.group('json')))
|
||||||
item.group('path').lstrip('/'): unescape(item.group('title').strip())
|
|
||||||
for item in regex_programs.finditer(raw_html)
|
for item in regex_programs.finditer(raw_html)
|
||||||
}
|
]
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
raise Exception('No programs found for %s' % channel)
|
raise Exception('No programs found')
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
# Fetch listing from cache or update if needed
|
# Fetch listing from cache or update if needed
|
||||||
data = self._handle_cache(key=['programs', channel], cache_mode=cache, update=update, ttl=30 * 5)
|
data = self._handle_cache(key=['programs'], cache_mode=cache, update=update, ttl=30 * 5)
|
||||||
if not data:
|
if not data:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
programs = []
|
if channel:
|
||||||
for path in data:
|
programs = [
|
||||||
title = data[path]
|
self._parse_program_data(record) for record in data if record['pageInfo']['brand'] == channel
|
||||||
program = self.get_program(channel, path, cache=CACHE_ONLY) # Get program details, but from cache only
|
]
|
||||||
if program:
|
|
||||||
# Use program with metadata from cache
|
|
||||||
programs.append(program)
|
|
||||||
else:
|
else:
|
||||||
# Use program with the values that we've parsed from the page
|
programs = [
|
||||||
programs.append(Program(channel=channel,
|
self._parse_program_data(record) for record in data
|
||||||
path=path,
|
]
|
||||||
title=title))
|
|
||||||
return programs
|
return programs
|
||||||
|
|
||||||
def get_program(self, channel, path, extract_clips=False, cache=CACHE_AUTO):
|
def get_program(self, path, extract_clips=False, cache=CACHE_AUTO):
|
||||||
""" Get a Program object from the specified page.
|
""" Get a Program object from the specified page.
|
||||||
:type channel: str
|
|
||||||
:type path: str
|
:type path: str
|
||||||
:type extract_clips: bool
|
:type extract_clips: bool
|
||||||
:type cache: int
|
:type cache: int
|
||||||
:rtype Program
|
:rtype Program
|
||||||
"""
|
"""
|
||||||
if channel not in CHANNELS:
|
|
||||||
raise Exception('Unknown channel %s' % channel)
|
|
||||||
|
|
||||||
# We want to use the html to extract clips
|
# We want to use the html to extract clips
|
||||||
# This is the worst hack, since Python 2.7 doesn't support nonlocal
|
# This is the worst hack, since Python 2.7 doesn't support nonlocal
|
||||||
raw_html = [None]
|
raw_html = [None]
|
||||||
@ -249,7 +234,7 @@ class ContentApi:
|
|||||||
def update():
|
def update():
|
||||||
""" Fetch the program metadata by scraping """
|
""" Fetch the program metadata by scraping """
|
||||||
# Fetch webpage
|
# Fetch webpage
|
||||||
page = self._get_url(CHANNELS[channel]['url'] + '/' + path)
|
page = self._get_url(self.SITE_URL + '/' + path)
|
||||||
|
|
||||||
# Store a copy in the parent's raw_html var.
|
# Store a copy in the parent's raw_html var.
|
||||||
raw_html[0] = page
|
raw_html[0] = page
|
||||||
@ -262,7 +247,7 @@ class ContentApi:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
# Fetch listing from cache or update if needed
|
# Fetch listing from cache or update if needed
|
||||||
data = self._handle_cache(key=['program', channel, path], cache_mode=cache, update=update)
|
data = self._handle_cache(key=['program', path], cache_mode=cache, update=update)
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -270,25 +255,22 @@ class ContentApi:
|
|||||||
|
|
||||||
# Also extract clips if we did a real HTTP call
|
# Also extract clips if we did a real HTTP call
|
||||||
if extract_clips and raw_html[0]:
|
if extract_clips and raw_html[0]:
|
||||||
clips = self._extract_videos(raw_html[0], channel)
|
clips = self._extract_videos(raw_html[0])
|
||||||
program.clips = clips
|
program.clips = clips
|
||||||
|
|
||||||
return program
|
return program
|
||||||
|
|
||||||
def get_episode(self, channel, path, cache=CACHE_AUTO):
|
def get_episode(self, path, cache=CACHE_AUTO):
|
||||||
""" Get a Episode object from the specified page.
|
""" Get a Episode object from the specified page.
|
||||||
:type channel: str
|
|
||||||
:type path: str
|
:type path: str
|
||||||
:type cache: str
|
:type cache: str
|
||||||
:rtype Episode
|
:rtype Episode
|
||||||
"""
|
"""
|
||||||
if channel not in CHANNELS:
|
|
||||||
raise Exception('Unknown channel %s' % channel)
|
|
||||||
|
|
||||||
def update():
|
def update():
|
||||||
""" Fetch the program metadata by scraping """
|
""" Fetch the program metadata by scraping """
|
||||||
# Load webpage
|
# Load webpage
|
||||||
page = self._get_url(CHANNELS[channel]['url'] + '/' + path)
|
page = self._get_url(self.SITE_URL + '/' + path)
|
||||||
|
|
||||||
program_json = None
|
program_json = None
|
||||||
episode_json = None
|
episode_json = None
|
||||||
@ -299,7 +281,7 @@ class ContentApi:
|
|||||||
result = regex_video_data.search(page)
|
result = regex_video_data.search(page)
|
||||||
if result:
|
if result:
|
||||||
video_id = json.loads(unescape(result.group(1)))['id']
|
video_id = json.loads(unescape(result.group(1)))['id']
|
||||||
video_json_data = self._get_url('%s/video/%s' % (self.SITE_APIS[channel], video_id))
|
video_json_data = self._get_url('%s/api/video/%s' % (self.SITE_URL, video_id))
|
||||||
video_json = json.loads(video_json_data)
|
video_json = json.loads(video_json_data)
|
||||||
return dict(video=video_json)
|
return dict(video=video_json)
|
||||||
|
|
||||||
@ -320,7 +302,7 @@ class ContentApi:
|
|||||||
return dict(program=program_json, episode=episode_json)
|
return dict(program=program_json, episode=episode_json)
|
||||||
|
|
||||||
# Fetch listing from cache or update if needed
|
# Fetch listing from cache or update if needed
|
||||||
data = self._handle_cache(key=['episode', channel, path], cache_mode=cache, update=update)
|
data = self._handle_cache(key=['episode', path], cache_mode=cache, update=update)
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -344,7 +326,7 @@ class ContentApi:
|
|||||||
:type uuid: str
|
:type uuid: str
|
||||||
:rtype str
|
:rtype str
|
||||||
"""
|
"""
|
||||||
response = self._get_url(self.API_ENDPOINT + '/content/%s' % uuid, authentication=True)
|
response = self._get_url(self.API_VIERVIJFZES + '/content/%s' % uuid, authentication=True)
|
||||||
data = json.loads(response)
|
data = json.loads(response)
|
||||||
|
|
||||||
if 'videoDash' in data:
|
if 'videoDash' in data:
|
||||||
@ -353,7 +335,7 @@ class ContentApi:
|
|||||||
drm_key = data['drmKey']['S']
|
drm_key = data['drmKey']['S']
|
||||||
|
|
||||||
_LOGGER.debug('Fetching Authentication XML with drm_key %s', drm_key)
|
_LOGGER.debug('Fetching Authentication XML with drm_key %s', drm_key)
|
||||||
response_drm = self._get_url(self.API2_ENDPOINT + '/decode/%s' % drm_key, authentication=True)
|
response_drm = self._get_url(self.API_GOPLAY + '/restricted/decode/%s' % drm_key, authentication=True)
|
||||||
data_drm = json.loads(response_drm)
|
data_drm = json.loads(response_drm)
|
||||||
|
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
@ -371,68 +353,64 @@ class ContentApi:
|
|||||||
stream_type=STREAM_HLS,
|
stream_type=STREAM_HLS,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_categories(self, channel):
|
# def get_categories(self):
|
||||||
""" Get a list of all categories of the specified channel.
|
# """ Get a list of all categories.
|
||||||
:type channel: str
|
# :rtype list[Category]
|
||||||
:rtype list[Category]
|
# """
|
||||||
"""
|
# # Load webpage
|
||||||
if channel not in CHANNELS:
|
# raw_html = self._get_url(self.SITE_URL)
|
||||||
raise Exception('Unknown channel %s' % channel)
|
#
|
||||||
|
# # 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
|
||||||
|
|
||||||
# Load webpage
|
# @staticmethod
|
||||||
raw_html = self._get_url(CHANNELS[channel]['url'])
|
# def _extract_programs(html, channel):
|
||||||
|
# """ Extract Programs from HTML code """
|
||||||
# Categories regexes
|
# # Item regexes
|
||||||
regex_articles = re.compile(r'<article([^>]+)>(.*?)</article>', re.DOTALL)
|
# regex_item = re.compile(r'<a[^>]+?href="(?P<path>[^"]+)"[^>]+?>'
|
||||||
regex_submenu_id = re.compile(r'data-submenu-id="([^"]*)"') # splitted since the order might change
|
# r'.*?<h3 class="poster-teaser__title"><span>(?P<title>[^<]*)</span></h3>.*?'
|
||||||
regex_submenu_title = re.compile(r'data-submenu-title="([^"]*)"')
|
# r'</a>', re.DOTALL)
|
||||||
|
#
|
||||||
categories = []
|
# # Extract items
|
||||||
for result in regex_articles.finditer(raw_html):
|
# programs = []
|
||||||
article_info_html = result.group(1)
|
# for item in regex_item.finditer(html):
|
||||||
article_html = result.group(2)
|
# path = item.group('path')
|
||||||
category_title = regex_submenu_title.search(article_info_html).group(1)
|
# if path.startswith('/video'):
|
||||||
category_id = regex_submenu_id.search(article_info_html).group(1)
|
# continue
|
||||||
|
#
|
||||||
# Skip empty categories or 'All programs'
|
# title = unescape(item.group('title'))
|
||||||
if not category_id or category_id == 'programmas':
|
#
|
||||||
continue
|
# # Program
|
||||||
|
# programs.append(Program(
|
||||||
# Extract items
|
# path=path.lstrip('/'),
|
||||||
programs = self._extract_programs(article_html, channel)
|
# channel=channel,
|
||||||
episodes = self._extract_videos(article_html, channel)
|
# title=title,
|
||||||
categories.append(Category(uuid=category_id, channel=channel, title=category_title, programs=programs, episodes=episodes))
|
# ))
|
||||||
|
#
|
||||||
return categories
|
# return programs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_programs(html, channel):
|
def _extract_videos(html):
|
||||||
""" 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
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _extract_videos(html, channel):
|
|
||||||
""" Extract videos from HTML code """
|
""" Extract videos from HTML code """
|
||||||
# Item regexes
|
# Item regexes
|
||||||
regex_item = re.compile(r'<a[^>]+?href="(?P<path>[^"]+)"[^>]+?>.*?</a>', re.DOTALL)
|
regex_item = re.compile(r'<a[^>]+?href="(?P<path>[^"]+)"[^>]+?>.*?</a>', re.DOTALL)
|
||||||
@ -490,7 +468,7 @@ class ContentApi:
|
|||||||
# Episode
|
# Episode
|
||||||
episodes.append(Episode(
|
episodes.append(Episode(
|
||||||
path=path.lstrip('/'),
|
path=path.lstrip('/'),
|
||||||
channel=channel,
|
channel='', # TODO
|
||||||
title=title,
|
title=title,
|
||||||
duration=episode_duration,
|
duration=episode_duration,
|
||||||
uuid=episode_video_id,
|
uuid=episode_video_id,
|
||||||
@ -511,7 +489,7 @@ class ContentApi:
|
|||||||
program = Program(
|
program = Program(
|
||||||
uuid=data['id'],
|
uuid=data['id'],
|
||||||
path=data['link'].lstrip('/'),
|
path=data['link'].lstrip('/'),
|
||||||
channel=data['pageInfo']['site'],
|
channel=data['pageInfo']['brand'],
|
||||||
title=data['title'],
|
title=data['title'],
|
||||||
description=data['description'],
|
description=data['description'],
|
||||||
aired=datetime.fromtimestamp(data.get('pageInfo', {}).get('publishDate')),
|
aired=datetime.fromtimestamp(data.get('pageInfo', {}).get('publishDate')),
|
||||||
@ -524,7 +502,7 @@ class ContentApi:
|
|||||||
key: Season(
|
key: Season(
|
||||||
uuid=playlist['id'],
|
uuid=playlist['id'],
|
||||||
path=playlist['link'].lstrip('/'),
|
path=playlist['link'].lstrip('/'),
|
||||||
channel=playlist['pageInfo']['site'],
|
channel=playlist['pageInfo']['brand'],
|
||||||
title=playlist['title'],
|
title=playlist['title'],
|
||||||
description=playlist['pageInfo']['description'],
|
description=playlist['pageInfo']['description'],
|
||||||
number=playlist['episodes'][0]['seasonNumber'], # You did not see this
|
number=playlist['episodes'][0]['seasonNumber'], # You did not see this
|
||||||
|
@ -67,12 +67,12 @@ class EpgProgram:
|
|||||||
|
|
||||||
|
|
||||||
class EpgApi:
|
class EpgApi:
|
||||||
""" VIER/VIJF/ZES EPG API """
|
""" GoPlay EPG API """
|
||||||
|
|
||||||
EPG_ENDPOINTS = {
|
EPG_ENDPOINTS = {
|
||||||
'vier': 'https://www.vier.be/api/epg/{date}',
|
'Play4': 'https://www.goplay.be/api/epg/vier/{date}',
|
||||||
'vijf': 'https://www.vijf.be/api/epg/{date}',
|
'Play5': 'https://www.goplay.be/api/epg/vijf/{date}',
|
||||||
'zes': 'https://www.zestv.be/api/epg/{date}',
|
'Play6': 'https://www.goplay.be/api/epg/zes/{date}'
|
||||||
}
|
}
|
||||||
|
|
||||||
EPG_NO_BROADCAST = 'Geen uitzending'
|
EPG_NO_BROADCAST = 'Geen uitzending'
|
||||||
|
@ -14,7 +14,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class SearchApi:
|
class SearchApi:
|
||||||
""" VIER/VIJF/ZES Search API """
|
""" GoPlay Search API """
|
||||||
API_ENDPOINT = 'https://api.viervijfzes.be/search'
|
API_ENDPOINT = 'https://api.viervijfzes.be/search'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
BIN
resources/logos/goplay-background.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
resources/logos/goplay.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
resources/logos/play4-background.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
resources/logos/play4.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
resources/logos/play5-background.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
resources/logos/play5.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
resources/logos/play6-background.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
resources/logos/play6.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 9.9 KiB |
@ -11,7 +11,7 @@ import unittest
|
|||||||
import resources.lib.kodiutils as kodiutils
|
import resources.lib.kodiutils as kodiutils
|
||||||
from resources.lib.viervijfzes import ResolvedStream
|
from resources.lib.viervijfzes import ResolvedStream
|
||||||
from resources.lib.viervijfzes.auth import AuthApi
|
from resources.lib.viervijfzes.auth import AuthApi
|
||||||
from resources.lib.viervijfzes.content import ContentApi, Program, Episode, Category, CACHE_PREVENT
|
from resources.lib.viervijfzes.content import ContentApi, Program, Episode, CACHE_PREVENT
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -23,39 +23,36 @@ class TestApi(unittest.TestCase):
|
|||||||
self._api = ContentApi(auth, cache_path=kodiutils.get_cache_path())
|
self._api = ContentApi(auth, cache_path=kodiutils.get_cache_path())
|
||||||
|
|
||||||
def test_programs(self):
|
def test_programs(self):
|
||||||
for channel in ['vier', 'vijf', 'zes']:
|
programs = self._api.get_programs()
|
||||||
programs = self._api.get_programs(channel)
|
|
||||||
self.assertIsInstance(programs, list)
|
self.assertIsInstance(programs, list)
|
||||||
self.assertIsInstance(programs[0], Program)
|
self.assertIsInstance(programs[0], Program)
|
||||||
|
|
||||||
def test_categories(self):
|
# def test_categories(self):
|
||||||
for channel in ['vier', 'vijf', 'zes']:
|
# categories = self._api.get_categories()
|
||||||
categories = self._api.get_categories(channel)
|
# self.assertIsInstance(categories, list)
|
||||||
self.assertIsInstance(categories, list)
|
# self.assertIsInstance(categories[0], Category)
|
||||||
if categories:
|
|
||||||
self.assertIsInstance(categories[0], Category)
|
|
||||||
|
|
||||||
def test_episodes(self):
|
def test_episodes(self):
|
||||||
for channel, program in [('vier', 'auwch'), ('vijf', 'zo-man-zo-vrouw')]:
|
for program in ['auwch', 'zo-man-zo-vrouw']:
|
||||||
program = self._api.get_program(channel, program, cache=CACHE_PREVENT)
|
program = self._api.get_program(program, cache=CACHE_PREVENT)
|
||||||
self.assertIsInstance(program, Program)
|
self.assertIsInstance(program, Program)
|
||||||
self.assertIsInstance(program.seasons, dict)
|
self.assertIsInstance(program.seasons, dict)
|
||||||
self.assertIsInstance(program.episodes, list)
|
self.assertIsInstance(program.episodes, list)
|
||||||
self.assertIsInstance(program.episodes[0], Episode)
|
self.assertIsInstance(program.episodes[0], Episode)
|
||||||
|
|
||||||
def test_clips(self):
|
def test_clips(self):
|
||||||
for channel, program in [('vier', 'gert-late-night')]:
|
for program in ['gert-late-night']:
|
||||||
program = self._api.get_program(channel, program, extract_clips=True, cache=CACHE_PREVENT)
|
program = self._api.get_program(program, extract_clips=True, cache=CACHE_PREVENT)
|
||||||
|
|
||||||
self.assertIsInstance(program.clips, list)
|
self.assertIsInstance(program.clips, list)
|
||||||
self.assertIsInstance(program.clips[0], Episode)
|
self.assertIsInstance(program.clips[0], Episode)
|
||||||
|
|
||||||
episode = self._api.get_episode(channel, program.clips[0].path, cache=CACHE_PREVENT)
|
episode = self._api.get_episode(program.clips[0].path, cache=CACHE_PREVENT)
|
||||||
self.assertIsInstance(episode, Episode)
|
self.assertIsInstance(episode, Episode)
|
||||||
|
|
||||||
@unittest.skipUnless(kodiutils.get_setting('username') and kodiutils.get_setting('password'), 'Skipping since we have no credentials.')
|
@unittest.skipUnless(kodiutils.get_setting('username') and kodiutils.get_setting('password'), 'Skipping since we have no credentials.')
|
||||||
def test_get_stream(self):
|
def test_get_stream(self):
|
||||||
program = self._api.get_program('vier', 'auwch')
|
program = self._api.get_program('auwch')
|
||||||
self.assertIsInstance(program, Program)
|
self.assertIsInstance(program, Program)
|
||||||
|
|
||||||
episode = program.episodes[0]
|
episode = program.episodes[0]
|
||||||
@ -64,7 +61,7 @@ class TestApi(unittest.TestCase):
|
|||||||
|
|
||||||
@unittest.skipUnless(kodiutils.get_setting('username') and kodiutils.get_setting('password'), 'Skipping since we have no credentials.')
|
@unittest.skipUnless(kodiutils.get_setting('username') and kodiutils.get_setting('password'), 'Skipping since we have no credentials.')
|
||||||
def test_get_drm_stream(self):
|
def test_get_drm_stream(self):
|
||||||
resolved_stream = self._api.get_stream_by_uuid('b56a41d9-5a6a-4ff0-b16a-56dc7b2e1f25')
|
resolved_stream = self._api.get_stream_by_uuid('a545bb4f-0f4c-4588-92a2-5086041c0e08') # Hawaii Five-O 8x12
|
||||||
self.assertIsInstance(resolved_stream, ResolvedStream)
|
self.assertIsInstance(resolved_stream, ResolvedStream)
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,17 +22,17 @@ class TestEpg(unittest.TestCase):
|
|||||||
self._epg = EpgApi()
|
self._epg = EpgApi()
|
||||||
|
|
||||||
def test_vier_today(self):
|
def test_vier_today(self):
|
||||||
programs = self._epg.get_epg('vier', date.today().strftime('%Y-%m-%d'))
|
programs = self._epg.get_epg('Play4', date.today().strftime('%Y-%m-%d'))
|
||||||
self.assertIsInstance(programs, list)
|
self.assertIsInstance(programs, list)
|
||||||
self.assertIsInstance(programs[0], EpgProgram)
|
self.assertIsInstance(programs[0], EpgProgram)
|
||||||
|
|
||||||
def test_vijf_today(self):
|
def test_vijf_today(self):
|
||||||
programs = self._epg.get_epg('vijf', date.today().strftime('%Y-%m-%d'))
|
programs = self._epg.get_epg('Play5', date.today().strftime('%Y-%m-%d'))
|
||||||
self.assertIsInstance(programs, list)
|
self.assertIsInstance(programs, list)
|
||||||
self.assertIsInstance(programs[0], EpgProgram)
|
self.assertIsInstance(programs[0], EpgProgram)
|
||||||
|
|
||||||
def test_zes_today(self):
|
def test_zes_today(self):
|
||||||
programs = self._epg.get_epg('zes', date.today().strftime('%Y-%m-%d'))
|
programs = self._epg.get_epg('Play6', date.today().strftime('%Y-%m-%d'))
|
||||||
self.assertIsInstance(programs, list)
|
self.assertIsInstance(programs, list)
|
||||||
self.assertIsInstance(programs[0], EpgProgram)
|
self.assertIsInstance(programs[0], EpgProgram)
|
||||||
|
|
||||||
@ -41,16 +41,16 @@ class TestEpg(unittest.TestCase):
|
|||||||
self._epg.get_epg('vtm', date.today().strftime('%Y-%m-%d'))
|
self._epg.get_epg('vtm', date.today().strftime('%Y-%m-%d'))
|
||||||
|
|
||||||
def test_vier_out_of_range(self):
|
def test_vier_out_of_range(self):
|
||||||
programs = self._epg.get_epg('vier', '2020-01-01')
|
programs = self._epg.get_epg('Play4', '2020-01-01')
|
||||||
self.assertEqual(programs, [])
|
self.assertEqual(programs, [])
|
||||||
|
|
||||||
def test_play_video_from_epg(self):
|
def test_play_video_from_epg(self):
|
||||||
epg_programs = self._epg.get_epg('vier', 'yesterday')
|
epg_programs = self._epg.get_epg('Play4', 'yesterday')
|
||||||
epg_program = [program for program in epg_programs if program.video_url][0]
|
epg_program = [program for program in epg_programs if program.video_url][0]
|
||||||
|
|
||||||
# Lookup the Episode data since we don't have an UUID
|
# Lookup the Episode data since we don't have an UUID
|
||||||
api = ContentApi(cache_path=kodiutils.get_cache_path())
|
api = ContentApi(cache_path=kodiutils.get_cache_path())
|
||||||
episode = api.get_episode(epg_program.channel, epg_program.video_url)
|
episode = api.get_episode(epg_program.video_url)
|
||||||
self.assertIsInstance(episode, Episode)
|
self.assertIsInstance(episode, Episode)
|
||||||
|
|
||||||
# def test_map_epg_genre(self):
|
# def test_map_epg_genre(self):
|
||||||
|
@ -29,27 +29,27 @@ class TestRouting(unittest.TestCase):
|
|||||||
|
|
||||||
def test_channels_menu(self):
|
def test_channels_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_channels), '0', ''])
|
routing.run([routing.url_for(addon.show_channels), '0', ''])
|
||||||
routing.run([routing.url_for(addon.show_channel_menu, channel='vier'), '0', ''])
|
routing.run([routing.url_for(addon.show_channel_menu, channel='Play4'), '0', ''])
|
||||||
|
|
||||||
def test_catalog_menu(self):
|
def test_catalog_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_catalog), '0', ''])
|
routing.run([routing.url_for(addon.show_catalog), '0', ''])
|
||||||
|
|
||||||
def test_catalog_channel_menu(self):
|
def test_catalog_channel_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_channel_catalog, channel='vier'), '0', ''])
|
routing.run([routing.url_for(addon.show_channel_catalog, channel='Play4'), '0', ''])
|
||||||
|
|
||||||
def test_catalog_program_menu(self):
|
def test_catalog_program_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_catalog_program, channel='vier', program='de-mol'), '0', ''])
|
routing.run([routing.url_for(addon.show_catalog_program, channel='Play4', program='de-mol'), '0', ''])
|
||||||
|
|
||||||
def test_catalog_program_season_menu(self):
|
def test_catalog_program_season_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_catalog_program_season, channel='vier', program='de-mol', season=-1), '0', ''])
|
routing.run([routing.url_for(addon.show_catalog_program_season, channel='Play4', program='de-mol', season=-1), '0', ''])
|
||||||
|
|
||||||
def test_search_menu(self):
|
def test_search_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_search), '0', ''])
|
routing.run([routing.url_for(addon.show_search), '0', ''])
|
||||||
routing.run([routing.url_for(addon.show_search, query='de mol'), '0', ''])
|
routing.run([routing.url_for(addon.show_search, query='de mol'), '0', ''])
|
||||||
|
|
||||||
def test_tvguide_menu(self):
|
def test_tvguide_menu(self):
|
||||||
routing.run([routing.url_for(addon.show_channel_tvguide, channel='vier'), '0', ''])
|
routing.run([routing.url_for(addon.show_channel_tvguide, channel='Play4'), '0', ''])
|
||||||
routing.run([routing.url_for(addon.show_channel_tvguide_detail, channel='vier', date='today'), '0', ''])
|
routing.run([routing.url_for(addon.show_channel_tvguide_detail, channel='Play4', date='today'), '0', ''])
|
||||||
|
|
||||||
# def test_metadata_update(self):
|
# def test_metadata_update(self):
|
||||||
# routing.run([routing.url_for(addon.metadata_update), '0', ''])
|
# routing.run([routing.url_for(addon.metadata_update), '0', ''])
|
||||||
|