New views.py

Combines maintainsViews from librarysync and videonodes and playlist
creation. Still need to move deletion from utils.
This commit is contained in:
angelblue05 2016-11-05 13:36:46 -05:00
parent 1d8d01b5f8
commit 16df4d6404
7 changed files with 291 additions and 987 deletions

View file

@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import xbmc
import api
import artwork
#################################################################################################
log = logging.getLogger("EMBY."+__name__)
#################################################################################################
class Kodidb_Functions(object):
kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
def __init__(self, cursor):
self.cursor = cursor
self.artwork = artwork.Artwork()
def createTag(self, name):
# This will create and return the tag_id
if self.kodiversion in (15, 16, 17):
# Kodi Isengard, Jarvis, Krypton
query = ' '.join((
"SELECT tag_id",
"FROM tag",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (name,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
self.cursor.execute("select coalesce(max(tag_id),0) from tag")
tag_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
self.cursor.execute(query, (tag_id, name))
log.debug("Create tag_id: %s name: %s", tag_id, name)
else:
# Kodi Helix
query = ' '.join((
"SELECT idTag",
"FROM tag",
"WHERE strTag = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (name,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
self.cursor.execute("select coalesce(max(idTag),0) from tag")
tag_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
self.cursor.execute(query, (tag_id, name))
log.debug("Create idTag: %s name: %s", tag_id, name)
return tag_id
def updateTag(self, oldtag, newtag, kodiid, mediatype):
# TODO: Move to video nodes eventually
log.debug("Updating: %s with %s for %s: %s", oldtag, newtag, mediatype, kodiid)
if self.kodiversion in (15, 16, 17):
# Kodi Isengard, Jarvis, Krypton
try:
query = ' '.join((
"UPDATE tag_link",
"SET tag_id = ?",
"WHERE media_id = ?",
"AND media_type = ?",
"AND tag_id = ?"
))
self.cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
except Exception:
# The new tag we are going to apply already exists for this item
# delete current tag instead
query = ' '.join((
"DELETE FROM tag_link",
"WHERE media_id = ?",
"AND media_type = ?",
"AND tag_id = ?"
))
self.cursor.execute(query, (kodiid, mediatype, oldtag,))
else:
# Kodi Helix
try:
query = ' '.join((
"UPDATE taglinks",
"SET idTag = ?",
"WHERE idMedia = ?",
"AND media_type = ?",
"AND idTag = ?"
))
self.cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
except Exception:
# The new tag we are going to apply already exists for this item
# delete current tag instead
query = ' '.join((
"DELETE FROM taglinks",
"WHERE idMedia = ?",
"AND media_type = ?",
"AND idTag = ?"
))
self.cursor.execute(query, (kodiid, mediatype, oldtag,))

View file

@ -17,10 +17,9 @@ import clientinfo
import downloadutils import downloadutils
import itemtypes import itemtypes
import embydb_functions as embydb import embydb_functions as embydb
import kodidb_functions as kodidb
import read_embyserver as embyserver import read_embyserver as embyserver
import userclient import userclient
import videonodes import views
from objects import Movies, MusicVideos, TVShows, Music from objects import Movies, MusicVideos, TVShows, Music
from utils import window, settings, language as lang, should_stop from utils import window, settings, language as lang, should_stop
from ga_client import GoogleAnalytics from ga_client import GoogleAnalytics
@ -60,7 +59,6 @@ class LibrarySync(threading.Thread):
self.doUtils = downloadutils.DownloadUtils().downloadUrl self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.user = userclient.UserClient() self.user = userclient.UserClient()
self.emby = embyserver.Read_EmbyServer() self.emby = embyserver.Read_EmbyServer()
self.vnodes = videonodes.VideoNodes()
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
@ -276,7 +274,9 @@ class LibrarySync(threading.Thread):
starttotal = datetime.now() starttotal = datetime.now()
# Set views # Set views
self.maintainViews(cursor_emby, cursor_video) views.Views(cursor_emby, cursor_video).maintain()
conn_emby.commit()
#self.maintainViews(cursor_emby, cursor_video)
# Sync video library # Sync video library
process = { process = {
@ -362,222 +362,10 @@ class LibrarySync(threading.Thread):
def refreshViews(self): def refreshViews(self):
with DatabaseConn('emby') as conn_emby, DatabaseConn('video') as conn_video: with DatabaseConn('emby') as conn_emby, DatabaseConn() as conn_video:
with closing(conn_emby.cursor()) as cursor_emby, closing(conn_video.cursor()) as cursor_video: with closing(conn_emby.cursor()) as cursor_emby, closing(conn_video.cursor()) as cursor_video:
# Compare views, assign correct tags to items # Compare views, assign correct tags to items
self.maintainViews(cursor_emby, cursor_video) views.Views(cursor_emby, cursor_video).maintain()
def maintainViews(self, embycursor, kodicursor):
# Compare the views to emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
kodi_db = kodidb.Kodidb_Functions(kodicursor)
# Get views
result = self.doUtils("{server}/emby/Users/{UserId}/Views?format=json")
grouped_views = result['Items'] if 'Items' in result else []
ordered_views = self.emby.getViews(sortedlist=True)
all_views = []
sorted_views = []
for view in ordered_views:
all_views.append(view['name'])
if view['type'] == "music":
continue
if view['type'] == "mixed":
sorted_views.append(view['name'])
sorted_views.append(view['name'])
log.info("Sorted views: %s" % sorted_views)
# total nodes for window properties
self.vnodes.clearProperties()
totalnodes = len(sorted_views) + 0
current_views = emby_db.getViews()
# Set views for supported media type
emby_mediatypes = {
'movies': "Movie",
'tvshows': "Series",
'musicvideos': "MusicVideo",
'homevideos': "Video",
'music': "Audio",
'photos': "Photo"
}
for mediatype in ['movies', 'tvshows', 'musicvideos', 'homevideos', 'music', 'photos']:
nodes = [] # Prevent duplicate for nodes of the same type
playlists = [] # Prevent duplicate for playlists of the same type
# Get media folders from server
folders = self.emby.getViews(mediatype, root=True)
for folder in folders:
folderid = folder['id']
foldername = folder['name']
viewtype = folder['type']
if foldername not in all_views:
# Media folders are grouped into userview
params = {
'ParentId': folderid,
'Recursive': True,
'Limit': 1,
'IncludeItemTypes': emby_mediatypes[mediatype]
} # Get one item from server using the folderid
url = "{server}/emby/Users/{UserId}/Items?format=json"
result = self.doUtils(url, parameters=params)
try:
verifyitem = result['Items'][0]['Id']
except (TypeError, IndexError):
# Something is wrong. Keep the same folder name.
# Could be the view is empty or the connection
pass
else:
for grouped_view in grouped_views:
# This is only reserved for the detection of grouped views
if (grouped_view['Type'] == "UserView" and
grouped_view.get('CollectionType') == mediatype):
# Take the userview, and validate the item belong to the view
if self.emby.verifyView(grouped_view['Id'], verifyitem):
# Take the name of the userview
log.info("Found corresponding view: %s %s"
% (grouped_view['Name'], grouped_view['Id']))
foldername = grouped_view['Name']
break
else:
# Unable to find a match, add the name to our sorted_view list
sorted_views.append(foldername)
log.info("Couldn't find corresponding grouped view: %s" % sorted_views)
# Failsafe
try:
sorted_views.index(foldername)
except ValueError:
sorted_views.append(foldername)
# Get current media folders from emby database
view = emby_db.getView_byId(folderid)
try:
current_viewname = view[0]
current_viewtype = view[1]
current_tagid = view[2]
except TypeError:
log.info("Creating viewid: %s in Emby database." % folderid)
tagid = kodi_db.createTag(foldername)
# Create playlist for the video library
if (foldername not in playlists and
mediatype in ('movies', 'tvshows', 'musicvideos')):
utils.playlistXSP(mediatype, foldername, folderid, viewtype)
playlists.append(foldername)
# Create the video node
if foldername not in nodes and mediatype not in ("musicvideos", "music"):
self.vnodes.viewNode(sorted_views.index(foldername), foldername, mediatype,
viewtype, folderid)
if viewtype == "mixed": # Change the value
sorted_views[sorted_views.index(foldername)] = "%ss" % foldername
nodes.append(foldername)
totalnodes += 1
# Add view to emby database
emby_db.addView(folderid, foldername, viewtype, tagid)
else:
log.debug(' '.join((
"Found viewid: %s" % folderid,
"viewname: %s" % current_viewname,
"viewtype: %s" % current_viewtype,
"tagid: %s" % current_tagid)))
# View is still valid
try:
current_views.remove(folderid)
except ValueError:
# View was just created, nothing to remove
pass
# View was modified, update with latest info
if current_viewname != foldername:
log.info("viewid: %s new viewname: %s" % (folderid, foldername))
tagid = kodi_db.createTag(foldername)
# Update view with new info
emby_db.updateView(foldername, tagid, folderid)
if mediatype != "music":
if emby_db.getView_byName(current_viewname) is None:
# The tag could be a combined view. Ensure there's no other tags
# with the same name before deleting playlist.
utils.playlistXSP(
mediatype, current_viewname, folderid, current_viewtype, True)
# Delete video node
if mediatype != "musicvideos":
self.vnodes.viewNode(
indexnumber=None,
tagname=current_viewname,
mediatype=mediatype,
viewtype=current_viewtype,
viewid=folderid,
delete=True)
# Added new playlist
if (foldername not in playlists and
mediatype in ('movies', 'tvshows', 'musicvideos')):
utils.playlistXSP(mediatype, foldername, folderid, viewtype)
playlists.append(foldername)
# Add new video node
if foldername not in nodes and mediatype != "musicvideos":
self.vnodes.viewNode(sorted_views.index(foldername), foldername,
mediatype, viewtype, folderid)
if viewtype == "mixed": # Change the value
sorted_views[sorted_views.index(foldername)] = "%ss" % foldername
nodes.append(foldername)
totalnodes += 1
# Update items with new tag
items = emby_db.getItem_byView(folderid)
for item in items:
# Remove the "s" from viewtype for tags
kodi_db.updateTag(
current_tagid, tagid, item[0], current_viewtype[:-1])
else:
# Validate the playlist exists or recreate it
if mediatype != "music":
if (foldername not in playlists and
mediatype in ('movies', 'tvshows', 'musicvideos')):
utils.playlistXSP(mediatype, foldername, folderid, viewtype)
playlists.append(foldername)
# Create the video node if not already exists
if foldername not in nodes and mediatype != "musicvideos":
self.vnodes.viewNode(sorted_views.index(foldername), foldername,
mediatype, viewtype, folderid)
if viewtype == "mixed": # Change the value
sorted_views[sorted_views.index(foldername)] = "%ss" % foldername
nodes.append(foldername)
totalnodes += 1
else:
# Add video nodes listings
self.vnodes.singleNode(totalnodes, "Favorite movies", "movies", "favourites")
totalnodes += 1
self.vnodes.singleNode(totalnodes, "Favorite tvshows", "tvshows", "favourites")
totalnodes += 1
self.vnodes.singleNode(totalnodes, "Favorite episodes", "episodes", "favourites")
totalnodes += 1
self.vnodes.singleNode(totalnodes, "channels", "movies", "channels")
totalnodes += 1
# Save total
window('Emby.nodes.total', str(totalnodes))
# Remove any old referenced views
log.info("Removing views: %s" % current_views)
for view in current_views:
emby_db.removeView(view)
# Remove any items that belongs to the old view
items = emby_db.get_item_by_view(view)
items = [i[0] for i in items] # Convert list of tuple to list
self.triage_items("remove", items)
def movies(self, embycursor, kodicursor, pdialog): def movies(self, embycursor, kodicursor, pdialog):

View file

@ -11,7 +11,6 @@ import xbmcgui
import clientinfo import clientinfo
import downloadutils import downloadutils
import kodidb_functions as kodidb
import websocket_client as wsc import websocket_client as wsc
from utils import window, settings, language as lang from utils import window, settings, language as lang
from ga_client import GoogleAnalytics, log_error from ga_client import GoogleAnalytics, log_error

View file

@ -31,6 +31,9 @@ class Read_EmbyServer():
self.userId = window('emby_currUser') self.userId = window('emby_currUser')
self.server = window('emby_server%s' % self.userId) self.server = window('emby_server%s' % self.userId)
def get_emby_url(self, handler):
return "{server}/emby/%s" % handler
def split_list(self, itemlist, size): def split_list(self, itemlist, size):
# Split up list in pieces of size. Will generate a list of lists # Split up list in pieces of size. Will generate a list of lists
@ -589,4 +592,15 @@ class Read_EmbyServer():
data = {'username': username, 'password': hashlib.sha1(password).hexdigest()} data = {'username': username, 'password': hashlib.sha1(password).hexdigest()}
user = self.doUtils(url, postBody=data, action_type="POST", authenticate=False) user = self.doUtils(url, postBody=data, action_type="POST", authenticate=False)
return user return user
def get_single_item(self, media_type, parent_id):
params = {
'ParentId': parent_id,
'Recursive': True,
'Limit': 1,
'IncludeItemTypes': media_type
}
url = self.get_emby_url('Users/{UserId}/Items?format=json')
return self.doUtils(url, parameters=params)

View file

@ -16,8 +16,8 @@ import initialsetup
import kodimonitor import kodimonitor
import librarysync import librarysync
import player import player
import videonodes
import websocket_client as wsc import websocket_client as wsc
from views import VideoNodes
from utils import window, settings, dialog, language as lang from utils import window, settings, dialog, language as lang
from ga_client import GoogleAnalytics from ga_client import GoogleAnalytics
import hashlib import hashlib
@ -77,7 +77,7 @@ class Service(object):
window(prop, clear=True) window(prop, clear=True)
# Clear video nodes properties # Clear video nodes properties
videonodes.VideoNodes().clearProperties() VideoNodes().clearProperties()
# Set the minimum database version # Set the minimum database version
window('emby_minDBVersion', value="1.1.63") window('emby_minDBVersion', value="1.1.63")

View file

@ -1,392 +0,0 @@
# -*- coding: utf-8 -*-
#################################################################################################
import logging
import shutil
import xml.etree.ElementTree as etree
import xbmc
import xbmcaddon
import xbmcvfs
import utils
from utils import window, language as lang
#################################################################################################
log = logging.getLogger("EMBY."+__name__)
#################################################################################################
class VideoNodes(object):
def __init__(self):
self.kodiversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
def commonRoot(self, order, label, tagname, roottype=1):
if roottype == 0:
# Index
root = etree.Element('node', attrib={'order': "%s" % order})
elif roottype == 1:
# Filter
root = etree.Element('node', attrib={'order': "%s" % order, 'type': "filter"})
etree.SubElement(root, 'match').text = "all"
# Add tag rule
rule = etree.SubElement(root, 'rule', attrib={'field': "tag", 'operator': "is"})
etree.SubElement(rule, 'value').text = tagname
else:
# Folder
root = etree.Element('node', attrib={'order': "%s" % order, 'type': "folder"})
etree.SubElement(root, 'label').text = label
etree.SubElement(root, 'icon').text = "special://home/addons/plugin.video.emby/icon.png"
return root
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False):
if viewtype == "mixed":
dirname = "%s - %s" % (viewid, mediatype)
else:
dirname = viewid
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
nodepath = xbmc.translatePath(
"special://profile/library/video/emby/%s/" % dirname).decode('utf-8')
# Verify the video directory
if not xbmcvfs.exists(path):
try:
shutil.copytree(
src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
except Exception as error:
log.error(error)
xbmcvfs.exists(path)
if delete:
dirs, files = xbmcvfs.listdir(nodepath)
for file in files:
xbmcvfs.delete(nodepath + file)
log.info("Sucessfully removed videonode: %s." % tagname)
return
# Create the node directory
if not xbmcvfs.exists(nodepath) and not mediatype == "photos":
# We need to copy over the default items
xbmcvfs.mkdirs(nodepath)
# Create index entry
nodeXML = "%sindex.xml" % nodepath
# Set windows property
path = "library://video/emby/%s/" % dirname
for i in range(1, indexnumber):
# Verify to make sure we don't create duplicates
if window('Emby.nodes.%s.index' % i) == path:
return
if mediatype == "photos":
path = "plugin://plugin.video.emby/?id=%s&mode=getsubfolders" % indexnumber
window('Emby.nodes.%s.index' % indexnumber, value=path)
# Root
if not mediatype == "photos":
if viewtype == "mixed":
specialtag = "%s - %s" % (tagname, mediatype)
root = self.commonRoot(order=0, label=specialtag, tagname=tagname, roottype=0)
else:
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
try:
utils.indent(root)
except: pass
etree.ElementTree(root).write(nodeXML)
nodetypes = {
'1': "all",
'2': "recent",
'3': "recentepisodes",
'4': "inprogress",
'5': "inprogressepisodes",
'6': "unwatched",
'7': "nextepisodes",
'8': "sets",
'9': "genres",
'10': "random",
'11': "recommended",
}
mediatypes = {
# label according to nodetype per mediatype
'movies':
{
'1': tagname,
'2': 30174,
'4': 30177,
'6': 30189,
'8': 20434,
'9': 135,
'10': 30229,
'11': 30230
},
'tvshows':
{
'1': tagname,
'2': 30170,
'3': 30175,
'4': 30171,
'5': 30178,
'7': 30179,
'9': 135,
'10': 30229,
'11': 30230
},
'homevideos':
{
'1': tagname,
'2': 30251,
'11': 30253
},
'photos':
{
'1': tagname,
'2': 30252,
'8': 30255,
'11': 30254
},
'musicvideos':
{
'1': tagname,
'2': 30256,
'4': 30257,
'6': 30258
}
}
nodes = mediatypes[mediatype]
for node in nodes:
nodetype = nodetypes[node]
nodeXML = "%s%s.xml" % (nodepath, nodetype)
# Get label
stringid = nodes[node]
if node != "1":
label = lang(stringid)
if not label:
label = xbmc.getLocalizedString(stringid)
else:
label = stringid
# Set window properties
if (mediatype == "homevideos" or mediatype == "photos") and nodetype == "all":
# Custom query
path = ("plugin://plugin.video.emby/?id=%s&mode=browsecontent&type=%s"
% (tagname, mediatype))
elif (mediatype == "homevideos" or mediatype == "photos"):
# Custom query
path = ("plugin://plugin.video.emby/?id=%s&mode=browsecontent&type=%s&folderid=%s"
% (tagname, mediatype, nodetype))
elif nodetype == "nextepisodes":
# Custom query
path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" % tagname
elif self.kodiversion == 14 and nodetype == "recentepisodes":
# Custom query
path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" % tagname
elif self.kodiversion == 14 and nodetype == "inprogressepisodes":
# Custom query
path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25"% tagname
else:
path = "library://video/emby/%s/%s.xml" % (viewid, nodetype)
if mediatype == "photos":
windowpath = "ActivateWindow(Pictures,%s,return)" % path
else:
windowpath = "ActivateWindow(Videos,%s,return)" % path
if nodetype == "all":
if viewtype == "mixed":
templabel = "%s - %s" % (tagname, mediatype)
else:
templabel = label
embynode = "Emby.nodes.%s" % indexnumber
window('%s.title' % embynode, value=templabel)
window('%s.path' % embynode, value=windowpath)
window('%s.content' % embynode, value=path)
window('%s.type' % embynode, value=mediatype)
else:
embynode = "Emby.nodes.%s.%s" % (indexnumber, nodetype)
window('%s.title' % embynode, value=label)
window('%s.path' % embynode, value=windowpath)
window('%s.content' % embynode, value=path)
if mediatype == "photos":
# 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
if xbmcvfs.exists(nodeXML):
# Don't recreate xml if already exists
continue
# Create the root
if (nodetype == "nextepisodes" or mediatype == "homevideos" or
(self.kodiversion == 14 and nodetype in ('recentepisodes', 'inprogressepisodes'))):
# Folder type with plugin path
root = self.commonRoot(order=node, label=label, tagname=tagname, roottype=2)
etree.SubElement(root, 'path').text = path
etree.SubElement(root, 'content').text = "episodes"
else:
root = self.commonRoot(order=node, label=label, tagname=tagname)
if nodetype in ('recentepisodes', 'inprogressepisodes'):
etree.SubElement(root, 'content').text = "episodes"
else:
etree.SubElement(root, 'content').text = mediatype
limit = "25"
# Elements per nodetype
if nodetype == "all":
etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
elif nodetype == "recent":
etree.SubElement(root, 'order', {'direction': "descending"}).text = "dateadded"
etree.SubElement(root, 'limit').text = limit
rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"})
etree.SubElement(rule, 'value').text = "0"
elif nodetype == "inprogress":
etree.SubElement(root, 'rule', {'field': "inprogress", 'operator': "true"})
etree.SubElement(root, 'limit').text = limit
elif nodetype == "genres":
etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
etree.SubElement(root, 'group').text = "genres"
elif nodetype == "unwatched":
etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
rule = etree.SubElement(root, "rule", {'field': "playcount", 'operator': "is"})
etree.SubElement(rule, 'value').text = "0"
elif nodetype == "sets":
etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
etree.SubElement(root, 'group').text = "sets"
elif nodetype == "random":
etree.SubElement(root, 'order', {'direction': "ascending"}).text = "random"
etree.SubElement(root, 'limit').text = limit
elif nodetype == "recommended":
etree.SubElement(root, 'order', {'direction': "descending"}).text = "rating"
etree.SubElement(root, 'limit').text = limit
rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"})
etree.SubElement(rule, 'value').text = "0"
rule2 = etree.SubElement(root, 'rule',
attrib={'field': "rating", 'operator': "greaterthan"})
etree.SubElement(rule2, 'value').text = "7"
elif nodetype == "recentepisodes":
# Kodi Isengard, Jarvis
etree.SubElement(root, 'order', {'direction': "descending"}).text = "dateadded"
etree.SubElement(root, 'limit').text = limit
rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"})
etree.SubElement(rule, 'value').text = "0"
elif nodetype == "inprogressepisodes":
# Kodi Isengard, Jarvis
etree.SubElement(root, 'limit').text = "25"
rule = etree.SubElement(root, 'rule',
attrib={'field': "inprogress", 'operator':"true"})
try:
utils.indent(root)
except: pass
etree.ElementTree(root).write(nodeXML)
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
tagname = tagname.encode('utf-8')
cleantagname = utils.normalize_nodes(tagname)
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
nodeXML = "%semby_%s.xml" % (nodepath, cleantagname)
path = "library://video/emby_%s.xml" % cleantagname
windowpath = "ActivateWindow(Videos,%s,return)" % path
# Create the video node directory
if not xbmcvfs.exists(nodepath):
# We need to copy over the default items
shutil.copytree(
src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
xbmcvfs.exists(path)
labels = {
'Favorite movies': 30180,
'Favorite tvshows': 30181,
'Favorite episodes': 30182,
'channels': 30173
}
label = lang(labels[tagname])
embynode = "Emby.nodes.%s" % indexnumber
window('%s.title' % embynode, value=label)
window('%s.path' % embynode, value=windowpath)
window('%s.content' % embynode, value=path)
window('%s.type' % embynode, value=itemtype)
if xbmcvfs.exists(nodeXML):
# Don't recreate xml if already exists
return
if itemtype == "channels":
root = self.commonRoot(order=1, label=label, tagname=tagname, roottype=2)
etree.SubElement(root, 'path').text = "plugin://plugin.video.emby/?id=0&mode=channels"
elif itemtype == "favourites" and mediatype == "episodes":
root = self.commonRoot(order=1, label=label, tagname=tagname, roottype=2)
etree.SubElement(root, 'path').text = "plugin://plugin.video.emby/?id=%s&mode=browsecontent&type=%s&folderid=favepisodes" %(tagname, mediatype)
else:
root = self.commonRoot(order=1, label=label, tagname=tagname)
etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
etree.SubElement(root, 'content').text = mediatype
try:
utils.indent(root)
except: pass
etree.ElementTree(root).write(nodeXML)
def clearProperties(self):
log.info("Clearing nodes properties.")
embyprops = window('Emby.nodes.total')
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"
]
if embyprops:
totalnodes = int(embyprops)
for i in range(totalnodes):
for prop in propnames:
window('Emby.nodes.%s.%s' % (str(i), prop), clear=True)

View file

@ -13,7 +13,7 @@ import xbmcvfs
import read_embyserver as embyserver import read_embyserver as embyserver
import embydb_functions as embydb import embydb_functions as embydb
from utils import window, language as lang from utils import window, language as lang, normalize_nodes, indent as xml_indent
################################################################################################# #################################################################################################
@ -30,20 +30,34 @@ class Views(object):
playlists = list() playlists = list()
views = list() views = list()
sorted_views = list() sorted_views = list()
grouped_views = list()
media_types = {
'movies': "Movie",
'tvshows': "Series",
'musicvideos': "MusicVideo",
'homevideos': "Video",
'music': "Audio",
'photos': "Photo"
}
def __init__(self, emby_cursor, kodi_cursor): def __init__(self, emby_cursor, kodi_cursor):
self.emby_cursor = emby_cursor self.emby_cursor = emby_cursor
self.kodi_cursor = kodi_cursor self.kodi_cursor = kodi_cursor
self.video_nodes = VideoNodes() self.video_nodes = VideoNodes()
self.playlist = Playlist()
self.emby = embyserver.Read_EmbyServer() self.emby = embyserver.Read_EmbyServer()
self.emby_db = embydb.Embydb_Functions(embycursor) self.emby_db = embydb.Embydb_Functions(emby_cursor)
def _populate_views(self): def _populate_views(self):
# Will get emby views and views in Kodi # Will get emby views and views in Kodi
self.views = self.emby.getViews(sortedlist=True) grouped_views = self.emby.get_views()
for view in self.views: self.grouped_views = grouped_views['Items'] if "Items" in grouped_views else []
for view in self.emby.getViews(sortedlist=True):
self.views.append(view['name'])
if view['type'] == "music": if view['type'] == "music":
continue continue
@ -57,178 +71,105 @@ class Views(object):
def maintain(self): def maintain(self):
# Compare views to emby # Compare views to emby
self._populate_views() self._populate_views()
view_list = [view['name'] for view in self.all_views] curr_views = self.emby_db.getViews()
grouped_views = self.emby.get_views()
current_views = emby_db.getViews()
# total nodes for window properties # total nodes for window properties
self.vnodes.clearProperties() self.video_nodes.clearProperties()
# Set views for supported media type for media_type in ('movies', 'tvshows', 'musicvideos', 'homevideos', 'music', 'photos'):
emby_mediatypes = {
'movies': "Movie",
'tvshows': "Series",
'musicvideos': "MusicVideo",
'homevideos': "Video",
'music': "Audio",
'photos': "Photo"
}
for mediatype in ['movies', 'tvshows', 'musicvideos', 'homevideos', 'music', 'photos']:
self.nodes = list() # Prevent duplicate for nodes of the same type self.nodes = list() # Prevent duplicate for nodes of the same type
self.playlists = list() # Prevent duplicate for playlists of the same type self.playlists = list() # Prevent duplicate for playlists of the same type
# Get media folders from server # Get media folders from the ordered
for folder in [view for view in ordered if view['type'] == mediatype]: for folder in self.emby.getViews(media_type, root=True):
folderid = folder['id'] view_id = folder['id']
foldername = folder['name'] view_name = folder['name']
viewtype = folder['type'] view_type = folder['type']
if foldername not in view_list: if view_name not in self.views:
# Media folders are grouped into userview # Media folders are grouped into userview
params = { view_name = self._get_grouped_view(media_type, view_id, view_name)
'ParentId': folderid,
'Recursive': True, try: # Make sure the view is in sorted views before proceeding
'Limit': 1, self.sorted_views.index(view_name)
'IncludeItemTypes': emby_mediatypes[mediatype]
} # Get one item from server using the folderid
url = "{server}/emby/Users/{UserId}/Items?format=json"
result = self.doUtils(url, parameters=params)
try:
verifyitem = result['Items'][0]['Id']
except (TypeError, IndexError):
# Something is wrong. Keep the same folder name.
# Could be the view is empty or the connection
pass
else:
for grouped_view in grouped_views:
# This is only reserved for the detection of grouped views
if (grouped_view['Type'] == "UserView" and
grouped_view.get('CollectionType') == mediatype):
# Take the userview, and validate the item belong to the view
if self.emby.verifyView(grouped_view['Id'], verifyitem):
# Take the name of the userview
log.info("Found corresponding view: %s %s"
% (grouped_view['Name'], grouped_view['Id']))
foldername = grouped_view['Name']
break
else:
# Unable to find a match, add the name to our sorted_view list
self.sorted_views.append(foldername)
log.info("Couldn't find corresponding grouped view: %s" % self.sorted_views)
# Failsafe
try:
self.sorted_views.index(foldername)
except ValueError: except ValueError:
self.sorted_views.append(foldername) self.sorted_views.append(view_name)
# Get current media folders from emby database # Get current media folders from emby database and compare
view = emby_db.getView_byId(folderid) if self.compare_view(media_type, view_id, view_name, view_type):
try: if view_id in curr_views: # View is still valid
current_viewname = view[0] curr_views.remove(view_id)
current_viewtype = view[1]
current_tagid = view[2]
except TypeError:
self._add_view(mediatype, folderid, foldername, viewtype)
else:
log.debug(' '.join((
"Found viewid: %s" % folderid,
"viewname: %s" % current_viewname,
"viewtype: %s" % current_viewtype,
"tagid: %s" % current_tagid)))
# View is still valid
try:
current_views.remove(folderid)
except ValueError:
# View was just created, nothing to remove
pass
# View was modified, update with latest info
if current_viewname != foldername:
log.info("viewid: %s new viewname: %s" % (folderid, foldername))
tagid = self._get_tag(foldername)
# Update view with new info
emby_db.updateView(foldername, tagid, folderid)
if mediatype != "music":
if emby_db.getView_byName(current_viewname) is None:
# The tag could be a combined view. Ensure there's no other tags
# with the same name before deleting playlist.
self._playlist(mediatype, current_viewname, folderid,
current_viewtype, True)
# Delete video node
if mediatype != "musicvideos":
self.vnodes.viewNode(
indexnumber=None,
tagname=current_viewname,
mediatype=mediatype,
viewtype=current_viewtype,
viewid=folderid,
delete=True)
# Added new playlist
self.create_playlist(mediatype, foldername, folderid, viewtype)
# Add new video node
self.create_node(mediatype, foldername, folderid, viewtype)
# Update items with new tag
items = emby_db.getItem_byView(folderid)
for item in items:
# Remove the "s" from viewtype for tags
self._update_tag(
current_tagid, tagid, item[0], current_viewtype[:-1])
else:
# Validate the playlist exists or recreate it
if mediatype != "music":
self.create_playlist(mediatype, foldername, folderid, viewtype)
# Create the video node if not already exists
self.create_node(mediatype, foldername, folderid, viewtype)
# Add video nodes listings # Add video nodes listings
self.add_single_node(totalnodes, "Favorite movies", "movies", "favourites") self.add_single_nodes()
self.add_single_node(totalnodes, "Favorite tvshows", "tvshows", "favourites")
self.add_single_node(totalnodes, "Favorite episodes", "episodes", "favourites")
self.add_single_node(totalnodes, "channels", "movies", "channels")
# Save total # Save total
window('Emby.nodes.total', str(self.total_nodes)) window('Emby.nodes.total', str(self.total_nodes))
# Remove any old referenced views # Remove any old referenced views
log.info("Removing views: %s", current_views) log.info("Removing views: %s", curr_views)
for view in current_views: for view in curr_views:
self.remove_view(view) self.remove_view(view)
def create_node(self, media_type, view_name, view_id, view_type): def _get_grouped_view(self, media_type, view_id, view_name):
# Get single item from view to compare
result = self.emby.get_single_item(self.media_types[media_type], view_id)
try:
item = result['Items'][0]['Id']
except (TypeError, IndexError):
# Something is wrong. Keep the same folder name.
# Could be the view is empty or the connection
pass
else:
for view in self.grouped_views:
if view['Type'] == "UserView" and view.get('CollectionType') == media_type:
# Take the userview, and validate the item belong to the view
if self.emby.verifyView(view['Id'], item):
log.info("found corresponding view: %s %s", view['Name'], view['Id'])
view_name = view['Name']
break
else: # Unable to find a match, add the name to our sorted_view list
log.info("couldn't find corresponding grouped view: %s", self.sorted_views)
if view_name not in self.nodes and media_type not in ('musicvideos', 'music'): return view_name
index = self.sorted_views.index(view_name)
self.video_nodes.viewNode(index, view_name, media_type,view_type, view_id)
if view_type == "mixed": # Change the value
self.sorted_views[index] = "%ss" % view_name
self.nodes.append(view_name)
self.total_nodes += 1
def add_single_node(self, index, tag, media_type, item_type):
self.video_nodes.singleNode(index, tag, media_type, item_type)
self.total_nodes += 1
def add_view(self, media_type, view_id, view_name, view_type): def add_view(self, media_type, view_id, view_name, view_type):
# Generate view, playlist and video node # Generate view, playlist and video node
log.info("creating view %s: %s", view_name, view_id) log.info("creating view %s: %s", view_name, view_id)
tag_id = self._get_tag(view_name) tag_id = self.get_tag(view_name)
# Create playlist for the video library self.add_playlist_node(media_type, view_id, view_name, view_type)
self.create_playlist(media_type, view_name, view_id, view_type)
# Create the video node
self.create_node(media_type, view_name, view_id, view_type)
# Add view to emby database # Add view to emby database
self.emby_db.addView(view_id, view_name, view_type, tag_id) self.emby_db.addView(view_id, view_name, view_type, tag_id)
def compare_view(self, media_type, view_id, view_name, view_type):
curr_view = self.emby_db.getView_byId(view_id)
try:
curr_view_name = curr_view[0]
curr_view_type = curr_view[1]
curr_tag_id = curr_view[2]
except TypeError:
self.add_view(media_type, view_id, view_name, view_type)
return False
# View is still valid
log.debug("Found viewid: %s viewname: %s viewtype: %s tagid: %s",
view_id, curr_view_name, curr_view_type, curr_tag_id)
if curr_view_name != view_name:
# View was modified, update with latest info
log.info("viewid: %s new viewname: %s", view_id, view_name)
tag_id = self.get_tag(view_name)
# Update view with new info
self.emby_db.updateView(view_name, tag_id, view_id)
# Delete old playlists and video nodes
self.delete_playlist_node(media_type, curr_view_name, view_id, curr_view_type)
# Update items with new tag
self._update_items_tag(curr_view_type[:-1], view_id, curr_tag_id, tag_id)
# Verify existance of playlist and nodes
self.add_playlist_node(media_type, view_id, view_name, view_type)
return True
def remove_view(self, view): def remove_view(self, view):
# Remove any items that belongs to the old view # Remove any items that belongs to the old view
items = self.emby_db.get_item_by_view(view) items = self.emby_db.get_item_by_view(view)
@ -236,79 +177,13 @@ class Views(object):
# TODO: Triage not accessible from here yet # TODO: Triage not accessible from here yet
#self.triage_items("remove", items) #self.triage_items("remove", items)
def create_playlist(self, media_type, view_name, view_id, view_type): def _update_items_tag(self, media_type, view_id, tag, new_tag):
items = self.emby_db.getItem_byView(view_id)
for item in items:
# Remove the "s" from viewtype for tags
self._update_tag(tag, new_tag, item[0], media_type)
if view_name not in self.playlists and media_type in ('movies', 'tvshows', 'musicvideos'): def get_tag(self, tag):
self._playlist(media_type, view_name, view_id, view_type)
self.playlists.append(view_name)
@classmethod
def _add_playlist(cls, tag, playlist_name, path, media_type):
# Using write process since there's no guarantee the xml declaration works with etree
special_types = {'homevideos': "movies"}
log.info("writing playlist to: %s", path)
try:
f = xbmcvfs.File(path, 'w')
except:
log.info("failed to create playlist: %s", path)
return False
else:
f.write(
'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n'
'<smartplaylist type="%s">\n\t'
'<name>Emby %s</name>\n\t'
'<match>all</match>\n\t'
'<rule field="tag" operator="is">\n\t\t'
'<value>%s</value>\n\t'
'</rule>'
'</smartplaylist>'
% (special_types.get(media_type, media_type), playlist_name, tag))
f.close()
log.info("successfully added playlist: %s" % tag)
return True
@classmethod
def _delete_playlist(cls, path):
xbmcvfs.delete(path)
log.info("successfully removed playlist: %s", path)
def _playlist(self, media_type, tag, view_id, view_type="", delete=False):
# Tagname is in unicode - actions: add or delete
tag = tag.encode('utf-8')
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
if view_type == "mixed":
playlist_name = "%s - %s" % (tag, media_type)
xsp_path = os.path.join(path, "Emby %s - %s.xsp" % (view_id, media_type))
else:
playlist_name = tag
xsp_path = os.path.join(path, "Emby %s" % view_id)
# Only add the playlist if it doesn't exist
if xbmcvfs.exists(xsp_path):
if delete:
self._delete_playlist(xsp_path)
return True
elif not xbmcvfs.exists(path):
log.info("creating directory: %s", path)
xbmcvfs.mkdirs(path)
return self._add_playlist(tag, playlist_name, xsp_path, media_type)
def _add_tag(self, tag):
self.kodi_cursor.execute("select coalesce(max(tag_id),0) from tag")
tag_id = self.kodi_cursor.fetchone()[0] + 1
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
self.kodi_cursor.execute(query, (tag_id, tag))
log.debug("Create tag_id: %s name: %s", tag_id, tag)
return tag_id
def _get_tag(self, tag):
# This will create and return the tag_id # This will create and return the tag_id
if KODI in (15, 16, 17): if KODI in (15, 16, 17):
# Kodi Isengard, Jarvis, Krypton # Kodi Isengard, Jarvis, Krypton
@ -345,6 +220,17 @@ class Views(object):
return tag_id return tag_id
def _add_tag(self, tag):
self.kodi_cursor.execute("select coalesce(max(tag_id),0) from tag")
tag_id = self.kodi_cursor.fetchone()[0] + 1
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
self.kodi_cursor.execute(query, (tag_id, tag))
log.debug("Create tag_id: %s name: %s", tag_id, tag)
return tag_id
def _update_tag(self, tag, new_tag, kodi_id, media_type): def _update_tag(self, tag, new_tag, kodi_id, media_type):
log.debug("Updating: %s with %s for %s: %s", tag, new_tag, media_type, kodi_id) log.debug("Updating: %s with %s for %s: %s", tag, new_tag, media_type, kodi_id)
@ -395,6 +281,110 @@ class Views(object):
)) ))
self.kodi_cursor.execute(query, (kodi_id, media_type, tag,)) self.kodi_cursor.execute(query, (kodi_id, media_type, tag,))
def add_playlist_node(self, media_type, view_id, view_name, view_type):
# Create playlist for the video library
if view_name not in self.playlists and media_type in ('movies', 'tvshows', 'musicvideos'):
self.playlist.process_playlist(media_type, view_id, view_name, view_type)
self.playlists.append(view_name)
# Create the video node
self._create_node(media_type, view_id, view_name, view_type)
def delete_playlist_node(self, media_type, view_id, view_name, view_type):
if media_type == "music":
return
if self.emby_db.getView_byName(view_name) is None:
# The tag could be a combined view. Ensure there's no other tags
# with the same name before deleting playlist.
self.playlist.process_playlist(media_type, view_id, view_name, view_type, True)
# Delete video node
if media_type != "musicvideos":
self.video_nodes.viewNode(None, view_name, media_type, view_type, view_id, True)
def _create_node(self, media_type, view_id, view_name, view_type):
if view_name not in self.nodes and media_type not in ('musicvideos', 'music'):
index = self.sorted_views.index(view_name)
self.video_nodes.viewNode(index, view_name, media_type,view_type, view_id)
if view_type == "mixed": # Change the value
self.sorted_views[index] = "%ss" % view_name
self.nodes.append(view_name)
self.total_nodes += 1
def add_single_nodes(self):
singles = [
("Favorite movies", "movies", "favourites"),
("Favorite tvshows", "tvshows", "favourites"),
("Favorite episodes", "episodes", "favourites"),
("channels", "movies", "channels")
]
for args in singles:
self._single_node(self.total_nodes, *args)
def _single_node(self, index, tag, media_type, view_type):
self.video_nodes.singleNode(index, tag, media_type, view_type)
self.total_nodes += 1
class Playlist(object):
def __init__(self):
pass
def process_playlist(self, media_type, view_id, view_name, view_type, delete=False):
# Tagname is in unicode - actions: add or delete
tag = view_name.encode('utf-8')
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
if view_type == "mixed":
playlist_name = "%s - %s" % (tag, media_type)
xsp_path = os.path.join(path, "Emby %s - %s.xsp" % (view_id, media_type))
else:
playlist_name = tag
xsp_path = os.path.join(path, "Emby %s" % view_id)
# Only add the playlist if it doesn't exist
if xbmcvfs.exists(xsp_path):
if delete:
self._delete_playlist(xsp_path)
return
elif not xbmcvfs.exists(path):
log.info("creating directory: %s", path)
xbmcvfs.mkdirs(path)
self._add_playlist(tag, playlist_name, xsp_path, media_type)
def _add_playlist(self, tag, name, path, media_type):
# Using write process since there's no guarantee the xml declaration works with etree
special_types = {'homevideos': "movies"}
log.info("writing playlist to: %s", path)
try:
f = xbmcvfs.File(path, 'w')
except:
log.info("failed to create playlist: %s", path)
else:
f.write(
'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n'
'<smartplaylist type="%s">\n\t'
'<name>Emby %s</name>\n\t'
'<match>all</match>\n\t'
'<rule field="tag" operator="is">\n\t\t'
'<value>%s</value>\n\t'
'</rule>'
'</smartplaylist>'
% (special_types.get(media_type, media_type), name, tag))
f.close()
log.info("successfully added playlist: %s" % tag)
@classmethod
def _delete_playlist(cls, path):
xbmcvfs.delete(path)
log.info("successfully removed playlist: %s", path)
class VideoNodes(object): class VideoNodes(object):
@ -402,7 +392,7 @@ class VideoNodes(object):
def __init__(self): def __init__(self):
pass pass
def commonRoot(self, order, label, tagname, roottype=1): def commonRoot(self, order, label, tagname="", roottype=1):
if roottype == 0: if roottype == 0:
# Index # Index
@ -430,29 +420,10 @@ class VideoNodes(object):
else: else:
dirname = viewid dirname = viewid
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
nodepath = xbmc.translatePath( nodepath = xbmc.translatePath(
"special://profile/library/video/emby/%s/" % dirname).decode('utf-8') "special://profile/library/video/emby/%s/" % dirname).decode('utf-8')
# Verify the video directory
if not xbmcvfs.exists(path):
try:
shutil.copytree(
src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
except Exception as error:
log.error(error)
xbmcvfs.exists(path)
emby_path = xbmc.translatePath("special://profile/library/video/emby/index.xml").decode('utf-8')
if not emby_path:
root = self.commonRoot(order=0, label="Emby", roottype=0)
try:
utils.indent(root)
except: pass
etree.ElementTree(root).write(emby_path)
if delete: if delete:
dirs, files = xbmcvfs.listdir(nodepath) dirs, files = xbmcvfs.listdir(nodepath)
for file in files: for file in files:
@ -460,10 +431,32 @@ class VideoNodes(object):
log.info("Sucessfully removed videonode: %s." % tagname) log.info("Sucessfully removed videonode: %s." % tagname)
return return
# Verify the video directory
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
if not xbmcvfs.exists(path):
try:
shutil.copytree(
src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
except Exception as error:
log.error(error)
xbmcvfs.mkdir(path)
embypath = xbmc.translatePath("special://profile/library/video/emby/").decode('utf-8')
if not xbmcvfs.exists(embypath):
xbmcvfs.mkdir(embypath)
root = self.commonRoot(order=0, label="Emby", roottype=0)
try:
xml_indent(root)
except: pass
etree.ElementTree(root).write(os.path.join(embypath, "index.xml"))
# Create the node directory # Create the node directory
if not xbmcvfs.exists(nodepath) and not mediatype == "photos": if not xbmcvfs.exists(nodepath) and not mediatype == "photos":
# We need to copy over the default items # We need to copy over the default items
xbmcvfs.mkdirs(nodepath) xbmcvfs.mkdir(nodepath)
# Create index entry # Create index entry
nodeXML = "%sindex.xml" % nodepath nodeXML = "%sindex.xml" % nodepath
@ -487,7 +480,7 @@ class VideoNodes(object):
else: else:
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0) root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
try: try:
utils.indent(root) xml_indent(root)
except: pass except: pass
etree.ElementTree(root).write(nodeXML) etree.ElementTree(root).write(nodeXML)
@ -582,10 +575,10 @@ class VideoNodes(object):
elif nodetype == "nextepisodes": elif nodetype == "nextepisodes":
# Custom query # Custom query
path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" % tagname path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" % tagname
elif self.kodiversion == 14 and nodetype == "recentepisodes": elif KODI == 14 and nodetype == "recentepisodes":
# Custom query # Custom query
path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" % tagname path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" % tagname
elif self.kodiversion == 14 and nodetype == "inprogressepisodes": elif KODI == 14 and nodetype == "inprogressepisodes":
# Custom query # Custom query
path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25"% tagname path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25"% tagname
else: else:
@ -626,7 +619,7 @@ class VideoNodes(object):
# Create the root # Create the root
if (nodetype == "nextepisodes" or mediatype == "homevideos" or if (nodetype == "nextepisodes" or mediatype == "homevideos" or
(self.kodiversion == 14 and nodetype in ('recentepisodes', 'inprogressepisodes'))): (KODI == 14 and nodetype in ('recentepisodes', 'inprogressepisodes'))):
# Folder type with plugin path # Folder type with plugin path
root = self.commonRoot(order=node, label=label, tagname=tagname, roottype=2) root = self.commonRoot(order=node, label=label, tagname=tagname, roottype=2)
etree.SubElement(root, 'path').text = path etree.SubElement(root, 'path').text = path
@ -693,14 +686,14 @@ class VideoNodes(object):
attrib={'field': "inprogress", 'operator':"true"}) attrib={'field': "inprogress", 'operator':"true"})
try: try:
utils.indent(root) xml_indent(root)
except: pass except: pass
etree.ElementTree(root).write(nodeXML) etree.ElementTree(root).write(nodeXML)
def singleNode(self, indexnumber, tagname, mediatype, itemtype): def singleNode(self, indexnumber, tagname, mediatype, itemtype):
tagname = tagname.encode('utf-8') tagname = tagname.encode('utf-8')
cleantagname = utils.normalize_nodes(tagname) cleantagname = normalize_nodes(tagname)
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8') nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
nodeXML = "%semby_%s.xml" % (nodepath, cleantagname) nodeXML = "%semby_%s.xml" % (nodepath, cleantagname)
path = "library://video/emby_%s.xml" % cleantagname path = "library://video/emby_%s.xml" % cleantagname
@ -745,6 +738,30 @@ class VideoNodes(object):
etree.SubElement(root, 'content').text = mediatype etree.SubElement(root, 'content').text = mediatype
try: try:
utils.indent(root) xml_indent(root)
except: pass except: pass
etree.ElementTree(root).write(nodeXML) etree.ElementTree(root).write(nodeXML)
def clearProperties(self):
log.info("Clearing nodes properties.")
embyprops = window('Emby.nodes.total')
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"
]
if embyprops:
totalnodes = int(embyprops)
for i in range(totalnodes):
for prop in propnames:
window('Emby.nodes.%s.%s' % (str(i), prop), clear=True)