################################################################################################# # VideoNodes - utils to create video nodes listings in kodi for the emby addon ################################################################################################# import xbmc import xbmcgui import xbmcaddon import xbmcvfs import json import os import shutil #import common elementree because cElementree has issues with kodi import xml.etree.ElementTree as etree import Utils as utils from ReadEmbyDB import ReadEmbyDB WINDOW = xbmcgui.Window(10000) addonSettings = xbmcaddon.Addon() language = addonSettings.getLocalizedString class VideoNodes(): def buildVideoNodeForView(self, tagname, type, windowPropId): #this method will build a video node for a particular Emby view (= tag in kodi) #we set some window props here to for easy future reference and to be used in skins (for easy access only) tagname_normalized = utils.normalize_nodes(tagname.encode('utf-8')) libraryPath = xbmc.translatePath("special://profile/library/video/Emby - %s/" %tagname_normalized) kodiVersion = 14 if xbmc.getInfoLabel("System.BuildVersion").startswith("15") or xbmc.getInfoLabel("System.BuildVersion").startswith("16"): kodiVersion = 15 #create tag node - index xbmcvfs.mkdir(libraryPath) nodefile = os.path.join(libraryPath, "index.xml") root = etree.Element("node", {"order":"0"}) etree.SubElement(root, "label").text = tagname etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" path = "library://video/Emby - %s/" %tagname_normalized WINDOW.setProperty("Emby.nodes.%s.index" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - all items nodefile = os.path.join(libraryPath, tagname_normalized + "_all.xml") root = etree.Element("node", {"order":"1", "type":"filter"}) etree.SubElement(root, "label").text = tagname etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = type etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) WINDOW.setProperty("Emby.nodes.%s.title" %str(windowPropId),tagname) path = "library://video/Emby - %s/%s_all.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.content" %str(windowPropId),path) WINDOW.setProperty("Emby.nodes.%s.type" %str(windowPropId),type) etree.SubElement(Rule, "value").text = tagname try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - recent items nodefile = os.path.join(libraryPath, tagname_normalized + "_recent.xml") root = etree.Element("node", {"order":"2", "type":"filter"}) if type == "tvshows": label = language(30170) else: label = language(30174) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = type etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname etree.SubElement(root, "order", {"direction":"descending"}).text = "dateadded" #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ? etree.SubElement(root, "limit").text = "25" #exclude watched items --> currently hardcoded --> TODO: add a setting for this ? Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"}) etree.SubElement(Rule2, "value").text = "0" WINDOW.setProperty("Emby.nodes.%s.recent.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_recent.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.recent.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.recent.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - inprogress items nodefile = os.path.join(libraryPath, tagname_normalized + "_progress.xml") root = etree.Element("node", {"order":"3", "type":"filter"}) if type == "tvshows": label = language(30171) else: label = language(30177) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = type etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ? etree.SubElement(root, "limit").text = "25" Rule2 = etree.SubElement(root, "rule", {"field":"inprogress","operator":"true"}) WINDOW.setProperty("Emby.nodes.%s.inprogress.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_progress.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.inprogress.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.inprogress.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #some movies-only nodes if type == "movies": #unwatched movies nodefile = os.path.join(libraryPath, tagname_normalized + "_unwatched.xml") root = etree.Element("node", {"order":"4", "type":"filter"}) label = language(30189) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = "movies" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname Rule = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"}) etree.SubElement(Rule, "value").text = "0" etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle" Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"}) etree.SubElement(Rule2, "value").text = "0" WINDOW.setProperty("Emby.nodes.%s.unwatched.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_unwatched.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.unwatched.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.unwatched.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #sets nodefile = os.path.join(libraryPath, tagname_normalized + "_sets.xml") root = etree.Element("node", {"order":"9", "type":"filter"}) label = xbmc.getLocalizedString(20434) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "group").text = "sets" etree.SubElement(root, "content").text = type etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname WINDOW.setProperty("Emby.nodes.%s.sets.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_sets.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.sets.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.sets.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - genres nodefile = os.path.join(libraryPath, tagname_normalized + "_genres.xml") root = etree.Element("node", {"order":"9", "type":"filter"}) label = xbmc.getLocalizedString(135) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "group").text = "genres" etree.SubElement(root, "content").text = type etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname WINDOW.setProperty("Emby.nodes.%s.genres.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_genres.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.genres.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.genres.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #### TAGS ONLY FOR TV SHOWS COLLECTIONS #### if type == "tvshows": #as from kodi isengard you can use tags for episodes to filter #for below isengard we still use the plugin's entrypoint to build a listing if kodiVersion == 15: #create tag node - recent episodes nodefile = os.path.join(libraryPath, tagname_normalized + "_recent_episodes.xml") root = etree.Element("node", {"order":"3", "type":"filter"}) label = language(30175) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = "episodes" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" etree.SubElement(root, "order", {"direction":"descending"}).text = "dateadded" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ? etree.SubElement(root, "limit").text = "25" #exclude watched items --> currently hardcoded --> TODO: add a setting for this ? Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"}) etree.SubElement(Rule2, "value").text = "0" WINDOW.setProperty("Emby.nodes.%s.recentepisodes.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_recent_episodes.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.recentepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.recentepisodes.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - inprogress episodes nodefile = os.path.join(libraryPath, tagname_normalized + "_progress_episodes.xml") root = etree.Element("node", {"order":"4", "type":"filter"}) label = language(30178) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = "episodes" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = tagname #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ? etree.SubElement(root, "limit").text = "25" Rule2 = etree.SubElement(root, "rule", {"field":"inprogress","operator":"true"}) WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_progress_episodes.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) if kodiVersion == 14: #create tag node - recent episodes nodefile = os.path.join(libraryPath, tagname_normalized + "_recent_episodes.xml") root = etree.Element("node", {"order":"4", "type":"folder"}) label = language(30175) etree.SubElement(root, "label").text = label etree.SubElement(root, "content").text = "episodes" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" %tagname etree.SubElement(root, "path").text = path WINDOW.setProperty("Emby.nodes.%s.recentepisodes.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_recent_episodes.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.recentepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.recentepisodes.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - inprogress items nodefile = os.path.join(libraryPath, tagname_normalized + "_progress_episodes.xml") root = etree.Element("node", {"order":"5", "type":"folder"}) label = language(30178) etree.SubElement(root, "label").text = label etree.SubElement(root, "content").text = "episodes" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25" %tagname etree.SubElement(root, "path").text = path WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_progress_episodes.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - nextup items #for nextup we always use the dynamic content approach with the plugin's entrypoint because it involves a custom query nodefile = os.path.join(libraryPath, tagname_normalized + "_nextup_episodes.xml") root = etree.Element("node", {"order":"6", "type":"folder"}) label = language(30179) etree.SubElement(root, "label").text = label etree.SubElement(root, "content").text = "episodes" path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" %tagname etree.SubElement(root, "path").text = path etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" WINDOW.setProperty("Emby.nodes.%s.nextepisodes.title" %str(windowPropId),label) path = "library://video/Emby - %s/%s_nextup_episodes.xml"%(tagname_normalized,tagname_normalized) WINDOW.setProperty("Emby.nodes.%s.nextepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.nextepisodes.content" %str(windowPropId),path) try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) def buildVideoNodesListing(self): try: # the library path doesn't exist on all systems if not xbmcvfs.exists("special://profile/library/"): xbmcvfs.mkdir("special://profile/library") if not xbmcvfs.exists("special://profile/library/video/"): #we need to copy over the default items shutil.copytree(xbmc.translatePath("special://xbmc/system/library/video"), xbmc.translatePath("special://profile/library/video")) #always cleanup existing Emby video nodes first because we don't want old stuff to stay in there path = "special://profile/library/video/" if xbmcvfs.exists(path): allDirs, allFiles = xbmcvfs.listdir(path) for dir in allDirs: if dir.startswith("Emby "): shutil.rmtree(xbmc.translatePath("special://profile/library/video/" + dir)) for file in allFiles: if file.startswith("emby"): xbmcvfs.delete(path + file) #we build up a listing and set window props for all nodes we created #the window props will be used by the main entry point to quickly build up the listing and can be used in skins (like titan) too for quick reference #comment marcelveldt: please leave the window props as-is because I will be referencing them in titan skin... totalNodesCount = 0 #build the listing for all views views_movies = ReadEmbyDB().getCollections("movies") if views_movies: for view in views_movies: self.buildVideoNodeForView(view.get('title'), "movies", totalNodesCount) totalNodesCount +=1 views_shows = ReadEmbyDB().getCollections("tvshows") if views_shows: for view in views_shows: self.buildVideoNodeForView(view.get('title'), "tvshows", totalNodesCount) totalNodesCount +=1 #create tag node for emby channels nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"), "emby_channels.xml") root = etree.Element("node", {"order":"1", "type":"folder"}) label = language(30173) etree.SubElement(root, "label").text = label etree.SubElement(root, "content").text = "movies" etree.SubElement(root, "path").text = "plugin://plugin.video.emby/?id=0&mode=channels" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label) WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"channels") path = "library://video/emby_channels.xml" WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path) totalNodesCount +=1 try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - favorite shows nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"),"emby_favorite_shows.xml") root = etree.Element("node", {"order":"1", "type":"filter"}) label = language(30181) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = "tvshows" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = "Favorite tvshows" #do not localize the tagname itself WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label) WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"favourites") path = "library://video/emby_favorite_shows.xml" WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path) totalNodesCount +=1 try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) #create tag node - favorite movies nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"),"emby_favorite_movies.xml") root = etree.Element("node", {"order":"1", "type":"filter"}) label = language(30180) etree.SubElement(root, "label").text = label etree.SubElement(root, "match").text = "all" etree.SubElement(root, "content").text = "movies" etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png" etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle" Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"}) etree.SubElement(Rule, "value").text = "Favorite movies" #do not localize the tagname itself WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label) WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"favourites") path = "library://video/emby_favorite_movies.xml" WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path) WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path) totalNodesCount +=1 try: etree.ElementTree(root).write(nodefile, xml_declaration=True) except: etree.ElementTree(root).write(nodefile) WINDOW.setProperty("Emby.nodes.total", str(totalNodesCount)) except Exception as e: utils.logMsg("Emby addon","Error while creating videonodes listings, restart required ?") print e