Skip to content

Commit

Permalink
Use CAPI for EPG so we have more data (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelarnauts authored Nov 11, 2020
1 parent d27dd18 commit 116acc4
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 123 deletions.
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ python-dateutil
requests
git+git://github.com/dagwieers/kodi-plugin-routing.git@setup#egg=routing
tox
six
sakee
2 changes: 1 addition & 1 deletion resources/lib/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

kodilogging.config()
routing = Plugin() # pylint: disable=invalid-name
_LOGGER = logging.getLogger('addon')
_LOGGER = logging.getLogger(__name__)


@routing.route('/')
Expand Down
13 changes: 11 additions & 2 deletions resources/lib/kodiutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
'unsorted', 'title'
]

_LOGGER = logging.getLogger('kodiutils')
_LOGGER = logging.getLogger(__name__)


class TitleItem:
Expand Down Expand Up @@ -109,9 +109,18 @@ def addon_path():
return get_addon_info('path')


def translate_path(path):
"""Converts a Kodi special:// path to a normal path"""
try: # Kodi 19 alpha 2 and higher
from xbmcvfs import translatePath
except ImportError: # Kodi 19 alpha 1 and lower
return to_unicode(xbmc.translatePath(from_unicode(path)))
return translatePath(path)


def addon_profile():
"""Cache and return add-on profile"""
return to_unicode(xbmc.translatePath(ADDON.getAddonInfo('profile')))
return translate_path(ADDON.getAddonInfo('profile'))


def url_for(name, *args, **kwargs):
Expand Down
16 changes: 11 additions & 5 deletions resources/lib/modules/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def show_channels(self):
listing = []
for item in channels:
title_item = Menu.generate_titleitem_channel(item)
title_item.path = kodiutils.url_for('show_channel', channel_id=item.uid)
title_item.path = kodiutils.url_for('show_channel', channel_id=item.get_combi_id())
title_item.is_playable = False
listing.append(title_item)

Expand All @@ -64,7 +64,7 @@ def show_channel(self, channel_id):
:param str channel_id: The channel we want to display.
"""
channel = self._channel_api.get_asset(channel_id)
channel = self._channel_api.get_asset(channel_id.split(':')[0])

listing = []

Expand Down Expand Up @@ -143,9 +143,15 @@ def show_channel_guide_detail(self, channel_id, date):
:param str channel_id: The channel for which we want to show an EPG.
:param str date: The date to show.
"""
programs = self._epg_api.get_guide([channel_id], date)
# Lookup with CAPI
lookup_id = channel_id.split(':')[1]
programs = self._epg_api.get_guide_with_capi([lookup_id], date)

listing = [Menu.generate_titleitem_program(item, timeline=True) for item in programs.get(channel_id)]
# Lookup with TV API
# lookup_id = channel_id.split(':')[0]
# programs = self._epg_api.get_guide([lookup_id], date)

listing = [Menu.generate_titleitem_program(item, timeline=True) for item in programs.get(lookup_id)]

kodiutils.show_listing(listing, 30013, content='files')

