Merge pull request #930 from croneter/upnext

Support for the Up Next Kodi add-on
This commit is contained in:
croneter 2019-07-14 13:00:37 +02:00 committed by GitHub
commit 024bf31b83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 110 additions and 0 deletions

View file

@ -7,10 +7,13 @@ from __future__ import absolute_import, division, unicode_literals
from logging import getLogger from logging import getLogger
from json import loads from json import loads
import copy import copy
import json
import binascii
import xbmc import xbmc
import xbmcgui import xbmcgui
from .plex_api import API
from .plex_db import PlexDB from .plex_db import PlexDB
from . import kodi_db from . import kodi_db
from .downloadutils import DownloadUtils as DU from .downloadutils import DownloadUtils as DU
@ -143,6 +146,8 @@ class KodiMonitor(xbmc.Monitor):
elif method == "System.OnQuit": elif method == "System.OnQuit":
LOG.info('Kodi OnQuit detected - shutting down') LOG.info('Kodi OnQuit detected - shutting down')
app.APP.stop_pkc = True app.APP.stop_pkc = True
elif method == 'Other.plugin.video.plexkodiconnect_play_action':
self._start_next_episode(data)
@staticmethod @staticmethod
def _hack_addon_paths_replay_video(): def _hack_addon_paths_replay_video():
@ -283,6 +288,18 @@ class KodiMonitor(xbmc.Monitor):
json_item.get('type'), json_item.get('type'),
json_item.get('file')) json_item.get('file'))
@staticmethod
def _start_next_episode(data):
"""
Used for the add-on Upnext to start playback of the next episode
"""
LOG.info('Upnext: Start playback of the next episode')
play_info = binascii.unhexlify(data[0])
play_info = json.loads(play_info)
app.APP.player.stop()
handle = 'RunPlugin(%s)' % play_info.get('handle')
xbmc.executebuiltin(handle.encode('utf-8'))
def PlayBackStart(self, data): def PlayBackStart(self, data):
""" """
Called whenever playback is started. Example data: Called whenever playback is started. Example data:
@ -415,6 +432,8 @@ class KodiMonitor(xbmc.Monitor):
status['playmethod'] = item.playmethod status['playmethod'] = item.playmethod
status['playcount'] = item.playcount status['playcount'] = item.playcount
LOG.debug('Set the player state: %s', status) LOG.debug('Set the player state: %s', status)
if not app.SYNC.direct_paths:
_notify_upnext(item)
def _playback_cleanup(ended=False): def _playback_cleanup(ended=False):
@ -537,6 +556,85 @@ def _clean_file_table():
LOG.debug('Done cleaning up Kodi file table') LOG.debug('Done cleaning up Kodi file table')
def _next_episode(current_api):
"""
Returns the xml for the next episode after the current one
Returns None if something went wrong or there is no next episode
"""
xml = PF.show_episodes(current_api.grandparent_id())
if xml is None:
return
for counter, episode in enumerate(xml):
api = API(episode)
if api.plex_id == current_api.plex_id:
break
else:
LOG.error('Did not find the episode with Plex id %s for show %s: %s',
current_api.plex_id, current_api.grandparent_id(),
current_api.grandparent_title())
return
try:
next_api = API(xml[counter + 1])
except IndexError:
# Was the last episode
return
return next_api
def _complete_artwork_keys(info):
"""
Make sure that the minimum set of keys is present in the info dict
"""
for key in ('tvshow.poster',
'tvshow.fanart',
'tvshow.landscape',
'tvshow.clearart',
'tvshow.clearlogo',
'thumb'):
if key not in info['art']:
info['art'][key] = ''
def _notify_upnext(item):
"""
Signals to the Kodi add-on Upnext that there is another episode after this
one.
Needed for add-on paths in order to prevent crashes when Upnext does this
by itself
"""
if not item.plex_type == v.PLEX_TYPE_EPISODE:
return
this_api = API(item.xml)
next_api = _next_episode(this_api)
if next_api is None:
return
info = {}
for key, api in (('current_episode', this_api),
('next_episode', next_api)):
info[key] = {
'episodeid': api.plex_id,
'tvshowid': api.grandparent_id(),
'title': api.title(),
'showtitle': api.grandparent_title(),
'plot': api.plot(),
'playcount': api.viewcount(),
'season': api.season_number(),
'episode': api.index(),
'firstaired': api.year(),
'rating': api.rating(),
'art': api.artwork(kodi_id=api.kodi_id,
kodi_type=api.kodi_type,
full_artwork=True)
}
_complete_artwork_keys(info[key])
info['play_info'] = {'handle': next_api.path(force_addon=True)}
sender = v.ADDON_ID.encode('utf-8')
method = 'upnext_data'.encode('utf-8')
data = binascii.hexlify(json.dumps(info))
data = '\\"[\\"{0}\\"]\\"'.format(data)
xbmc.executebuiltin('NotifyAll(%s, %s, %s)' % (sender, method, data))
class ContextMonitor(backgroundthread.KillableThread): class ContextMonitor(backgroundthread.KillableThread):
""" """
Detect the resume dialog for widgets. Could also be used to detect Detect the resume dialog for widgets. Could also be used to detect

View file

@ -1029,3 +1029,15 @@ def GetUserArtworkURL(username):
url = user.thumb url = user.thumb
LOG.debug("Avatar url for user %s is: %s", username, url) LOG.debug("Avatar url for user %s is: %s", username, url)
return url return url
def show_episodes(plex_id):
"""
Returns all episodes for the tv show with plex_id
"""
url = "{server}/library/metadata/%s/allLeaves" % plex_id
arguments = {
'checkFiles': 0,
'skipRefresh': 1,
}
return DownloadChunks(utils.extend_url(url, arguments))