Add support for playing from the Guide with IPTV Manager (#29)

This commit is contained in:
Michaël Arnauts 2020-05-25 20:41:38 +02:00 committed by GitHub
parent b5e36047f5
commit c8781424c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 157 additions and 9 deletions

View File

@ -23,6 +23,7 @@ De volgende features worden ondersteund:
* Programma's rechtstreeks afspelen vanuit de tv-gids
* Doorzoeken van alle programma's
* Afspelen van gerelateerde Youtube content
* Integratie met [IPTV Manager](https://github.com/add-ons/service.iptv.manager)
## Screenshots

View File

@ -148,6 +148,10 @@ msgctxt "#30717"
msgid "This program is not available in the catalogue."
msgstr ""
msgctxt "#30718"
msgid "There is no live stream available for {channel}."
msgstr ""
### SETTINGS
msgctxt "#30800"

View File

@ -149,6 +149,11 @@ msgctxt "#30717"
msgid "This program is not available in the catalogue."
msgstr "Dit programma is niet beschikbaar in de catalogus."
msgctxt "#30718"
msgid "There is no live stream available for {channel}."
msgstr "Er is geen live stream beschikbaar voor {channel}."
### SETTINGS
msgctxt "#30800"

View File

@ -106,8 +106,22 @@ def show_search(query=None):
Search().show_search(query)
@routing.route('/play/live/<channel>')
def play_live(channel):
""" Play the requested item """
from resources.lib.modules.player import Player
Player().live(channel)
@routing.route('/play/epg/<channel>/<timestamp>')
def play_epg(channel, timestamp):
""" Play the requested item """
from resources.lib.modules.tvguide import TvGuide
TvGuide().play_epg_datetime(channel, timestamp)
@routing.route('/play/catalog/<uuid>')
def play(uuid):
def play_catalog(uuid):
""" Play the requested item """
from resources.lib.modules.player import Player
Player().play(uuid)
@ -132,6 +146,20 @@ def metadata_update():
Metadata().update()
@routing.route('/iptv/channels')
def iptv_channels():
""" Generate channel data for the Kodi PVR integration """
from resources.lib.modules.iptvmanager import IPTVManager
IPTVManager(int(routing.args['port'][0])).send_channels() # pylint: disable=too-many-function-args
@routing.route('/iptv/epg')
def iptv_epg():
""" Generate EPG data for the Kodi PVR integration """
from resources.lib.modules.iptvmanager import IPTVManager
IPTVManager(int(routing.args['port'][0])).send_epg() # pylint: disable=too-many-function-args
def run(params):
""" Run the routing plugin """
routing.run(params)

View File

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
"""Implementation of IPTVManager class"""
from __future__ import absolute_import, division, unicode_literals
import logging
from datetime import timedelta
from resources.lib import kodiutils
from resources.lib.viervijfzes import CHANNELS
from resources.lib.viervijfzes.epg import EpgApi
_LOGGER = logging.getLogger(__name__)
class IPTVManager:
"""Interface to IPTV Manager"""
def __init__(self, port):
"""Initialize IPTV Manager object"""
self.port = port
def via_socket(func): # pylint: disable=no-self-argument
"""Send the output of the wrapped function to socket"""
def send(self):
"""Decorator to send over a socket"""
import json
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', self.port))
try:
sock.send(json.dumps(func())) # pylint: disable=not-callable
finally:
sock.close()
return send
@via_socket
def send_channels(): # pylint: disable=no-method-argument
"""Return JSON-STREAMS formatted information to IPTV Manager"""
streams = []
for key, channel in CHANNELS.items():
item = dict(
id=channel.get('iptv_id'),
name=channel.get('name'),
logo='special://home/addons/{addon}/resources/logos/{logo}'.format(addon=kodiutils.addon_id(), logo=channel.get('logo')),
preset=channel.get('iptv_preset'),
stream='plugin://plugin.video.viervijfzes/play/live/{channel}'.format(channel=key),
vod='plugin://plugin.video.viervijfzes/play/epg/{channel}/{{date}}'.format(channel=key)
)
streams.append(item)
return dict(version=1, streams=streams)
@via_socket
def send_epg(): # pylint: disable=no-method-argument
"""Return JSON-EPG formatted information to IPTV Manager"""
epg_api = EpgApi()
results = dict()
for key, channel in CHANNELS.items():
iptv_id = channel.get('iptv_id')
results[iptv_id] = []
for date in ['yesterday', 'today', 'tomorrow']:
epg = epg_api.get_epg(key, date)
results[iptv_id].extend([
dict(
start=program.start.isoformat(),
stop=(program.start + timedelta(seconds=program.duration)).isoformat(),
title=program.program_title,
description=program.description,
image=program.cover,
available=bool(program.video_url))
for program in epg if program.duration
])
return dict(version=1, epg=results)

View File

@ -124,7 +124,7 @@ class Menu:
else:
# We have an UUID and can play this item directly
# This is not preferred since we will lack metadata
path = kodiutils.url_for('play', uuid=item.uuid)
path = kodiutils.url_for('play_catalog', uuid=item.uuid)
return TitleItem(title=info_dict['title'],
path=path,

View File

@ -7,6 +7,7 @@ import logging
from resources.lib import kodiutils
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_awsidp import InvalidLoginException, AuthenticationException
from resources.lib.viervijfzes.content import ContentApi, UnavailableException, GeoblockedException
@ -25,6 +26,15 @@ class Player:
# Workaround for Raspberry Pi 3 and older
kodiutils.set_global_setting('videoplayer.useomxplayer', True)
@staticmethod
def live(channel):
""" Play the live channel.
:type channel: string
"""
channel_name = CHANNELS.get(channel, dict(name=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()
def play_from_page(self, channel, path):
""" Play the requested item.
:type channel: string
@ -34,6 +44,10 @@ class Player:
episode = self._api.get_episode(channel, path)
resolved_stream = None
if episode is None:
kodiutils.ok_dialog(message=kodiutils.localize(30712))
return
if episode.stream:
# We already have a resolved stream. Nice!
# We don't need credentials for these streams.

View File

@ -8,6 +8,7 @@ from datetime import datetime, timedelta
from resources.lib import kodiutils
from resources.lib.kodiutils import TitleItem
from resources.lib.modules.player import Player
from resources.lib.viervijfzes import STREAM_DICT
from resources.lib.viervijfzes.content import UnavailableException
from resources.lib.viervijfzes.epg import EpgApi
@ -171,9 +172,13 @@ class TvGuide:
"""
broadcast = self._epg.get_broadcast(channel, timestamp)
if not broadcast:
kodiutils.ok_dialog(heading=kodiutils.localize(30711), message=kodiutils.localize(30713)) # The requested video was not found in the guide.
kodiutils.ok_dialog(message=kodiutils.localize(30713)) # The requested video was not found in the guide.
kodiutils.end_of_directory()
return
kodiutils.container_update(
kodiutils.url_for('play', uuid=broadcast.video_url))
if not broadcast.video_url:
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable and can't be played right now.
kodiutils.end_of_directory()
return
Player().play_from_page(channel, broadcast.video_url)

View File

@ -11,6 +11,8 @@ CHANNELS = OrderedDict([
logo='vier.png',
background='vier-background.jpg',
studio_icon='vier',
iptv_preset=4,
iptv_id='vier.be',
youtube=[
dict(
label='VIER / VIJF',
@ -25,6 +27,8 @@ CHANNELS = OrderedDict([
logo='vijf.png',
background='vijf-background.jpg',
studio_icon='vijf',
iptv_preset=5,
iptv_id='vijf.be',
youtube=[
dict(
label='VIER / VIJF',
@ -39,6 +43,8 @@ CHANNELS = OrderedDict([
logo='zes.png',
background='zes-background.jpg',
studio_icon='zes',
iptv_preset=6,
iptv_id='zes.be',
youtube=[],
))
])

View File

@ -7,7 +7,8 @@ import json
import logging
from datetime import datetime, timedelta
import dateutil
import dateutil.parser
import dateutil.tz
import requests
_LOGGER = logging.getLogger('epg-api')
@ -90,8 +91,8 @@ class EpgApi:
duration = int(data.get('duration')) if data.get('duration') else None
# Check if this broadcast is currently airing
timestamp = datetime.now()
start = datetime.fromtimestamp(data.get('timestamp'))
timestamp = datetime.now().replace(tzinfo=dateutil.tz.gettz('CET'))
start = datetime.fromtimestamp(data.get('timestamp')).replace(tzinfo=dateutil.tz.gettz('CET'))
if duration:
airing = bool(start <= timestamp < (start + timedelta(seconds=duration)))
else:
@ -132,7 +133,7 @@ class EpgApi:
:rtype: EpgProgram
"""
# Parse to a real datetime
timestamp = dateutil.parser.parse(timestamp)
timestamp = dateutil.parser.parse(timestamp).replace(tzinfo=dateutil.tz.gettz('CET'))
# Load guide info for this date
programs = self.get_epg(channel=channel, date=timestamp.strftime('%Y-%m-%d'))

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<setting id="metadata_last_updated" visible="false"/>
<setting id="iptv.enabled" default="true" visible="false"/>
<setting id="iptv.channels_uri" default="plugin://plugin.video.viervijfzes/iptv/channels" visible="false"/>
<setting id="iptv.epg_uri" default="plugin://plugin.video.viervijfzes/iptv/epg" visible="false"/>
<category label="30800"> <!-- Credentials -->
<setting label="30801" type="lsep"/> <!-- Credentials -->
<setting label="30803" type="text" id="username"/>