Expand All @@ -154,7 +160,7 @@ def show_channel_replay(self, channel_id):
:param str channel_id: The channel for which we want to show the replay programs.
"""
programs = self._channel_api.get_replay(channel_id)
programs = self._channel_api.get_replay(channel_id.split(':')[0])

listing = []
for item in programs:
Expand Down
24 changes: 21 additions & 3 deletions resources/lib/modules/iptvmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from collections import defaultdict

from resources.lib import kodiutils
from resources.lib.solocoo import Credit
from resources.lib.solocoo.auth import AuthApi
from resources.lib.solocoo.channel import ChannelApi
from resources.lib.solocoo.epg import EpgApi
Expand Down Expand Up @@ -54,7 +55,7 @@ def send_channels(self):
streams.append(dict(
name=channel.title,
stream=kodiutils.url_for('play_asset', asset_id=channel.uid),
id=channel.uid,
id=channel.station_id,
logo=channel.icon,
preset=channel.number,
))
Expand All @@ -72,22 +73,39 @@ def send_epg(self):
# Load EPG data
channels = channel_api.get_channels()
for date in ['yesterday', 'today', 'tomorrow']:
for channel, programs in epg_api.get_guide([channel.uid for channel in channels], date).items():
for channel, programs in epg_api.get_guide_with_capi([channel.station_id for channel in channels], date).items():
for program in programs:
# Hide these items
if program.title == EpgApi.EPG_NO_BROADCAST:
continue

# Construct mapping for credits
program_credits = []
for credit in program.credit:
if credit.role == Credit.ROLE_ACTOR:
program_credits.append({'type': 'actor', 'name': credit.person, 'role': credit.character})
elif credit.role == Credit.ROLE_DIRECTOR:
program_credits.append({'type': 'director', 'name': credit.person})
elif credit.role == Credit.ROLE_PRODUCER:
program_credits.append({'type': 'producer', 'name': credit.person})
elif credit.role == Credit.ROLE_COMPOSER:
program_credits.append({'type': 'composer', 'name': credit.person})
elif credit.role == Credit.ROLE_PRESENTER:
program_credits.append({'type': 'presenter', 'name': credit.person})
elif credit.role == Credit.ROLE_GUEST:
program_credits.append({'type': 'guest', 'name': credit.person})

epg[channel].append(dict(
start=program.start.isoformat(),
stop=program.end.isoformat(),
title=program.title,
description=program.description,
subtitle=None,
episode='S%dE%d' % (program.season, program.episode) if program.season and program.episode else None,
genre=None,
genre=program.genres,
image=program.cover,
date=None,
credits=program_credits,
stream=kodiutils.url_for('play_asset', asset_id=program.uid) if program.replay else None))

return dict(version=1, epg=epg)
12 changes: 9 additions & 3 deletions resources/lib/modules/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from resources.lib import kodiutils
from resources.lib.kodiutils import TitleItem
from resources.lib.solocoo import Credit

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -107,9 +108,9 @@ def generate_titleitem_program(cls, item, timeline=False):
path=kodiutils.url_for('play_asset', asset_id=item.uid),
art_dict={
'cover': item.cover,
'icon': item.preview,
'thumb': item.preview,
'fanart': item.preview,
'icon': item.preview or item.cover,
'thumb': item.preview or item.cover,
'fanart': item.preview or item.cover,
},
info_dict={
'tvshowtitle': item.title,
Expand All @@ -122,6 +123,11 @@ def generate_titleitem_program(cls, item, timeline=False):
'aired': item.start.strftime('%Y-%m-%d'),
'date': item.start.strftime('%d.%m.%Y'),
'duration': item.duration,
'cast':
[(credit.person, credit.character) for credit in item.credit if credit.role == Credit.ROLE_ACTOR] +
[credit.person for credit in item.credit if credit.role in [Credit.ROLE_PRESENTER, Credit.ROLE_GUEST]],
'director': [credit.person for credit in item.credit if credit.role in [Credit.ROLE_DIRECTOR, Credit.ROLE_PRODUCER]],
# 'credits': [credit.person for credit in item.credit if credit.role in [Credit.ROLE_COMPOSER]],
},
prop_dict={
'inputstream.adaptive.play_timeshift_buffer': 'true', # Play from the beginning
Expand Down
15 changes: 11 additions & 4 deletions resources/lib/modules/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

from resources.lib import kodiutils
from resources.lib.modules.menu import Menu
from resources.lib.solocoo import Program, Channel
from resources.lib.solocoo.auth import AuthApi
from resources.lib.solocoo.channel import ChannelApi
from resources.lib.solocoo.exceptions import NotAvailableInOfferException, UnavailableException, InvalidTokenException
from resources.lib.solocoo.util import Program, Channel

_LOGGER = logging.getLogger(__name__)

Expand All @@ -32,20 +32,27 @@ def play_asset(self, asset_id):
:param string asset_id: The ID of the asset to play.
"""
# Get asset info
asset = self._channel_api.get_asset(asset_id)
if len(asset_id) == 32:
# a locId is 32 chars
asset = self._channel_api.get_asset_by_locid(asset_id)
else:
# an asset_id is 40 chars
asset = self._channel_api.get_asset(asset_id)

