Skip to content

Commit

Permalink
Hide direct url in the title.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelarnauts committed Jun 11, 2020
1 parent b0b33d5 commit 7b22d70
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 120 deletions.
12 changes: 8 additions & 4 deletions resources/lib/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ def refresh():

def play_from_contextmenu():
"""Play an item from the Context Menu"""
# Fetch selection from Kodi
program = ContextMenu.get_selection()
if program:
ContextMenu.play(program)
# Try to get a direct stream
stream = ContextMenu.get_stream()
if not stream:
# TODO: show message that we could not play this item
_LOGGER.error('We found no stream')
return

ContextMenu.play(stream)


def open_settings():
Expand Down
132 changes: 18 additions & 114 deletions resources/lib/modules/contextmenu.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import json
import logging
import os
import re
import sys
import time
from datetime import datetime
Expand All @@ -23,120 +24,23 @@ class ContextMenu:
def __init__(self):
"""Initialise the Context Menu Module"""

@classmethod
def play(cls, program):
"""Play the selected program."""
_LOGGER.debug('Asked to play %s', program)

# Get a list of addons that can play the selected channel
# We do the lookup based on Channel Name, since that's all we have
try:
addons = cls._get_addons_for_channel(program.get('channel'))
except IOError:
if kodiutils.yesno_dialog(message=kodiutils.localize(30713)): # The EPG data is not up to date...
from resources.lib.modules.addon import Addon
Addon.refresh(True)
return

if not addons:
# Channel was not found.
_LOGGER.debug('No Add-on was found to play %s', program.get('channel'))
kodiutils.notification(
message=kodiutils.localize(30710, channel=program.get('channel'))) # Could not find an Add-on...
return

if len(addons) == 1:
# Channel has one Add-on. Play it directly.
_LOGGER.debug('One Add-on was found to play %s: %s', program.get('channel'), addons)
cls._play(list(addons.values())[0], program)
return

# Ask the user to pick an Add-on
_LOGGER.debug('Multiple Add-on were found to play %s: %s', program.get('channel'), addons)
addons_list = list(addons)
ret = kodiutils.select(heading=kodiutils.localize(30711), options=addons_list) # Select an Add-on...
if ret == -1:
_LOGGER.debug('The selection to play an item from %s was canceled', program.get('channel'))
return

cls._play(addons.get(addons_list[ret]), program)

@classmethod
def _play(cls, uri, program):
"""Play the selected program with the specified URI."""
format_params = {}
if '{date}' in uri:
format_params.update({'date': program.get('start').isoformat()})

if '{duration}' in uri:
format_params.update({'duration': program.get('duration')})

if format_params:
uri = uri.format(**format_params)

_LOGGER.debug('Executing "%s"', uri)
kodiutils.execute_builtin('PlayMedia', uri)

@classmethod
def get_selection(cls):
"""Retrieve information about the selected ListItem."""

# The selected ListItem is available in sys.listitem, but there is not enough data that we can use to know what
# exact item was selected. Therefore, we use xbmc.getInfoLabel(ListItem.xxx), that references the currently
# selected ListItem. This is not always the same as the item where the Context Menu was opened on when the
# selection was moved really quick before the Python code was started. This often happens when using the mouse,
# but should not happen when using the keyboard or a remote control. Therefore, we do a check to see if the
# sys.listitem.getPath is the same as xbmc.getInfoLabel(ListItem.FolderPath) before continuing.
# For now, this is the best we can do.
#
# The sys.listitem.getPath() returns a string like "pvr://guide/0016/2020-05-28 09:24:47.epg". We could use the
# date to find out what item was selected, but we can't match the channel with something that makes sense. It's
# not the same ID as the ID in the JSON-RPC "PVR.GetChannels" or "PVR.GetChannelDetails" commands.
#
# The available fields are:
# * sys.listitem.getLabel() # Universiteit van Vlaanderen
# * sys.listitem.getPath() # pvr://guide/0016/2020-05-28 09:24:47.epg
#
# I would have preferred to use the Channel ID we use for for the epg (like een.be), but that isn't available.
# We only have a name (ListItem.ChannelName), or the channel number (ListItem.ChannelNumberLabel).

# Check if the selected item is also the intended item
if sys.listitem.getPath() != kodiutils.get_info_label('ListItem.FolderPath'): # pylint: disable=no-member
# We are in trouble. We know that the data we want to use is invalid, but there is nothing we can do.
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # Could not determine the selected program...
return None

# Load information from the ListItem
date = kodiutils.get_info_label('ListItem.Date')
duration = kodiutils.get_info_label('ListItem.Duration')
channel = kodiutils.to_unicode(kodiutils.get_info_label('ListItem.ChannelName'))

# Parse begin to a datetime. The ListItem.Date doesn't contain seconds
date_format = kodiutils.get_region('dateshort') + ' ' + kodiutils.get_region('time').replace(':%S', '')
try:
start = datetime.strptime(date, date_format)
except TypeError:
start = datetime(*(time.strptime(date, date_format)[0:6]))

# Parse duration to seconds
splitted = duration.split(':')
if len(splitted) == 1: # %S
seconds = int(splitted[0])

elif len(splitted) == 2: # %M:%S
seconds = int(splitted[0]) * 60 + int(splitted[1])

elif len(splitted) == 3: # %H:%M:%S
seconds = int(splitted[0]) * 3600 + int(splitted[1]) * 60 + int(splitted[2])

else:
raise Exception('Unknown duration %s' % duration)

return dict(
start=start,
duration=seconds,
channel=channel,
)
@staticmethod
def play(stream):
"""Play the requested stream"""
kodiutils.execute_builtin('PlayMedia', stream)

@staticmethod
def get_stream():
"""Retrieve a direct stream from the selected ListItem."""
# We use a clever way / ugly hack (pick your choice) to hide the direct stream in Kodi 18.
# Title [COLOR green][B]•[/B][/COLOR][CR][COLOR vod="plugin://plugin.video.example/play/whatever"][/COLOR]
label = sys.listitem.getLabel()
stream = re.search(r'\[COLOR vod="([^"]+)"\]', label)

if stream:
return stream.group(1)

return None

@staticmethod
def write_channels(channels):
Expand Down
8 changes: 6 additions & 2 deletions resources/lib/modules/iptvsimple.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,12 @@ def write_epg(cls, epg):

# Add an icon ourselves in Kodi 18
if title and item.get('stream') and kodiutils.kodi_version_major() < 19:
# Add [CR] to fix a bug that causes the [/B] to be visible
title = title + ' [COLOR green][B]•[/B][/COLOR][CR]'
# Add [CR] to fix a bug that causes the [/B] to be visible.
# We also use a clever way to hide the stream in the label so
# Kodi 18 can access the direct stream
title = '%s [COLOR green][B]•[/B][/COLOR][CR][COLOR vod="%s"][/COLOR]' % (
cls._xml_encode(title), cls._xml_encode(item.get('stream'))
)

program = '<programme start="{start}" stop="{stop}" channel="{channel}"{vod}>\n'.format(
start=start,
Expand Down

0 comments on commit 7b22d70

Please sign in to comment.