Major Plex Companion overhaul, part 2

This commit is contained in:
croneter 2017-12-13 20:14:27 +01:00
parent cc347d5654
commit 9cac51d5c9
7 changed files with 258 additions and 217 deletions

View File

@ -2690,7 +2690,8 @@ class API():
plexitem = "plex_%s" % playurl
window('%s.runtime' % plexitem, value=str(userdata['Runtime']))
window('%s.type' % plexitem, value=itemtype)
window('%s.itemid' % plexitem, value=self.getRatingKey())
state.PLEX_IDS[tryDecode(playurl)] = self.getRatingKey()
# window('%s.itemid' % plexitem, value=self.getRatingKey())
window('%s.playcount' % plexitem, value=str(userdata['PlayCount']))
if itemtype == v.PLEX_TYPE_EPISODE:

View File

@ -362,6 +362,7 @@ def get_episodes(params):
def get_item(playerid):
"""
UNRELIABLE on playback startup! (as other JSON and Python Kodi functions)
Returns the following for the currently playing item:
{
u'title': u'Okja',

View File

@ -808,7 +808,7 @@ class Kodidb_Functions():
ids.append(row[0])
return ids
def getIdFromFilename(self, filename, path):
def video_id_from_filename(self, filename, path):
"""
Returns the tuple (itemId, type) where
itemId: Kodi DB unique Id for either movie or episode
@ -884,6 +884,34 @@ class Kodidb_Functions():
return
return itemId, typus
def music_id_from_filename(self, filename, path):
"""
Returns the Kodi song_id from the Kodi music database or None if not
found OR something went wrong.
"""
query = '''
SELECT idPath
FROM path
WHERE strPath = ?
'''
self.cursor.execute(query, (path,))
path_id = self.cursor.fetchall()
if len(path_id) != 1:
log.error('Found wrong number of path ids: %s for path %s, abort',
path_id, path)
return
query = '''
SELECT idSong
FROM song
WHERE strFileName = ? AND idPath = ?
'''
self.cursor.execute(query, (filename, path_id[0]))
song_id = self.cursor.fetchall()
if len(song_id) != 1:
log.info('Found wrong number of songs %s, abort', song_id)
return
return song_id[0]
def getUnplayedItems(self):
"""
VIDEOS
@ -1522,24 +1550,29 @@ class Kodidb_Functions():
self.cursor.execute(query, (kodi_id, kodi_type))
def get_kodiid_from_filename(file):
def kodiid_from_filename(path, kodi_type):
"""
Returns the tuple (kodiid, type) if we have a video in the database with
said filename, or (None, None)
Returns kodi_id if we have an item in the Kodi video or audio database with
said path. Feed with the Kodi itemtype, e.v. 'movie', 'song'
Returns None if not possible
"""
kodiid = None
typus = None
kodi_id = None
try:
filename = file.rsplit('/', 1)[1]
path = file.rsplit('/', 1)[0] + '/'
filename = path.rsplit('/', 1)[1]
path = path.rsplit('/', 1)[0] + '/'
except IndexError:
filename = file.rsplit('\\', 1)[1]
path = file.rsplit('\\', 1)[0] + '\\'
log.debug('Trying to figure out playing item from filename: %s '
'and path: %s' % (filename, path))
with GetKodiDB('video') as kodi_db:
try:
kodiid, typus = kodi_db.getIdFromFilename(filename, path)
except TypeError:
log.info('No kodi video element found with filename %s' % filename)
return (kodiid, typus)
filename = path.rsplit('\\', 1)[1]
path = path.rsplit('\\', 1)[0] + '\\'
if kodi_type == v.KODI_TYPE_SONG:
with GetKodiDB('music') as kodi_db:
try:
kodi_id, _ = kodi_db.music_id_from_filename(filename, path)
except TypeError:
log.info('No Kodi audio db element found for path %s', path)
else:
with GetKodiDB('video') as kodi_db:
try:
kodi_id, _ = kodi_db.video_id_from_filename(filename, path)
except TypeError:
log.info('No kodi video db element found for path %s', path)
return kodi_id

View File

@ -11,7 +11,7 @@ import plexdb_functions as plexdb
from utils import window, settings, CatchExceptions, tryDecode, tryEncode, \
plex_command
from PlexFunctions import scrobble
from kodidb_functions import get_kodiid_from_filename
from kodidb_functions import kodiid_from_filename
from PlexAPI import API
import json_rpc as js
import state
@ -185,68 +185,61 @@ class KodiMonitor(Monitor):
u'item': {u'type': u'movie', u'title': u''},
u'player': {u'playerid': 1, u'speed': 1}
}
Unfortunately VERY random inputs!
E.g. when using Widgets, Kodi doesn't tell us shit
"""
log.debug('PlayBackStart called with: %s', data)
# Get the type of media we're playing
try:
kodi_type = data['item']['type']
playerid = data['player']['playerid']
json_data = js.get_item(playerid)
except (TypeError, KeyError):
log.info('Aborting playback report - item is invalid for updates')
log.info('Aborting playback report - item invalid for updates %s',
data)
return
json_data = js.get_item(playerid)
path = json_data.get('file')
kodi_id = json_data.get('id')
if not path and not kodi_id:
log.info('Aborting playback report - no Kodi id or file for %s',
json_data)
return
# Plex id will NOT be set with direct paths
plex_id = state.PLEX_IDS.get(path)
try:
kodi_id = json_data['id']
kodi_type = json_data['type']
plex_type = v.PLEX_TYPE_FROM_KODI_TYPE[kodi_type]
except KeyError:
log.info('Aborting playback report - no Kodi id for %s', json_data)
return
# Get Plex' item id
with plexdb.Get_Plex_DB() as plex_db:
plex_dbitem = plex_db.getItem_byKodiId(kodi_id, kodi_type)
try:
plex_id = plex_dbitem[0]
plex_type = plex_dbitem[2]
except TypeError:
# No plex id, hence item not in the library. E.g. clips
plex_id = None
plex_type = None
# No Kodi id returned by Kodi, even if there is one. Ex: Widgets
if plex_id and not kodi_id:
with plexdb.Get_Plex_DB() as plex_db:
plex_dbitem = plex_db.getItem_byId(plex_id)
try:
kodi_id = plex_dbitem[0]
except TypeError:
kodi_id = None
# If using direct paths and starting playback from a widget
if not path.startswith('http'):
if not kodi_id:
kodi_id = kodiid_from_filename(path, kodi_type)
if not plex_id and kodi_id:
with plexdb.Get_Plex_DB() as plex_db:
plex_dbitem = plex_db.getItem_byKodiId(kodi_id, kodi_type)
try:
plex_id = plex_dbitem[0]
plex_type = plex_dbitem[2]
except TypeError:
# No plex id, hence item not in the library. E.g. clips
pass
state.PLAYER_STATES[playerid].update(js.get_player_props(playerid))
state.PLAYER_STATES[playerid]['file'] = json_data['file']
state.PLAYER_STATES[playerid]['kodi_id'] = kodi_id
state.PLAYER_STATES[playerid]['kodi_type'] = kodi_type
state.PLAYER_STATES[playerid]['plex_id'] = plex_id
state.PLAYER_STATES[playerid]['plex_type'] = plex_type
log.debug('Set the player state %s', state.PLAYER_STATES[playerid])
# Set other stuff like volume
state.PLAYER_STATES[playerid]['volume'] = js.get_volume()
state.PLAYER_STATES[playerid]['muted'] = js.get_muted()
return
# Switch subtitle tracks if applicable
subtitle = window('plex_%s.subtitle' % tryEncode(currentFile))
if window(tryEncode('plex_%s.playmethod' % currentFile)) \
== 'Transcode' and subtitle:
if window('plex_%s.subtitle' % currentFile) == 'None':
self.xbmcplayer.showSubtitles(False)
else:
self.xbmcplayer.setSubtitleStream(int(subtitle))
# Set some stuff if Kodi initiated playback
if ((settings('useDirectPaths') == "1" and not typus == "song")
or
(typus == "song" and settings('enableMusic') == "true")):
if self.StartDirectPath(plex_id,
typus,
tryEncode(currentFile)) is False:
log.error('Could not initiate monitoring; aborting')
return
# Save currentFile for cleanup later and to be able to access refs
window('plex_lastPlayedFiled', value=currentFile)
window('plex_currently_playing_itemid', value=plex_id)
window("plex_%s.itemid" % tryEncode(currentFile), value=plex_id)
log.info('Finish playback startup')
log.debug('Set the player state: %s', state.PLAYER_STATES[playerid])
def StartDirectPath(self, plex_id, type, currentFile):
"""

View File

@ -1,56 +1,68 @@
import logging
import re
import threading
from xbmc import sleep
"""
Manages getting playstate from Kodi and sending it to the PMS as well as
subscribed Plex Companion clients.
"""
from logging import getLogger
from re import sub
from threading import Thread, RLock
import downloadutils
from clientinfo import getXArgsDeviceInfo
from utils import window, kodi_time_to_millis
import PlexFunctions as pf
import state
import variables as v
import json_rpc as js
###############################################################################
log = logging.getLogger("PLEX."+__name__)
LOG = getLogger("PLEX." + __name__)
###############################################################################
# What is Companion controllable?
CONTROLLABLE = {
v.PLEX_TYPE_PHOTO: 'skipPrevious,skipNext,stop',
v.PLEX_TYPE_AUDIO: 'playPause,stop,volume,shuffle,repeat,seekTo,' \
v.PLEX_TYPE_AUDIO: 'playPause,stop,volume,shuffle,repeat,seekTo,'
'skipPrevious,skipNext,stepBack,stepForward',
v.PLEX_TYPE_VIDEO: 'playPause,stop,volume,audioStream,subtitleStream,' \
'seekTo,skipPrevious,skipNext,stepBack,stepForward'
v.PLEX_TYPE_VIDEO: 'playPause,stop,volume,shuffle,audioStream,'
'subtitleStream,seekTo,skipPrevious,skipNext,stepBack,stepForward'
}
class SubscriptionManager:
"""
Manages Plex companion subscriptions
"""
def __init__(self, RequestMgr, player, mgr):
self.serverlist = []
self.subscribers = {}
self.info = {}
self.lastkey = ""
self.containerKey = ""
self.ratingkey = ""
self.lastplayers = {}
self.lastinfo = {
'video': {},
'audio': {},
'picture': {}
}
self.containerKey = None
self.ratingkey = None
self.server = ""
self.protocol = "http"
self.port = ""
self.playerprops = {}
self.doUtils = downloadutils.DownloadUtils().downloadUrl
# In order to be able to signal a stop at the end
self.last_params = {}
self.lastplayers = {}
self.doUtils = downloadutils.DownloadUtils
self.xbmcplayer = player
self.playqueue = mgr.playqueue
self.RequestMgr = RequestMgr
@staticmethod
def _headers():
"""
Headers are different for Plex Companion!
"""
return {
'Content-type': 'text/plain',
'Connection': 'Keep-Alive',
'Keep-Alive': 'timeout=20',
'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER,
'Access-Control-Expose-Headers': 'X-Plex-Client-Identifier',
'X-Plex-Protocol': "1.0"
}
def getServerByHost(self, host):
if len(self.serverlist) == 1:
return self.serverlist[0]
@ -61,64 +73,80 @@ class SubscriptionManager:
return {}
def msg(self, players):
log.debug('players: %s', players)
LOG.debug('players: %s', players)
msg = v.XML_HEADER
msg += '<MediaContainer size="3" commandID="INSERTCOMMANDID"'
msg += ' machineIdentifier="%s">' % v.PKC_MACHINE_IDENTIFIER
msg += self.getTimelineXML(players.get(v.KODI_TYPE_AUDIO),
v.PLEX_TYPE_AUDIO)
msg += self.getTimelineXML(players.get(v.KODI_TYPE_PHOTO),
v.PLEX_TYPE_PHOTO)
msg += self.getTimelineXML(players.get(v.KODI_TYPE_VIDEO),
v.PLEX_TYPE_VIDEO)
msg += "\n</MediaContainer>"
log.debug('msg is: %s', msg)
msg += ' machineIdentifier="%s">\n' % v.PKC_MACHINE_IDENTIFIER
msg += self.get_timeline_xml(players.get(v.KODI_TYPE_AUDIO),
v.PLEX_TYPE_AUDIO)
msg += self.get_timeline_xml(players.get(v.KODI_TYPE_PHOTO),
v.PLEX_TYPE_PHOTO)
msg += self.get_timeline_xml(players.get(v.KODI_TYPE_VIDEO),
v.PLEX_TYPE_VIDEO)
msg += "</MediaContainer>"
LOG.debug('msg is: %s', msg)
return msg
def getTimelineXML(self, player, ptype):
if player is None:
status = 'stopped'
def _get_container_key(self, playerid):
key = None
playlistid = state.PLAYER_STATES[playerid]['playlistid']
LOG.debug('type: %s, playlistid: %s', type(playlistid), playlistid)
if playlistid != -1:
# -1 is Kodi's answer if there is no playlist
try:
key = self.playqueue.playqueues[playlistid].id
except (KeyError, IndexError, TypeError):
pass
if key is not None:
key = '/playQueues/%s' % key
else:
playerid = player['playerid']
info = state.PLAYER_STATES[playerid]
# save this info off so the server update can use it too
# self.playerprops[playerid] = info
status = ("paused", "playing")[info['speed']]
ret = ('\n <Timeline state="%s" controllable="%s" type="%s" '
'itemType="%s"' % (status, CONTROLLABLE[ptype], ptype, ptype))
if player is None:
ret += ' />'
return ret
if state.PLAYER_STATES[playerid]['plex_id']:
key = '/library/metadata/%s' % \
state.PLAYER_STATES[playerid]['plex_id']
return key
def get_timeline_xml(self, player, ptype):
if player is None:
return ' <Timeline state="stopped" controllable="%s" type="%s" ' \
'itemType="%s" />\n' % (CONTROLLABLE[ptype], ptype, ptype)
playerid = player['playerid']
info = state.PLAYER_STATES[playerid]
status = 'paused' if info['speed'] == '0' else 'playing'
ret = ' <Timeline state="%s"' % status
ret += ' controllable="%s"' % CONTROLLABLE[ptype]
ret += ' type="%s" itemType="%s"' % (ptype, ptype)
ret += ' time="%s"' % kodi_time_to_millis(info['time'])
ret += ' duration="%s"' % kodi_time_to_millis(info['totaltime'])
ret += ' shuffle="%s"' % ("0", "1")[info['shuffled']]
shuffled = '1' if info['shuffled'] else '0'
ret += ' shuffle="%s"' % shuffled
ret += ' repeat="%s"' % v.PLEX_REPEAT_FROM_KODI_REPEAT[info['repeat']]
if ptype != v.KODI_TYPE_PHOTO:
ret += ' volume="%s"' % info['volume']
ret += ' mute="%s"' % ("0", "1")[info['muted']]
muted = '1' if info['muted'] is True else '0'
ret += ' mute="%s"' % muted
pbmc_server = window('pms_server')
server = self.getServerByHost(self.server)
if pbmc_server:
(self.protocol, self.server, self.port) = pbmc_server.split(':')
self.server = self.server.replace('/', '')
if info['plex_id']:
self.lastkey = "/library/metadata/%s" % info['plex_id']
self.ratingkey = info['plex_id']
ret += ' key="/library/metadata/%s"' % info['plex_id']
ret += ' ratingKey="%s"' % info['plex_id']
# PlayQueue stuff
playqueue = self.playqueue.playqueues[playerid]
pos = info['position']
try:
ret += ' playQueueItemID="%s"' % playqueue.items[pos].ID or 'null'
self.containerKey = "/playQueues/%s" % playqueue.ID or 'null'
ret += ' playQueueID="%s"' % playqueue.ID or 'null'
ret += ' playQueueVersion="%s"' % playqueue.version or 'null'
key = self._get_container_key(playerid)
if key is not None and key.startswith('/playQueues'):
self.containerKey = key
ret += ' containerKey="%s"' % self.containerKey
pos = info['position']
ret += ' playQueueItemID="%s"' % playqueue.items[pos].id or 'null'
ret += ' playQueueID="%s"' % playqueue.id or 'null'
ret += ' playQueueVersion="%s"' % playqueue.version or 'null'
ret += ' guid="%s"' % playqueue.items[pos].guid or 'null'
except IndexError:
pass
elif key:
self.containerKey = key
ret += ' containerKey="%s"' % self.containerKey
ret += ' machineIdentifier="%s"' % server.get('uuid', "")
ret += ' protocol="%s"' % server.get('protocol', 'http')
ret += ' address="%s"' % server.get('server', self.server)
@ -132,8 +160,7 @@ class SubscriptionManager:
if ptype == 'video':
ret += ' subtitleStreamID="-1"'
ret += ' audioStreamID="-1"'
ret += '/>'
ret += '/>\n'
return ret
def updateCommandID(self, uuid, commandID):
@ -142,120 +169,92 @@ class SubscriptionManager:
def notify(self, event=False):
self.cleanup()
# Don't tell anyone if we don't know a Plex ID and are still playing
# (e.g. no stop called). Used for e.g. PVR/TV without PKC usage
if (not window('plex_currently_playing_itemid')
and not self.lastplayers):
return True
# Do we need a check to NOT tell about e.g. PVR/TV and Addon playback?
players = js.get_players()
# fetch the message, subscribers or not, since the server
# will need the info anyway
msg = self.msg(players)
if self.subscribers:
with threading.RLock():
for sub in self.subscribers.values():
sub.send_update(msg, len(players) == 0)
with RLock():
for subscriber in self.subscribers.values():
subscriber.send_update(msg, len(players) == 0)
self.notifyServer(players)
self.lastplayers = players
return True
def notifyServer(self, players):
for typus, p in players.iteritems():
info = self.playerprops[p.get('playerid')]
self._sendNotification(info, int(p['playerid']))
self.lastinfo[typus] = info
# Cross the one of the list
for typus, player in players.iteritems():
self._send_pms_notification(
player['playerid'], self._get_pms_params(player['playerid']))
try:
del self.lastplayers[typus]
except KeyError:
pass
# Process the players we have left (to signal a stop)
for typus, p in self.lastplayers.iteritems():
self.lastinfo[typus]['state'] = 'stopped'
self._sendNotification(self.lastinfo[typus], int(p['playerid']))
for typus, player in self.lastplayers.iteritems():
self.last_params['state'] = 'stopped'
self._send_pms_notification(player['playerid'], self.last_params)
def _sendNotification(self, info, playerid):
playqueue = self.playqueue.playqueues[playerid]
xargs = getXArgsDeviceInfo(include_token=False)
params = {
'containerKey': self.containerKey or "/library/metadata/900000",
'key': self.lastkey or "/library/metadata/900000",
'ratingKey': self.ratingkey or "900000",
'state': info['state'],
'time': info['time'],
'duration': info['duration']
def _get_pms_params(self, playerid):
info = state.PLAYER_STATES[playerid]
status = 'paused' if info['speed'] == '0' else 'playing'
params = {'state': status,
'ratingKey': self.ratingkey,
'key': '/library/metadata/%s' % self.ratingkey,
'time': kodi_time_to_millis(info['time']),
'duration': kodi_time_to_millis(info['totaltime'])
}
if self.containerKey:
params['containerKey'] = self.containerKey
if self.containerKey is not None and \
self.containerKey.startswith('/playQueues/'):
params['playQueueVersion'] = info['playQueueVersion']
params['playQueueItemID'] = info['playQueueItemID']
self.last_params = params
return params
def _send_pms_notification(self, playerid, params):
serv = self.getServerByHost(self.server)
xargs = self._headers()
playqueue = self.playqueue.playqueues[playerid]
if state.PLEX_TRANSIENT_TOKEN:
xargs['X-Plex-Token'] = state.PLEX_TRANSIENT_TOKEN
elif playqueue.plex_transient_token:
xargs['X-Plex-Token'] = playqueue.plex_transient_token
if info.get('playQueueID'):
params['containerKey'] = '/playQueues/%s' % info['playQueueID']
params['playQueueVersion'] = info['playQueueVersion']
params['playQueueItemID'] = info['playQueueItemID']
serv = self.getServerByHost(self.server)
url = '%s://%s:%s/:/timeline' % (serv.get('protocol', 'http'),
serv.get('server', 'localhost'),
serv.get('port', '32400'))
self.doUtils(url, parameters=params, headerOptions=xargs)
log.debug("Sent server notification with parameters: %s to %s"
% (params, url))
self.doUtils().downloadUrl(
url, parameters=params, headerOptions=xargs)
# Save to be able to signal a stop at the end
LOG.debug("Sent server notification with parameters: %s to %s",
params, url)
def addSubscriber(self, protocol, host, port, uuid, commandID):
sub = Subscriber(protocol,
host,
port,
uuid,
commandID,
self,
self.RequestMgr)
with threading.RLock():
self.subscribers[sub.uuid] = sub
return sub
subscriber = Subscriber(protocol,
host,
port,
uuid,
commandID,
self,
self.RequestMgr)
with RLock():
self.subscribers[subscriber.uuid] = subscriber
return subscriber
def removeSubscriber(self, uuid):
with threading.RLock():
for sub in self.subscribers.values():
if sub.uuid == uuid or sub.host == uuid:
sub.cleanup()
del self.subscribers[sub.uuid]
with RLock():
for subscriber in self.subscribers.values():
if subscriber.uuid == uuid or subscriber.host == uuid:
subscriber.cleanup()
del self.subscribers[subscriber.uuid]
def cleanup(self):
with threading.RLock():
for sub in self.subscribers.values():
if sub.age > 30:
sub.cleanup()
del self.subscribers[sub.uuid]
def getPlayerProperties(self, playerid):
# Get the playqueue
playqueue = self.playqueue.playqueues[playerid]
# get info from the player
props = state.PLAYER_STATES[playerid]
info = {
'time': kodi_time_to_millis(props['time']),
'duration': kodi_time_to_millis(props['totaltime']),
'state': ("paused", "playing")[int(props['speed'])],
'shuffle': ("0", "1")[props.get('shuffled', False)],
'repeat': v.PLEX_REPEAT_FROM_KODI_REPEAT[props.get('repeat')]
}
pos = props['position']
try:
info['playQueueItemID'] = playqueue.items[pos].ID or 'null'
info['guid'] = playqueue.items[pos].guid or 'null'
info['playQueueID'] = playqueue.ID or 'null'
info['playQueueVersion'] = playqueue.version or 'null'
info['itemType'] = playqueue.items[pos].plex_type or 'null'
except:
info['itemType'] = props.get('type') or 'null'
# get the volume from the application
info['volume'] = js.get_volume()
info['mute'] = js.get_muted()
info['plex_transient_token'] = playqueue.plex_transient_token
return info
with RLock():
for subscriber in self.subscribers.values():
if subscriber.age > 30:
subscriber.cleanup()
del self.subscribers[subscriber.uuid]
class Subscriber:
@ -268,16 +267,13 @@ class Subscriber:
self.commandID = int(commandID) or 0
self.navlocationsent = False
self.age = 0
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.doUtils = downloadutils.DownloadUtils
self.subMgr = subMgr
self.RequestMgr = RequestMgr
def __eq__(self, other):
return self.uuid == other.uuid
def tostr(self):
return "uuid=%s,commandID=%i" % (self.uuid, self.commandID)
def cleanup(self):
self.RequestMgr.closeConnection(self.protocol, self.host, self.port)
@ -289,11 +285,12 @@ class Subscriber:
return True
else:
self.navlocationsent = True
msg = re.sub(r"INSERTCOMMANDID", str(self.commandID), msg)
log.debug("sending xml to subscriber %s:\n%s" % (self.tostr(), msg))
msg = sub(r"INSERTCOMMANDID", str(self.commandID), msg)
LOG.debug("sending xml to subscriber uuid=%s,commandID=%i:\n%s",
self.uuid, self.commandID, msg)
url = self.protocol + '://' + self.host + ':' + self.port \
+ "/:/timeline"
t = threading.Thread(target=self.threadedSend, args=(url, msg))
t = Thread(target=self.threadedSend, args=(url, msg))
t.start()
def threadedSend(self, url, msg):
@ -301,9 +298,8 @@ class Subscriber:
Threaded POST request, because they stall due to PMS response missing
the Content-Length header :-(
"""
response = self.doUtils(url,
postBody=msg,
action_type="POST")
log.debug('response is: %s', response)
response = self.doUtils().downloadUrl(url,
postBody=msg,
action_type="POST")
if response in [False, None, 401]:
self.subMgr.removeSubscriber(self.uuid)