if isinstance(asset, Program):
item = Menu.generate_titleitem_program(asset)
elif isinstance(asset, Channel):
item = Menu.generate_titleitem_channel(asset)
else:
raise Exception('Unknown asset type: %s' % asset)

# Get stream info
try:
stream_info = self._channel_api.get_stream(asset_id)
stream_info = self._channel_api.get_stream(asset.uid)
except InvalidTokenException:
# Retry with fresh tokens
self._auth.login(True)
stream_info = self._channel_api.get_stream(asset_id)
stream_info = self._channel_api.get_stream(asset.uid)
except (NotAvailableInOfferException, UnavailableException) as exc:
_LOGGER.error(exc)
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable and can't be played right now.
Expand Down
2 changes: 1 addition & 1 deletion resources/lib/modules/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

from resources.lib import kodiutils
from resources.lib.modules.menu import Menu
from resources.lib.solocoo import Program, Channel
from resources.lib.solocoo.auth import AuthApi
from resources.lib.solocoo.search import SearchApi
from resources.lib.solocoo.util import Program, Channel

_LOGGER = logging.getLogger(__name__)

Expand Down
98 changes: 98 additions & 0 deletions resources/lib/solocoo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,101 @@
)),
# and many more, ...
])


class Channel:
""" Channel Object """

def __init__(self, uid, station_id, title, icon, preview, number, epg_now=None, epg_next=None, replay=False, radio=False, available=None):
"""
:param Program epg_now: The currently playing program on this channel.
:param Program epg_next: The next playing program on this channel.
"""
self.uid = uid
self.station_id = station_id
self.title = title
self.icon = icon
self.preview = preview
self.number = number
self.epg_now = epg_now
self.epg_next = epg_next
self.replay = replay
self.radio = radio

self.available = available

def __repr__(self):
return "%r" % self.__dict__

def get_combi_id(self):
""" Return a combination of the uid and the station_id. """
return "%s:%s" % (self.uid, self.station_id)


class StreamInfo:
""" Stream information """

def __init__(self, url, protocol, drm_protocol, drm_license_url, drm_certificate):
self.url = url
self.protocol = protocol
self.drm_protocol = drm_protocol
self.drm_license_url = drm_license_url
self.drm_certificate = drm_certificate

def __repr__(self):
return "%r" % self.__dict__


class Program:
""" Program object """

def __init__(self, uid, title, description, cover, preview, start, end, duration, channel_id, formats, genres, replay,
restart, age, series_id=None, season=None, episode=None, credit=None, available=None):
"""
:type credit: list[Credit]
"""
self.uid = uid
self.title = title
self.description = description
self.cover = cover
self.preview = preview
self.start = start
self.end = end
self.duration = duration

self.age = age
self.channel_id = channel_id

self.formats = formats
self.genres = genres

self.replay = replay
self.restart = restart

self.series_id = series_id
self.season = season
self.episode = episode

self.credit = credit

self.available = available

def __repr__(self):
return "%r" % self.__dict__


class Credit:
""" Credit object """

ROLE_ACTOR = 'Actor'
ROLE_COMPOSER = 'Composer'
ROLE_DIRECTOR = 'Director'
ROLE_GUEST = 'Guest'
ROLE_PRESENTER = 'Presenter'
ROLE_PRODUCER = 'Producer'

def __init__(self, role, person, character=None):
self.role = role
self.person = person
self.character = character
4 changes: 4 additions & 0 deletions resources/lib/solocoo/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ def __init__(self, username, password, tenant, token_path):
# Do login so we have valid tokens
self.login()

def get_tenant(self):
""" Return the tenant information. """
return self._tenant

def login(self, force=False):
""" Make a login request.
Expand Down
Loading

0 comments on commit 116acc4

Please sign in to comment.