PlexKodiConnect/resources/lib/library_sync/nodes.py
2019-07-14 07:46:09 +02:00

412 lines
15 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
import urllib
import copy
from ..utils import etree
from .. import variables as v, utils
ICON_PATH = 'special://home/addons/plugin.video.plexkodiconnect/icon.png'
RECOMMENDED_SCORE_LOWER_BOUND = 7
# Logic of the following nodes:
# (node_type,
# label/node name,
# args for PKC add-on callback,
# Kodi "content",
# Bool: does this node's xml even point back to PKC add-on callback?
# )
NODE_TYPES = {
v.PLEX_TYPE_MOVIE: (
('plex_ondeck',
utils.lang(39500), # "On Deck"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/onDeck',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
True),
('ondeck',
utils.lang(39502), # "PKC On Deck (faster)"
{},
v.CONTENT_TYPE_MOVIE,
False),
('recent',
utils.lang(30174), # "Recently Added"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/recentlyAdded',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('all',
'{self.name}', # We're using this section's name
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/all',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('recommended',
utils.lang(30230), # "Recommended"
{
'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'rating:desc'})),
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('genres',
utils.lang(135), # "Genres"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/genre',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('sets',
utils.lang(39501), # "Collections"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/collection',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('random',
utils.lang(30227), # "Random"
{
'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'random'})),
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('lastplayed',
utils.lang(568), # "Last played"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/recentlyViewed',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE,
False),
('browse',
utils.lang(39702), # "Browse by folder"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/folder',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}',
'folder': True
},
v.CONTENT_TYPE_MOVIE,
True),
('more',
utils.lang(22082), # "More..."
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}',
'section_id': '{self.section_id}',
'folder': True
},
v.CONTENT_TYPE_FILE,
True),
),
###########################################################
v.PLEX_TYPE_SHOW: (
('ondeck',
utils.lang(39500), # "On Deck"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/onDeck',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_EPISODE,
True),
('recent',
utils.lang(30174), # "Recently Added"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/recentlyAdded',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_EPISODE,
False),
('all',
'{self.name}', # We're using this section's name
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/all',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_SHOW,
False),
('recommended',
utils.lang(30230), # "Recommended"
{
'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'rating:desc'})),
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_SHOW,
False),
('genres',
utils.lang(135), # "Genres"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/genre',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_SHOW,
False),
('sets',
utils.lang(39501), # "Collections"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/collection',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_SHOW,
True), # There are no sets/collections for shows with Kodi
('random',
utils.lang(30227), # "Random"
{
'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'random'})),
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_SHOW,
False),
('lastplayed',
utils.lang(568), # "Last played"
{
'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}/recentlyViewed&%s'
% urllib.urlencode({'type': v.PLEX_TYPE_NUMBER_FROM_PLEX_TYPE[v.PLEX_TYPE_EPISODE]})),
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_EPISODE,
False),
('browse',
utils.lang(39702), # "Browse by folder"
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/folder',
'section_id': '{self.section_id}',
'folder': True
},
v.CONTENT_TYPE_EPISODE,
True),
('more',
utils.lang(22082), # "More..."
{
'mode': 'browseplex',
'key': '/library/sections/{self.section_id}',
'section_id': '{self.section_id}',
'folder': True
},
v.CONTENT_TYPE_FILE,
True),
),
}
def node_pms(section, node_name, args):
"""
Nodes where the logic resides with the PMS - we're NOT building an
xml that filters and sorts, but point to PKC add-on path
Be sure to set args['folder'] = True if the listing is a folder and does
not contain playable elements like movies, episodes or tracks
"""
if 'folder' in args:
args = copy.deepcopy(args)
args.pop('folder')
folder = True
else:
folder = False
xml = etree.Element('node',
attrib={'order': unicode(section.order),
'type': 'folder' if folder else 'filter'})
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml, 'path').text = section.addon_path(args)
return xml
def node_ondeck(section, node_name):
"""
For movies only - returns in-progress movies sorted by last played
"""
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
etree.SubElement(xml, 'rule', attrib={'field': 'inprogress',
'operator': 'true'})
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml, 'limit').text = utils.settings('widgetLimit')
etree.SubElement(xml,
'order',
attrib={'direction':
'descending'}).text = 'lastplayed'
return xml
def node_recent(section, node_name):
xml = etree.Element('node',
attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
if ((section.section_type == v.PLEX_TYPE_SHOW and
utils.settings('TVShowWatched') == 'false') or
(section.section_type == v.PLEX_TYPE_MOVIE and
utils.settings('MovieShowWatched') == 'false')):
# Adds an additional rule if user deactivated the PKC setting
# "Recently Added: Also show already watched episodes"
# or
# "Recently Added: Also show already watched episodes"
rule = etree.SubElement(xml, 'rule', attrib={'field': 'playcount',
'operator': 'is'})
etree.SubElement(rule, 'value').text = '0'
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml, 'limit').text = utils.settings('widgetLimit')
etree.SubElement(xml,
'order',
attrib={'direction':
'descending'}).text = 'dateadded'
return xml
def node_all(section, node_name):
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml,
'order',
attrib={'direction':
'ascending'}).text = 'sorttitle'
return xml
def node_recommended(section, node_name):
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
# rule = etree.SubElement(xml, 'rule', attrib={'field': 'rating',
# 'operator': 'greaterthan'})
# etree.SubElement(rule, 'value').text = unicode(RECOMMENDED_SCORE_LOWER_BOUND)
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml, 'limit').text = utils.settings('widgetLimit')
etree.SubElement(xml,
'order',
attrib={'direction':
'descending'}).text = 'rating'
return xml
def node_genres(section, node_name):
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml,
'order',
attrib={'direction':
'ascending'}).text = 'sorttitle'
etree.SubElement(xml, 'group').text = 'genres'
return xml
def node_sets(section, node_name):
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
# "Collections"
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml,
'order',
attrib={'direction':
'ascending'}).text = 'sorttitle'
etree.SubElement(xml, 'group').text = 'sets'
return xml
def node_random(section, node_name):
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml, 'limit').text = utils.settings('widgetLimit')
etree.SubElement(xml,
'order',
attrib={'direction':
'ascending'}).text = 'random'
return xml
def node_lastplayed(section, node_name):
xml = etree.Element('node', attrib={'order': unicode(section.order),
'type': 'filter'})
etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag',
'operator': 'is'})
etree.SubElement(rule, 'value').text = section.name
rule = etree.SubElement(xml, 'rule', attrib={'field': 'playcount',
'operator': 'greaterthan'})
etree.SubElement(rule, 'value').text = '0'
etree.SubElement(xml, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content
etree.SubElement(xml, 'limit').text = utils.settings('widgetLimit')
etree.SubElement(xml,
'order',
attrib={'direction':
'descending'}).text = 'lastplayed'
return xml