View File

@ -112,6 +112,9 @@ PLAYER_STATES = {
2: {},
3: {}
}
# Dict containing all filenames as keys with plex id as values - used for addon
# paths for playback (since we're not receiving a Kodi id)
PLEX_IDS = {}
PLAYED_INFO = {}
# Kodi webserver details

View File

@ -199,6 +199,20 @@ KODITYPE_FROM_PLEXTYPE = {
'XXXXXXX': 'genre'
}
PLEX_TYPE_FROM_KODI_TYPE = {
KODI_TYPE_VIDEO: PLEX_TYPE_VIDEO,
KODI_TYPE_MOVIE: PLEX_TYPE_MOVIE,
KODI_TYPE_EPISODE: PLEX_TYPE_EPISODE,
KODI_TYPE_SEASON: PLEX_TYPE_SEASON,
KODI_TYPE_SHOW: PLEX_TYPE_SHOW,
KODI_TYPE_CLIP: PLEX_TYPE_CLIP,
KODI_TYPE_ARTIST: PLEX_TYPE_ARTIST,
KODI_TYPE_ALBUM: PLEX_TYPE_ALBUM,
KODI_TYPE_SONG: PLEX_TYPE_SONG,
KODI_TYPE_AUDIO: PLEX_TYPE_AUDIO,
KODI_TYPE_PHOTO: PLEX_TYPE_PHOTO
}
KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE = {
PLEX_TYPE_VIDEO: KODI_TYPE_VIDEO,
PLEX_TYPE_MOVIE: KODI_TYPE_VIDEO,