PlexKodiConnect/resources/lib/videonodes.py

491 lines
20 KiB
Python
Raw Normal View History

2015-12-25 07:07:00 +11:00
# -*- coding: utf-8 -*-
###############################################################################
2017-12-10 00:35:08 +11:00
from logging import getLogger
from distutils import dir_util
2015-12-25 07:07:00 +11:00
import xml.etree.ElementTree as etree
from os import makedirs
2015-12-25 07:07:00 +11:00
import xbmc
from xbmcvfs import exists
2015-12-25 07:07:00 +11:00
2018-06-22 03:24:37 +10:00
from . import utils
from . import variables as v
from . import state
2015-12-25 07:07:00 +11:00
2016-09-05 00:34:25 +10:00
###############################################################################
2018-06-22 03:24:37 +10:00
LOG = getLogger('PLEX.videonodes')
2016-09-05 00:34:25 +10:00
###############################################################################
# Paths are strings, NOT unicode!
2015-12-25 07:07:00 +11:00
class VideoNodes(object):
def commonRoot(self, order, label, tagname, roottype=1):
if roottype == 0:
# Index
root = etree.Element('node', attrib={'order': "%s" % order})
elif roottype == 1:
# Filter
2018-06-22 03:24:37 +10:00
root = etree.Element('node',
attrib={'order': "%s" % order, 'type': "filter"})
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'match').text = "all"
# Add tag rule
2018-06-22 03:24:37 +10:00
rule = etree.SubElement(root,
'rule',
attrib={'field': "tag", 'operator': "is"})
2015-12-25 07:07:00 +11:00
etree.SubElement(rule, 'value').text = tagname
else:
# Folder
2018-06-22 03:24:37 +10:00
root = etree.Element('node',
attrib={'order': "%s" % order, 'type': "folder"})
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'label').text = label
etree.SubElement(root, 'icon').text = "special://home/addons/plugin.video.plexkodiconnect/icon.png"
2015-12-25 07:07:00 +11:00
return root
2018-06-22 03:24:37 +10:00
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid,
delete=False):
2016-03-03 19:04:15 +11:00
# Plex: reassign mediatype due to Kodi inner workings
2016-03-15 23:09:51 +11:00
# How many items do we get at most?
2018-02-13 17:24:39 +11:00
limit = state.FETCH_PMS_ITEM_NUMBER
2016-03-03 19:04:15 +11:00
mediatypes = {
'movie': 'movies',
'show': 'tvshows',
'photo': 'photos',
'homevideo': 'homevideos',
'musicvideos': 'musicvideos'
}
mediatype = mediatypes[mediatype]
2015-12-25 07:07:00 +11:00
if viewtype == "mixed":
dirname = "%s-%s" % (viewid, mediatype)
2015-12-25 07:07:00 +11:00
else:
dirname = viewid
2017-01-21 01:46:34 +11:00
# Returns strings
2018-06-22 03:24:37 +10:00
path = utils.try_decode(xbmc.translatePath(
"special://profile/library/video/"))
2018-06-22 03:24:37 +10:00
nodepath = utils.try_decode(xbmc.translatePath(
"special://profile/library/video/Plex-%s/" % dirname))
2015-12-25 07:07:00 +11:00
2017-01-21 01:46:34 +11:00
if delete:
2018-06-22 03:24:37 +10:00
if utils.exists_dir(nodepath):
from shutil import rmtree
rmtree(nodepath)
2018-06-22 03:24:37 +10:00
LOG.info("Sucessfully removed videonode: %s." % tagname)
2017-01-21 01:46:34 +11:00
return
2015-12-25 07:07:00 +11:00
# Verify the video directory
2018-06-22 03:24:37 +10:00
if not utils.exists_dir(path):
dir_util.copy_tree(
2018-06-22 03:24:37 +10:00
src=utils.try_decode(
xbmc.translatePath("special://xbmc/system/library/video")),
2018-06-22 03:24:37 +10:00
dst=utils.try_decode(
xbmc.translatePath("special://profile/library/video")),
preserve_mode=0) # do not copy permission bits!
2015-12-25 07:07:00 +11:00
# Create the node directory
2016-06-05 02:48:22 +10:00
if mediatype != "photos":
2018-06-22 03:24:37 +10:00
if not utils.exists_dir(nodepath):
# folder does not exist yet
2018-06-22 03:24:37 +10:00
LOG.debug('Creating folder %s' % nodepath)
makedirs(nodepath)
2015-12-25 07:07:00 +11:00
# Create index entry
nodeXML = "%sindex.xml" % nodepath
# Set windows property
path = "library://video/Plex-%s/" % dirname
2015-12-25 07:07:00 +11:00
for i in range(1, indexnumber):
# Verify to make sure we don't create duplicates
2018-06-22 03:24:37 +10:00
if utils.window('Plex.nodes.%s.index' % i) == path:
2015-12-25 07:07:00 +11:00
return
2016-06-05 02:48:22 +10:00
if mediatype == "photos":
2017-03-09 03:53:43 +11:00
path = "plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s&id=%s" % (viewid, viewid)
2018-06-22 03:24:37 +10:00
utils.window('Plex.nodes.%s.index' % indexnumber, value=path)
2015-12-25 07:07:00 +11:00
# Root
2016-06-05 02:48:22 +10:00
if not mediatype == "photos":
2016-02-23 10:56:08 +11:00
if viewtype == "mixed":
specialtag = "%s-%s" % (tagname, mediatype)
root = self.commonRoot(order=0,
label=specialtag,
tagname=tagname,
roottype=0)
2016-02-23 10:56:08 +11:00
else:
root = self.commonRoot(order=0,
label=tagname,
tagname=tagname,
roottype=0)
try:
2018-06-22 03:24:37 +10:00
utils.indent(root)
except:
pass
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
2015-12-25 07:07:00 +11:00
nodetypes = {
'1': "all",
'2': "recent",
'3': "recentepisodes",
'4': "inprogress",
'5': "inprogressepisodes",
'6': "unwatched",
2016-01-06 18:59:41 +11:00
'7': "nextepisodes",
2015-12-25 07:07:00 +11:00
'8': "sets",
'9': "genres",
'10': "random",
2016-01-13 11:03:35 +11:00
'11': "recommended",
2017-03-09 02:41:49 +11:00
'12': "ondeck",
'13': 'browsefiles'
2015-12-25 07:07:00 +11:00
}
mediatypes = {
# label according to nodetype per mediatype
2017-03-09 02:41:49 +11:00
'movies':
2016-01-13 11:03:35 +11:00
{
2017-03-09 02:41:49 +11:00
'1': tagname,
'2': 30174,
# '4': 30177,
# '6': 30189,
'8': 39501,
'9': 135,
'10': 30227,
'11': 30230,
'12': 39500,
'13': 39702
2016-01-13 11:03:35 +11:00
},
2015-12-25 07:07:00 +11:00
2017-03-09 02:41:49 +11:00
'tvshows':
2016-01-13 11:03:35 +11:00
{
2017-03-09 02:41:49 +11:00
'1': tagname,
# '2': 30170,
'3': 30174,
# '4': 30171,
# '5': 30178,
# '7': 30179,
'9': 135,
'10': 30227,
# '11': 30230,
'12': 39500,
'13': 39702
2016-01-13 11:03:35 +11:00
},
2017-03-09 02:41:49 +11:00
'homevideos':
2016-01-13 11:03:35 +11:00
{
2017-03-09 02:41:49 +11:00
'1': tagname,
'2': 30251,
'11': 30253,
'13': 39702
2016-01-13 11:03:35 +11:00
},
2017-03-09 02:41:49 +11:00
'photos':
2016-01-13 11:03:35 +11:00
{
2017-03-09 02:41:49 +11:00
'1': tagname,
'2': 30252,
'8': 30255,
'11': 30254,
'13': 39702
2016-01-13 11:03:35 +11:00
},
2016-02-04 23:22:33 +11:00
2017-03-09 02:41:49 +11:00
'musicvideos':
2016-01-19 06:17:14 +11:00
{
2017-03-09 02:41:49 +11:00
'1': tagname,
'2': 30256,
'4': 30257,
'6': 30258,
'13': 39702
2016-02-04 23:22:33 +11:00
}
2015-12-25 07:07:00 +11:00
}
2016-03-15 23:09:51 +11:00
# Key: nodetypes, value: sort order in Kodi
sortorder = {
'1': '3', # "all",
'2': '2', # "recent",
'3': '2', # "recentepisodes",
# '4': # "inprogress",
# '5': # "inprogressepisodes",
# '6': # "unwatched",
# '7': # "nextepisodes",
'8': '7', # "sets",
'9': '6', # "genres",
'10': '8', # "random",
'11': '5', # "recommended",
'12': '1', # "ondeck"
2017-03-09 02:41:49 +11:00
'13': '9' # browse by folder
2016-03-15 23:09:51 +11:00
}
2015-12-25 07:07:00 +11:00
nodes = mediatypes[mediatype]
for node in nodes:
nodetype = nodetypes[node]
nodeXML = "%s%s_%s.xml" % (nodepath, viewid, nodetype)
2015-12-25 07:07:00 +11:00
# Get label
stringid = nodes[node]
2016-02-17 19:13:37 +11:00
if node != "1":
2018-06-22 03:24:37 +10:00
label = utils.lang(stringid)
2015-12-25 07:07:00 +11:00
if not label:
label = xbmc.getLocalizedString(stringid)
else:
label = stringid
# Set window properties
2018-06-22 03:24:37 +10:00
if ((mediatype == "homevideos" or mediatype == "photos") and
nodetype == "all"):
# Custom query
2016-06-05 02:48:22 +10:00
path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s"
% (viewid, mediatype))
elif (mediatype == "homevideos" or mediatype == "photos"):
# Custom query
2016-06-05 02:48:22 +10:00
path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s&folderid=%s"
% (viewid, mediatype, nodetype))
elif nodetype == "nextepisodes":
2015-12-25 07:07:00 +11:00
# Custom query
2016-03-15 23:09:51 +11:00
path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=nextup&limit=%s" % (tagname, limit)
2017-01-25 02:56:07 +11:00
# elif v.KODIVERSION == 14 and nodetype == "recentepisodes":
2016-03-16 00:19:56 +11:00
elif nodetype == "recentepisodes":
2015-12-25 07:07:00 +11:00
# Custom query
2016-03-16 00:19:56 +11:00
path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=recentepisodes&type=%s&tagname=%s&limit=%s"
% (viewid, mediatype, tagname, limit))
2017-01-25 02:56:07 +11:00
elif v.KODIVERSION == 14 and nodetype == "inprogressepisodes":
2015-12-25 07:07:00 +11:00
# Custom query
2016-03-15 23:09:51 +11:00
path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=%s" % (tagname, limit)
2016-03-15 03:47:05 +11:00
elif nodetype == 'ondeck':
# PLEX custom query
2016-03-15 23:09:51 +11:00
if mediatype == "tvshows":
path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=ondeck&type=%s&tagname=%s&limit=%s"
% (viewid, mediatype, tagname, limit))
elif mediatype =="movies":
# Reset nodetype; we got the label
nodetype = 'inprogress'
2017-03-09 02:41:49 +11:00
elif nodetype == 'browsefiles':
path = 'plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s/folder' % viewid
2015-12-25 07:07:00 +11:00
else:
path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype)
2016-06-05 02:48:22 +10:00
if mediatype == "photos":
windowpath = "ActivateWindow(Pictures,%s,return)" % path
else:
2017-01-25 02:56:07 +11:00
if v.KODIVERSION >= 17:
# Krypton
windowpath = "ActivateWindow(Videos,%s,return)" % path
else:
windowpath = "ActivateWindow(Video,%s,return)" % path
2015-12-25 07:07:00 +11:00
if nodetype == "all":
if viewtype == "mixed":
templabel = "%s-%s" % (tagname, mediatype)
2015-12-25 07:07:00 +11:00
else:
templabel = label
2016-05-31 16:06:42 +10:00
embynode = "Plex.nodes.%s" % indexnumber
2018-06-22 03:24:37 +10:00
utils.window('%s.title' % embynode, value=templabel)
utils.window('%s.path' % embynode, value=windowpath)
utils.window('%s.content' % embynode, value=path)
utils.window('%s.type' % embynode, value=mediatype)
2015-12-25 07:07:00 +11:00
else:
2016-05-31 16:06:42 +10:00
embynode = "Plex.nodes.%s.%s" % (indexnumber, nodetype)
2018-06-22 03:24:37 +10:00
utils.window('%s.title' % embynode, value=label)
utils.window('%s.path' % embynode, value=windowpath)
utils.window('%s.content' % embynode, value=path)
2015-12-25 07:07:00 +11:00
2016-06-05 02:48:22 +10:00
if mediatype == "photos":
2018-06-22 03:24:37 +10:00
# For photos, we do not create a node in videos but we do want
# the window props to be created. To do: add our photos nodes to
# kodi picture sources somehow
continue
2018-06-22 03:24:37 +10:00
if exists(utils.try_encode(nodeXML)):
2015-12-25 07:07:00 +11:00
# Don't recreate xml if already exists
continue
# Create the root
2017-03-09 02:41:49 +11:00
if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes', 'browsefiles') or mediatype == "homevideos"):
2015-12-25 07:07:00 +11:00
# Folder type with plugin path
2018-06-22 03:24:37 +10:00
root = self.commonRoot(order=sortorder[node],
label=label,
tagname=tagname,
roottype=2)
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'path').text = path
etree.SubElement(root, 'content').text = "episodes"
else:
2018-06-22 03:24:37 +10:00
root = self.commonRoot(order=sortorder[node],
label=label,
tagname=tagname)
2015-12-25 07:07:00 +11:00
if nodetype in ('recentepisodes', 'inprogressepisodes'):
etree.SubElement(root, 'content').text = "episodes"
else:
etree.SubElement(root, 'content').text = mediatype
# Elements per nodetype
if nodetype == "all":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "ascending"}).text = "sorttitle"
2015-12-25 07:07:00 +11:00
elif nodetype == "recent":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "descending"}).text = "dateadded"
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'limit').text = limit
2018-06-22 03:24:37 +10:00
if utils.settings('MovieShowWatched') == 'false':
rule = etree.SubElement(root,
'rule',
{'field': "playcount",
'operator': "is"})
etree.SubElement(rule, 'value').text = "0"
2015-12-25 07:07:00 +11:00
elif nodetype == "inprogress":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'rule',
{'field': "inprogress", 'operator': "true"})
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'limit').text = limit
2016-04-08 21:06:20 +10:00
etree.SubElement(
root,
'order',
{'direction': 'descending'}
).text = 'lastplayed'
2015-12-25 07:07:00 +11:00
elif nodetype == "genres":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "ascending"}).text = "sorttitle"
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'group').text = "genres"
elif nodetype == "unwatched":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "ascending"}).text = "sorttitle"
rule = etree.SubElement(root,
"rule",
{'field': "playcount", 'operator': "is"})
2015-12-25 07:07:00 +11:00
etree.SubElement(rule, 'value').text = "0"
elif nodetype == "sets":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "ascending"}).text = "sorttitle"
etree.SubElement(root, 'group').text = "tags"
2015-12-25 07:07:00 +11:00
elif nodetype == "random":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "ascending"}).text = "random"
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'limit').text = limit
elif nodetype == "recommended":
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "descending"}).text = "rating"
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'limit').text = limit
2018-06-22 03:24:37 +10:00
rule = etree.SubElement(root,
'rule',
{'field': "playcount", 'operator': "is"})
2015-12-25 07:07:00 +11:00
etree.SubElement(rule, 'value').text = "0"
2018-06-22 03:24:37 +10:00
rule2 = etree.SubElement(root,
'rule',
attrib={'field': "rating", 'operator': "greaterthan"})
2015-12-25 07:07:00 +11:00
etree.SubElement(rule2, 'value').text = "7"
elif nodetype == "recentepisodes":
# Kodi Isengard, Jarvis
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "descending"}).text = "dateadded"
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'limit').text = limit
2018-06-22 03:24:37 +10:00
rule = etree.SubElement(root,
'rule',
{'field': "playcount", 'operator': "is"})
2015-12-25 07:07:00 +11:00
etree.SubElement(rule, 'value').text = "0"
elif nodetype == "inprogressepisodes":
# Kodi Isengard, Jarvis
2016-03-15 23:09:51 +11:00
etree.SubElement(root, 'limit').text = limit
2018-06-22 03:24:37 +10:00
rule = etree.SubElement(root,
'rule',
attrib={'field': "inprogress", 'operator':"true"})
2015-12-25 07:07:00 +11:00
try:
2018-06-22 03:24:37 +10:00
utils.indent(root)
except:
pass
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
2015-12-25 07:07:00 +11:00
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
2018-06-22 03:24:37 +10:00
tagname = utils.try_encode(tagname)
cleantagname = utils.try_decode(utils.normalize_nodes(tagname))
nodepath = utils.try_decode(xbmc.translatePath(
"special://profile/library/video/"))
nodeXML = "%splex_%s.xml" % (nodepath, cleantagname)
path = "library://video/plex_%s.xml" % cleantagname
2017-01-25 02:56:07 +11:00
if v.KODIVERSION >= 17:
# Krypton
windowpath = "ActivateWindow(Videos,%s,return)" % path
else:
windowpath = "ActivateWindow(Video,%s,return)" % path
2015-12-25 07:07:00 +11:00
# Create the video node directory
2018-06-22 03:24:37 +10:00
if not utils.exists_dir(nodepath):
2015-12-25 07:07:00 +11:00
# We need to copy over the default items
dir_util.copy_tree(
2018-06-22 03:24:37 +10:00
src=utils.try_decode(
xbmc.translatePath("special://xbmc/system/library/video")),
2018-06-22 03:24:37 +10:00
dst=utils.try_decode(
xbmc.translatePath("special://profile/library/video")),
preserve_mode=0) # do not copy permission bits!
2015-12-25 07:07:00 +11:00
labels = {
'Favorite movies': 30180,
'Favorite tvshows': 30181,
'channels': 30173
}
2018-06-22 03:24:37 +10:00
label = utils.lang(labels[tagname])
2016-05-31 16:06:42 +10:00
embynode = "Plex.nodes.%s" % indexnumber
2018-06-22 03:24:37 +10:00
utils.window('%s.title' % embynode, value=label)
utils.window('%s.path' % embynode, value=windowpath)
utils.window('%s.content' % embynode, value=path)
utils.window('%s.type' % embynode, value=itemtype)
2015-12-25 07:07:00 +11:00
2018-06-22 03:24:37 +10:00
if exists(utils.try_encode(nodeXML)):
2015-12-25 07:07:00 +11:00
# Don't recreate xml if already exists
return
if itemtype == "channels":
root = self.commonRoot(order=1,
label=label,
tagname=tagname,
roottype=2)
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'path').text = "plugin://plugin.video.plexkodiconnect/?id=0&mode=channels"
2015-12-25 07:07:00 +11:00
else:
root = self.commonRoot(order=1, label=label, tagname=tagname)
2018-06-22 03:24:37 +10:00
etree.SubElement(root,
'order',
{'direction': "ascending"}).text = "sorttitle"
2015-12-25 07:07:00 +11:00
etree.SubElement(root, 'content').text = mediatype
try:
2018-06-22 03:24:37 +10:00
utils.indent(root)
except:
pass
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
2015-12-25 07:07:00 +11:00
def clearProperties(self):
2018-06-22 03:24:37 +10:00
LOG.info("Clearing nodes properties.")
plexprops = utils.window('Plex.nodes.total')
2015-12-25 07:07:00 +11:00
propnames = [
"index","path","title","content",
"inprogress.content","inprogress.title",
"inprogress.content","inprogress.path",
"nextepisodes.title","nextepisodes.content",
"nextepisodes.path","unwatched.title",
"unwatched.content","unwatched.path",
"recent.title","recent.content","recent.path",
"recentepisodes.title","recentepisodes.content",
"recentepisodes.path","inprogressepisodes.title",
"inprogressepisodes.content","inprogressepisodes.path"
]
2016-05-31 16:06:42 +10:00
if plexprops:
totalnodes = int(plexprops)
2015-12-25 07:07:00 +11:00
for i in range(totalnodes):
for prop in propnames:
2018-06-22 03:24:37 +10:00
utils.window('Plex.nodes.%s.%s' % (str(i), prop),
clear=True)