Enable Kodi libraries for Plex Music libraries
This commit is contained in:
parent
777b9e15e4
commit
25d80521c7
5 changed files with 164 additions and 73 deletions
|
@ -329,8 +329,7 @@ class LibrarySync(Thread):
|
||||||
utils.playlist_xsp(mediatype, foldername, folderid, viewtype)
|
utils.playlist_xsp(mediatype, foldername, folderid, viewtype)
|
||||||
lists.append(foldername)
|
lists.append(foldername)
|
||||||
# Create the video node
|
# Create the video node
|
||||||
if (foldername not in nodes and
|
if foldername not in nodes:
|
||||||
mediatype != v.PLEX_TYPE_ARTIST):
|
|
||||||
vnodes.viewNode(sorted_views.index(foldername),
|
vnodes.viewNode(sorted_views.index(foldername),
|
||||||
foldername,
|
foldername,
|
||||||
mediatype,
|
mediatype,
|
||||||
|
@ -362,42 +361,41 @@ class LibrarySync(Thread):
|
||||||
# Update view with new info
|
# Update view with new info
|
||||||
plex_db.updateView(foldername, tagid, folderid)
|
plex_db.updateView(foldername, tagid, folderid)
|
||||||
|
|
||||||
if mediatype != "artist":
|
if plex_db.getView_byName(current_viewname) is None:
|
||||||
if plex_db.getView_byName(current_viewname) is None:
|
# The tag could be a combined view. Ensure there's
|
||||||
# The tag could be a combined view. Ensure there's
|
# no other tags with the same name before deleting
|
||||||
# no other tags with the same name before deleting
|
# playlist.
|
||||||
# playlist.
|
utils.playlist_xsp(mediatype,
|
||||||
utils.playlist_xsp(mediatype,
|
current_viewname,
|
||||||
current_viewname,
|
folderid,
|
||||||
folderid,
|
current_viewtype,
|
||||||
current_viewtype,
|
True)
|
||||||
True)
|
# Delete video node
|
||||||
# Delete video node
|
if mediatype != "musicvideos":
|
||||||
if mediatype != "musicvideos":
|
vnodes.viewNode(
|
||||||
vnodes.viewNode(
|
indexnumber=sorted_views.index(foldername),
|
||||||
indexnumber=sorted_views.index(foldername),
|
tagname=current_viewname,
|
||||||
tagname=current_viewname,
|
mediatype=mediatype,
|
||||||
mediatype=mediatype,
|
viewtype=current_viewtype,
|
||||||
viewtype=current_viewtype,
|
viewid=folderid,
|
||||||
viewid=folderid,
|
delete=True)
|
||||||
delete=True)
|
# Added new playlist
|
||||||
# Added new playlist
|
if (foldername not in lists and mediatype in
|
||||||
if (foldername not in lists and mediatype in
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
||||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
utils.playlist_xsp(mediatype,
|
||||||
utils.playlist_xsp(mediatype,
|
foldername,
|
||||||
foldername,
|
folderid,
|
||||||
folderid,
|
viewtype)
|
||||||
viewtype)
|
lists.append(foldername)
|
||||||
lists.append(foldername)
|
# Add new video node
|
||||||
# Add new video node
|
if foldername not in nodes and mediatype != "musicvideos":
|
||||||
if foldername not in nodes and mediatype != "musicvideos":
|
vnodes.viewNode(sorted_views.index(foldername),
|
||||||
vnodes.viewNode(sorted_views.index(foldername),
|
foldername,
|
||||||
foldername,
|
mediatype,
|
||||||
mediatype,
|
viewtype,
|
||||||
viewtype,
|
folderid)
|
||||||
folderid)
|
nodes.append(foldername)
|
||||||
nodes.append(foldername)
|
totalnodes += 1
|
||||||
totalnodes += 1
|
|
||||||
|
|
||||||
# Update items with new tag
|
# Update items with new tag
|
||||||
items = plex_db.getItem_byView(folderid)
|
items = plex_db.getItem_byView(folderid)
|
||||||
|
@ -407,23 +405,22 @@ class LibrarySync(Thread):
|
||||||
current_tagid, tagid, item[0], current_viewtype[:-1])
|
current_tagid, tagid, item[0], current_viewtype[:-1])
|
||||||
else:
|
else:
|
||||||
# Validate the playlist exists or recreate it
|
# Validate the playlist exists or recreate it
|
||||||
if mediatype != v.PLEX_TYPE_ARTIST:
|
if (foldername not in lists and mediatype in
|
||||||
if (foldername not in lists and mediatype in
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
||||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
utils.playlist_xsp(mediatype,
|
||||||
utils.playlist_xsp(mediatype,
|
foldername,
|
||||||
foldername,
|
folderid,
|
||||||
folderid,
|
viewtype)
|
||||||
viewtype)
|
lists.append(foldername)
|
||||||
lists.append(foldername)
|
# Create the video node if not already exists
|
||||||
# Create the video node if not already exists
|
if foldername not in nodes and mediatype != "musicvideos":
|
||||||
if foldername not in nodes and mediatype != "musicvideos":
|
vnodes.viewNode(sorted_views.index(foldername),
|
||||||
vnodes.viewNode(sorted_views.index(foldername),
|
foldername,
|
||||||
foldername,
|
mediatype,
|
||||||
mediatype,
|
viewtype,
|
||||||
viewtype,
|
folderid)
|
||||||
folderid)
|
nodes.append(foldername)
|
||||||
nodes.append(foldername)
|
totalnodes += 1
|
||||||
totalnodes += 1
|
|
||||||
return totalnodes
|
return totalnodes
|
||||||
|
|
||||||
def maintain_views(self):
|
def maintain_views(self):
|
||||||
|
@ -454,7 +451,8 @@ class LibrarySync(Thread):
|
||||||
|
|
||||||
for view in sections:
|
for view in sections:
|
||||||
if (view.attrib['type'] in
|
if (view.attrib['type'] in
|
||||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW, v.PLEX_TYPE_PHOTO)):
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW, v.PLEX_TYPE_PHOTO,
|
||||||
|
v.PLEX_TYPE_ARTIST)):
|
||||||
self.sorted_views.append(view.attrib['title'])
|
self.sorted_views.append(view.attrib['title'])
|
||||||
LOG.debug('Sorted views: %s', self.sorted_views)
|
LOG.debug('Sorted views: %s', self.sorted_views)
|
||||||
|
|
||||||
|
|
|
@ -69,14 +69,19 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
|
||||||
try:
|
try:
|
||||||
pos = js.get_position(playqueue.playlistid)
|
pos = js.get_position(playqueue.playlistid)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.error('Still no position - abort')
|
LOG.info('Assuming video instead of audio playlist playback')
|
||||||
# "Play error"
|
playqueue = PQ.get_playqueue_from_type(v.KODI_PLAYLIST_TYPE_VIDEO)
|
||||||
utils.dialog('notification',
|
try:
|
||||||
utils.lang(29999),
|
pos = js.get_position(playqueue.playlistid)
|
||||||
utils.lang(30128),
|
except KeyError:
|
||||||
icon='{error}')
|
LOG.error('Still no position - abort')
|
||||||
_ensure_resolve(abort=True)
|
# "Play error"
|
||||||
return
|
utils.dialog('notification',
|
||||||
|
utils.lang(29999),
|
||||||
|
utils.lang(30128),
|
||||||
|
icon='{error}')
|
||||||
|
_ensure_resolve(abort=True)
|
||||||
|
return
|
||||||
# HACK to detect playback of playlists for add-on paths
|
# HACK to detect playback of playlists for add-on paths
|
||||||
items = js.playlist_get_items(playqueue.playlistid)
|
items = js.playlist_get_items(playqueue.playlistid)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -313,7 +313,8 @@ def verify_kodi_item(plex_id, kodi_item):
|
||||||
LOG.debug('Detected song. Research results: %s', kodi_item)
|
LOG.debug('Detected song. Research results: %s', kodi_item)
|
||||||
return kodi_item
|
return kodi_item
|
||||||
# Need more info since we don't have kodi_id nor type. Use file path.
|
# Need more info since we don't have kodi_id nor type. Use file path.
|
||||||
if (kodi_item['file'].startswith('plugin') or
|
if ((kodi_item['file'].startswith('plugin') and
|
||||||
|
not kodi_item['file'].startswith('plugin://%s' % v.ADDON_ID)) or
|
||||||
kodi_item['file'].startswith('http')):
|
kodi_item['file'].startswith('http')):
|
||||||
LOG.info('kodi_item %s cannot be used for Plex playback', kodi_item)
|
LOG.info('kodi_item %s cannot be used for Plex playback', kodi_item)
|
||||||
raise PlaylistError
|
raise PlaylistError
|
||||||
|
|
|
@ -1474,6 +1474,9 @@ class API(object):
|
||||||
# Only set the bare minimum of artwork
|
# Only set the bare minimum of artwork
|
||||||
listitem.setArt({'icon': 'DefaultPicture.png',
|
listitem.setArt({'icon': 'DefaultPicture.png',
|
||||||
'fanart': self.one_artwork('thumb')})
|
'fanart': self.one_artwork('thumb')})
|
||||||
|
elif self.plex_type() == v.PLEX_TYPE_SONG:
|
||||||
|
listitem = self._create_audio_listitem(listitem)
|
||||||
|
listitem.setArt(self.artwork())
|
||||||
else:
|
else:
|
||||||
listitem = self._create_video_listitem(listitem,
|
listitem = self._create_video_listitem(listitem,
|
||||||
append_show_title,
|
append_show_title,
|
||||||
|
@ -1596,6 +1599,74 @@ class API(object):
|
||||||
pass
|
pass
|
||||||
return listitem
|
return listitem
|
||||||
|
|
||||||
|
def track_number(self):
|
||||||
|
"""
|
||||||
|
Returns the song's track number as an int or None if not found
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return int(self.item.get('index'))
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def disc_number(self):
|
||||||
|
"""
|
||||||
|
Returns the song's disc number as an int or None if not found
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return int(self.item.get('parentIndex'))
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _create_audio_listitem(self, listitem=None):
|
||||||
|
"""
|
||||||
|
Use for songs only
|
||||||
|
Call on a child level of PMS xml response (e.g. in a for loop)
|
||||||
|
|
||||||
|
listitem : existing xbmcgui.ListItem to work with
|
||||||
|
otherwise, a new one is created
|
||||||
|
|
||||||
|
Returns XBMC listitem for this PMS library item
|
||||||
|
"""
|
||||||
|
if listitem is None:
|
||||||
|
listitem = ListItem(self.title())
|
||||||
|
else:
|
||||||
|
listitem.setLabel(self.title())
|
||||||
|
listitem.setProperty('IsPlayable', 'true')
|
||||||
|
userdata = self.userdata()
|
||||||
|
metadata = {
|
||||||
|
'mediatype': 'song',
|
||||||
|
'tracknumber': self.track_number(),
|
||||||
|
'discnumber': self.track_number(),
|
||||||
|
'duration': userdata['Runtime'],
|
||||||
|
'year': self.year(),
|
||||||
|
# Kodi does not support list of str
|
||||||
|
'genre': ','.join(self.genre_list()) or None,
|
||||||
|
'album': self.item.get('parentTitle'),
|
||||||
|
'artist': self.item.get('originalTitle') or self.grandparent_title(),
|
||||||
|
'title': self.title(),
|
||||||
|
'rating': self.audience_rating(),
|
||||||
|
'playcount': userdata['PlayCount'],
|
||||||
|
'lastplayed': userdata['LastPlayedDate'],
|
||||||
|
# lyrics string (On a dark desert highway...)
|
||||||
|
# userrating integer - range is 1..10
|
||||||
|
# comment string (This is a great song)
|
||||||
|
# listeners integer (25614)
|
||||||
|
# musicbrainztrackid string (cd1de9af-0b71-4503-9f96-9f5efe27923c)
|
||||||
|
# musicbrainzartistid string (d87e52c5-bb8d-4da8-b941-9f4928627dc8)
|
||||||
|
# musicbrainzalbumid string (24944755-2f68-3778-974e-f572a9e30108)
|
||||||
|
# musicbrainzalbumartistid string (d87e52c5-bb8d-4da8-b941-9f4928627dc8)
|
||||||
|
}
|
||||||
|
plex_id = self.plex_id()
|
||||||
|
listitem.setProperty('plexid', plex_id)
|
||||||
|
if v.KODIVERSION >= 18:
|
||||||
|
with plexdb.Get_Plex_DB() as plex_db:
|
||||||
|
kodi_id = plex_db.getItem_byId(plex_id)
|
||||||
|
if kodi_id:
|
||||||
|
kodi_id = kodi_id[0]
|
||||||
|
metadata['dbid'] = kodi_id
|
||||||
|
listitem.setInfo('music', infoLabels=metadata)
|
||||||
|
return listitem
|
||||||
|
|
||||||
def add_video_streams(self, listitem):
|
def add_video_streams(self, listitem):
|
||||||
"""
|
"""
|
||||||
Add media stream information to xbmcgui.ListItem
|
Add media stream information to xbmcgui.ListItem
|
||||||
|
|
|
@ -54,7 +54,8 @@ class VideoNodes(object):
|
||||||
'show': 'tvshows',
|
'show': 'tvshows',
|
||||||
'photo': 'photos',
|
'photo': 'photos',
|
||||||
'homevideo': 'homevideos',
|
'homevideo': 'homevideos',
|
||||||
'musicvideos': 'musicvideos'
|
'musicvideos': 'musicvideos',
|
||||||
|
'artist': 'albums'
|
||||||
}
|
}
|
||||||
mediatype = mediatypes[mediatype]
|
mediatype = mediatypes[mediatype]
|
||||||
|
|
||||||
|
@ -196,6 +197,15 @@ class VideoNodes(object):
|
||||||
'4': 30257,
|
'4': 30257,
|
||||||
'6': 30258,
|
'6': 30258,
|
||||||
'13': 39702
|
'13': 39702
|
||||||
|
},
|
||||||
|
|
||||||
|
'albums':
|
||||||
|
{
|
||||||
|
'1': tagname,
|
||||||
|
'2': 517, # Recently played albums
|
||||||
|
'2': 359, # Recently added albums
|
||||||
|
'13': 39702, # browse by folder
|
||||||
|
'14': 136 # Playlists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,17 +327,23 @@ class VideoNodes(object):
|
||||||
label=label,
|
label=label,
|
||||||
tagname=tagname,
|
tagname=tagname,
|
||||||
roottype=2)
|
roottype=2)
|
||||||
etree.SubElement(root, 'path').text = path
|
|
||||||
etree.SubElement(root, 'content').text = "episodes"
|
|
||||||
else:
|
else:
|
||||||
root = self.commonRoot(order=sortorder[node],
|
root = self.commonRoot(order=sortorder[node],
|
||||||
label=label,
|
label=label,
|
||||||
tagname=tagname)
|
tagname=tagname)
|
||||||
if nodetype in ('recentepisodes', 'inprogressepisodes'):
|
# Set the content type
|
||||||
etree.SubElement(root, 'content').text = "episodes"
|
if mediatype == 'tvshows':
|
||||||
else:
|
etree.SubElement(root, 'content').text = 'episodes'
|
||||||
etree.SubElement(root, 'content').text = mediatype
|
else:
|
||||||
|
etree.SubElement(root, 'content').text = mediatype
|
||||||
|
# Now fill the view
|
||||||
|
if (nodetype in ("nextepisodes",
|
||||||
|
"ondeck",
|
||||||
|
'recentepisodes',
|
||||||
|
'browsefiles',
|
||||||
|
'playlists') or mediatype == "homevideos"):
|
||||||
|
etree.SubElement(root, 'path').text = path
|
||||||
|
else:
|
||||||
# Elements per nodetype
|
# Elements per nodetype
|
||||||
if nodetype == "all":
|
if nodetype == "all":
|
||||||
etree.SubElement(root,
|
etree.SubElement(root,
|
||||||
|
|
Loading…
Reference in a new issue