Merge pull request #930 from croneter/upnext
Support for the Up Next Kodi add-on
This commit is contained in:
commit
024bf31b83
2 changed files with 110 additions and 0 deletions
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in a new issue