diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 11c66f5d..f5c4ef20 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -1,1089 +1,1089 @@ -# -*- coding: utf-8 -*- - -################################################################################################# - -import json -import os -import sys -import urlparse - -import xbmc -import xbmcaddon -import xbmcgui -import xbmcvfs -import xbmcplugin - -import artwork -import utils -import clientinfo -import downloadutils -import librarysync -import read_embyserver as embyserver -import embydb_functions as embydb -import playlist -import playbackutils as pbutils -import playutils -import api - - -################################################################################################# - - -def doPlayback(itemid, dbid): - - emby = embyserver.Read_EmbyServer() - item = emby.getItem(itemid) - pbutils.PlaybackUtils(item).play(itemid, dbid) - -##### DO RESET AUTH ##### -def resetAuth(): - # User tried login and failed too many times - resp = xbmcgui.Dialog().yesno( - heading="Warning", - line1=( - "Emby might lock your account if you fail to log in too many times. " - "Proceed anyway?")) - if resp == 1: - utils.logMsg("EMBY", "Reset login attempts.", 1) - utils.window('emby_serverStatus', value="Auth") - else: - xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)') - -def addDirectoryItem(label, path, folder=True): - li = xbmcgui.ListItem(label, path=path) - li.setThumbnailImage("special://home/addons/plugin.video.emby/icon.png") - li.setArt({"fanart":"special://home/addons/plugin.video.emby/fanart.jpg"}) - li.setArt({"landscape":"special://home/addons/plugin.video.emby/fanart.jpg"}) - xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=folder) - -def doMainListing(): - xbmcplugin.setContent(int(sys.argv[1]), 'files') - # Get emby nodes from the window props - embyprops = utils.window('Emby.nodes.total') - if embyprops: - totalnodes = int(embyprops) - for i in range(totalnodes): - path = utils.window('Emby.nodes.%s.index' % i) - if not path: - path = utils.window('Emby.nodes.%s.content' % i) - label = utils.window('Emby.nodes.%s.title' % i) - type = utils.window('Emby.nodes.%s.type' % i) - #because we do not use seperate entrypoints for each content type, we need to figure out which items to show in each listing. - #for now we just only show picture nodes in the picture library video nodes in the video library and all nodes in any other window - if path and xbmc.getCondVisibility("Window.IsActive(Pictures)") and type == "photos": - addDirectoryItem(label, path) - elif path and xbmc.getCondVisibility("Window.IsActive(VideoLibrary)") and type != "photos": - addDirectoryItem(label, path) - elif path and not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) | Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"): - addDirectoryItem(label, path) - - #experimental live tv nodes - addDirectoryItem("Live Tv Channels (experimental)", "plugin://plugin.video.emby/?mode=browsecontent&type=tvchannels&folderid=root") - addDirectoryItem("Live Tv Recordings (experimental)", "plugin://plugin.video.emby/?mode=browsecontent&type=recordings&folderid=root") - - # some extra entries for settings and stuff. TODO --> localize the labels - addDirectoryItem("Network credentials", "plugin://plugin.video.emby/?mode=passwords") - addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings") - addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser") - addDirectoryItem("Refresh Emby playlists/nodes", "plugin://plugin.video.emby/?mode=refreshplaylist") - addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync") - addDirectoryItem("Repair local database (force update all content)", "plugin://plugin.video.emby/?mode=repair") - addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset") - addDirectoryItem("Cache all images to Kodi texture cache", "plugin://plugin.video.emby/?mode=texturecache") - addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia") - - xbmcplugin.endOfDirectory(int(sys.argv[1])) - - -##### Generate a new deviceId -def resetDeviceId(): - - dialog = xbmcgui.Dialog() - language = utils.language - - deviceId_old = utils.window('emby_deviceId') - try: - utils.window('emby_deviceId', clear=True) - deviceId = clientinfo.ClientInfo().getDeviceId(reset=True) - except Exception as e: - utils.logMsg("EMBY", "Failed to generate a new device Id: %s" % e, 1) - dialog.ok( - heading="Emby for Kodi", - line1=language(33032)) - else: - utils.logMsg("EMBY", "Successfully removed old deviceId: %s New deviceId: %s" - % (deviceId_old, deviceId), 1) - dialog.ok( - heading="Emby for Kodi", - line1=language(33033)) - xbmc.executebuiltin('RestartApp') - -##### Delete Item -def deleteItem(): - - # Serves as a keymap action - if xbmc.getInfoLabel('ListItem.Property(embyid)'): # If we already have the embyid - embyid = xbmc.getInfoLabel('ListItem.Property(embyid)') - else: - dbid = xbmc.getInfoLabel('ListItem.DBID') - itemtype = xbmc.getInfoLabel('ListItem.DBTYPE') - - if not itemtype: - - if xbmc.getCondVisibility('Container.Content(albums)'): - itemtype = "album" - elif xbmc.getCondVisibility('Container.Content(artists)'): - itemtype = "artist" - elif xbmc.getCondVisibility('Container.Content(songs)'): - itemtype = "song" - elif xbmc.getCondVisibility('Container.Content(pictures)'): - itemtype = "picture" - else: - utils.logMsg("EMBY delete", "Unknown type, unable to proceed.", 1) - return - - embyconn = utils.kodiSQL('emby') - embycursor = embyconn.cursor() - emby_db = embydb.Embydb_Functions(embycursor) - item = emby_db.getItem_byKodiId(dbid, itemtype) - embycursor.close() - - try: - embyid = item[0] - except TypeError: - utils.logMsg("EMBY delete", "Unknown embyId, unable to proceed.", 1) - return - - if utils.settings('skipContextMenu') != "true": - resp = xbmcgui.Dialog().yesno( - heading="Confirm delete", - line1=("Delete file from Emby Server? This will " - "also delete the file(s) from disk!")) - if not resp: - utils.logMsg("EMBY delete", "User skipped deletion for: %s." % embyid, 1) - return - - doUtils = downloadutils.DownloadUtils() - url = "{server}/emby/Items/%s?format=json" % embyid - utils.logMsg("EMBY delete", "Deleting request: %s" % embyid, 0) - doUtils.downloadUrl(url, type="DELETE") - -##### ADD ADDITIONAL USERS ##### -def addUser(): - - doUtils = downloadutils.DownloadUtils() - art = artwork.Artwork() - clientInfo = clientinfo.ClientInfo() - deviceId = clientInfo.getDeviceId() - deviceName = clientInfo.getDeviceName() - userid = utils.window('emby_currUser') - dialog = xbmcgui.Dialog() - - # Get session - url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId - result = doUtils.downloadUrl(url) - - try: - sessionId = result[0]['Id'] - additionalUsers = result[0]['AdditionalUsers'] - # Add user to session - userlist = {} - users = [] - url = "{server}/emby/Users?IsDisabled=false&IsHidden=false&format=json" - result = doUtils.downloadUrl(url) - - # pull the list of users - for user in result: - name = user['Name'] - userId = user['Id'] - if userid != userId: - userlist[name] = userId - users.append(name) - - # Display dialog if there's additional users - if additionalUsers: - - option = dialog.select("Add/Remove user from the session", ["Add user", "Remove user"]) - # Users currently in the session - additionalUserlist = {} - additionalUsername = [] - # Users currently in the session - for user in additionalUsers: - name = user['UserName'] - userId = user['UserId'] - additionalUserlist[name] = userId - additionalUsername.append(name) - - if option == 1: - # User selected Remove user - resp = dialog.select("Remove user from the session", additionalUsername) - if resp > -1: - selected = additionalUsername[resp] - selected_userId = additionalUserlist[selected] - url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) - doUtils.downloadUrl(url, postBody={}, type="DELETE") - dialog.notification( - heading="Success!", - message="%s removed from viewing session" % selected, - icon="special://home/addons/plugin.video.emby/icon.png", - time=1000) - - # clear picture - position = utils.window('EmbyAdditionalUserPosition.%s' % selected_userId) - utils.window('EmbyAdditionalUserImage.%s' % position, clear=True) - return - else: - return - - elif option == 0: - # User selected Add user - for adduser in additionalUsername: - try: # Remove from selected already added users. It is possible they are hidden. - users.remove(adduser) - except: pass - - elif option < 0: - # User cancelled - return - - # Subtract any additional users - utils.logMsg("EMBY", "Displaying list of users: %s" % users) - resp = dialog.select("Add user to the session", users) - # post additional user - if resp > -1: - selected = users[resp] - selected_userId = userlist[selected] - url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) - doUtils.downloadUrl(url, postBody={}, type="POST") - dialog.notification( - heading="Success!", - message="%s added to viewing session" % selected, - icon="special://home/addons/plugin.video.emby/icon.png", - time=1000) - - except: - utils.logMsg("EMBY", "Failed to add user to session.") - dialog.notification( - heading="Error", - message="Unable to add/remove user from the session.", - icon=xbmcgui.NOTIFICATION_ERROR) - - # Add additional user images - # always clear the individual items first - totalNodes = 10 - for i in range(totalNodes): - if not utils.window('EmbyAdditionalUserImage.%s' % i): - break - utils.window('EmbyAdditionalUserImage.%s' % i, clear=True) - - url = "{server}/emby/Sessions?DeviceId=%s" % deviceId - result = doUtils.downloadUrl(url) - additionalUsers = result[0]['AdditionalUsers'] - count = 0 - for additionaluser in additionalUsers: - userid = additionaluser['UserId'] - url = "{server}/emby/Users/%s?format=json" % userid - result = doUtils.downloadUrl(url) - utils.window('EmbyAdditionalUserImage.%s' % count, - value=art.getUserArtwork(result['Id'], 'Primary')) - utils.window('EmbyAdditionalUserPosition.%s' % userid, value=str(count)) - count +=1 - -##### THEME MUSIC/VIDEOS ##### -def getThemeMedia(): - - doUtils = downloadutils.DownloadUtils() - dialog = xbmcgui.Dialog() - playback = None - - # Choose playback method - resp = dialog.select("Playback method for your themes", ["Direct Play", "Direct Stream"]) - if resp == 0: - playback = "DirectPlay" - elif resp == 1: - playback = "DirectStream" - else: - return - - library = xbmc.translatePath( - "special://profile/addon_data/plugin.video.emby/library/").decode('utf-8') - # Create library directory - if not xbmcvfs.exists(library): - xbmcvfs.mkdir(library) - - # Set custom path for user - tvtunes_path = xbmc.translatePath( - "special://profile/addon_data/script.tvtunes/").decode('utf-8') - if xbmcvfs.exists(tvtunes_path): - tvtunes = xbmcaddon.Addon(id="script.tvtunes") - tvtunes.setSetting('custom_path_enable', "true") - tvtunes.setSetting('custom_path', library) - utils.logMsg("EMBY", "TV Tunes custom path is enabled and set.", 1) - else: - # if it does not exist this will not work so warn user - # often they need to edit the settings first for it to be created. - dialog.ok( - heading="Warning", - line1=( - "The settings file does not exist in tvtunes. ", - "Go to the tvtunes addon and change a setting, then come back and re-run.")) - xbmc.executebuiltin('Addon.OpenSettings(script.tvtunes)') - return - - # Get every user view Id - embyconn = utils.kodiSQL('emby') - embycursor = embyconn.cursor() - emby_db = embydb.Embydb_Functions(embycursor) - viewids = emby_db.getViews() - embycursor.close() - - # Get Ids with Theme Videos - itemIds = {} - for view in viewids: - url = "{server}/emby/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view - result = doUtils.downloadUrl(url) - if result['TotalRecordCount'] != 0: - for item in result['Items']: - itemId = item['Id'] - folderName = item['Name'] - folderName = utils.normalize_string(folderName.encode('utf-8')) - itemIds[itemId] = folderName - - # Get paths for theme videos - for itemId in itemIds: - nfo_path = xbmc.translatePath( - "special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId]) - # Create folders for each content - if not xbmcvfs.exists(nfo_path): - xbmcvfs.mkdir(nfo_path) - # Where to put the nfos - nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") - - url = "{server}/emby/Items/%s/ThemeVideos?format=json" % itemId - result = doUtils.downloadUrl(url) - - # Create nfo and write themes to it - nfo_file = xbmcvfs.File(nfo_path, 'w') - pathstowrite = "" - # May be more than one theme - for theme in result['Items']: - putils = playutils.PlayUtils(theme) - if playback == "DirectPlay": - playurl = putils.directPlay() - else: - playurl = putils.directStream() - pathstowrite += ('%s' % playurl.encode('utf-8')) - - # Check if the item has theme songs and add them - url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId - result = doUtils.downloadUrl(url) - - # May be more than one theme - for theme in result['Items']: - putils = playutils.PlayUtils(theme) - if playback == "DirectPlay": - playurl = putils.directPlay() - else: - playurl = putils.directStream() - pathstowrite += ('%s' % playurl.encode('utf-8')) - - nfo_file.write( - '%s' % pathstowrite - ) - # Close nfo file - nfo_file.close() - - # Get Ids with Theme songs - musicitemIds = {} - for view in viewids: - url = "{server}/emby/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view - result = doUtils.downloadUrl(url) - if result['TotalRecordCount'] != 0: - for item in result['Items']: - itemId = item['Id'] - folderName = item['Name'] - folderName = utils.normalize_string(folderName.encode('utf-8')) - musicitemIds[itemId] = folderName - - # Get paths - for itemId in musicitemIds: - - # if the item was already processed with video themes back out - if itemId in itemIds: - continue - - nfo_path = xbmc.translatePath( - "special://profile/addon_data/plugin.video.emby/library/%s/" % musicitemIds[itemId]) - # Create folders for each content - if not xbmcvfs.exists(nfo_path): - xbmcvfs.mkdir(nfo_path) - # Where to put the nfos - nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") - - url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId - result = doUtils.downloadUrl(url) - - # Create nfo and write themes to it - nfo_file = xbmcvfs.File(nfo_path, 'w') - pathstowrite = "" - # May be more than one theme - for theme in result['Items']: - putils = playutils.PlayUtils(theme) - if playback == "DirectPlay": - playurl = putils.directPlay() - else: - playurl = putils.directStream() - pathstowrite += ('%s' % playurl.encode('utf-8')) - - nfo_file.write( - '%s' % pathstowrite - ) - # Close nfo file - nfo_file.close() - - dialog.notification( - heading="Emby for Kodi", - message="Themes added!", - icon="special://home/addons/plugin.video.emby/icon.png", - time=1000, - sound=False) - -##### REFRESH EMBY PLAYLISTS ##### -def refreshPlaylist(): - - lib = librarysync.LibrarySync() - dialog = xbmcgui.Dialog() - try: - # First remove playlists - utils.deletePlaylists() - # Remove video nodes - utils.deleteNodes() - # Refresh views - lib.refreshViews() - dialog.notification( - heading="Emby for Kodi", - message="Emby playlists/nodes refreshed", - icon="special://home/addons/plugin.video.emby/icon.png", - time=1000, - sound=False) - - except Exception as e: - utils.logMsg("EMBY", "Refresh playlists/nodes failed: %s" % e, 1) - dialog.notification( - heading="Emby for Kodi", - message="Emby playlists/nodes refresh failed", - icon=xbmcgui.NOTIFICATION_ERROR, - time=1000, - sound=False) - -#### SHOW SUBFOLDERS FOR NODE ##### -def GetSubFolders(nodeindex): - nodetypes = ["",".recent",".recentepisodes",".inprogress",".inprogressepisodes",".unwatched",".nextepisodes",".sets",".genres",".random",".recommended"] - for node in nodetypes: - title = utils.window('Emby.nodes.%s%s.title' %(nodeindex,node)) - if title: - path = utils.window('Emby.nodes.%s%s.content' %(nodeindex,node)) - type = utils.window('Emby.nodes.%s%s.type' %(nodeindex,node)) - addDirectoryItem(title, path) - xbmcplugin.endOfDirectory(int(sys.argv[1])) - -##### BROWSE EMBY NODES DIRECTLY ##### -def BrowseContent(viewname, type="", folderid=""): - - emby = embyserver.Read_EmbyServer() - art = artwork.Artwork() - doUtils = downloadutils.DownloadUtils() - - #folderid used as filter ? - if folderid in ["recent","recentepisodes","inprogress","inprogressepisodes","unwatched","nextepisodes","sets","genres","random","recommended"]: - filter = folderid - folderid = "" - else: - filter = "" - - xbmcplugin.setPluginCategory(int(sys.argv[1]), viewname) - #get views for root level - if not folderid: - views = emby.getViews(type) - for view in views: - if view.get("name") == viewname.decode('utf-8'): - folderid = view.get("id") - - if viewname is not None: - utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname.decode('utf-8'), type.decode('utf-8'), folderid.decode('utf-8'), filter.decode('utf-8'))) - #set the correct params for the content type - #only proceed if we have a folderid - if folderid: - if type.lower() == "homevideos": - xbmcplugin.setContent(int(sys.argv[1]), 'episodes') - itemtype = "Video,Folder,PhotoAlbum" - elif type.lower() == "photos": - xbmcplugin.setContent(int(sys.argv[1]), 'files') - itemtype = "Photo,PhotoAlbum,Folder" - else: - itemtype = "" - - #get the actual listing - if type == "recordings": - listing = emby.getTvRecordings(folderid) - elif type == "tvchannels": - listing = emby.getTvChannels() - elif filter == "recent": - listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="DateCreated", recursive=True, limit=25, sortorder="Descending") - elif filter == "random": - listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="Random", recursive=True, limit=150, sortorder="Descending") - elif filter == "recommended": - listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite") - elif filter == "sets": - listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[1], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite") - else: - listing = emby.getFilteredSection(folderid, itemtype=itemtype, recursive=False) - - #process the listing - if listing: - for item in listing.get("Items"): - li = createListItemFromEmbyItem(item,art,doUtils) - if item.get("IsFolder") == True: - #for folders we add an additional browse request, passing the folderId - path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), type.decode('utf-8'), item.get("Id").decode('utf-8')) - xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True) - else: - #playable item, set plugin path and mediastreams - xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=li.getProperty("path"), listitem=li) - - - if filter == "recent": - xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) - else: - xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) - xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) - xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING) - xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) - - xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) - -##### CREATE LISTITEM FROM EMBY METADATA ##### -def createListItemFromEmbyItem(item,art=artwork.Artwork(),doUtils=downloadutils.DownloadUtils()): - API = api.API(item) - itemid = item['Id'] - - title = item.get('Name') - li = xbmcgui.ListItem(title) - - premieredate = item.get('PremiereDate',"") - if not premieredate: premieredate = item.get('DateCreated',"") - if premieredate: - premieredatelst = premieredate.split('T')[0].split("-") - premieredate = "%s.%s.%s" %(premieredatelst[2],premieredatelst[1],premieredatelst[0]) - - li.setProperty("embyid",itemid) - - allart = art.getAllArtwork(item) - - if item["Type"] == "Photo": - #listitem setup for pictures... - img_path = allart.get('Primary') - li.setProperty("path",img_path) - picture = doUtils.downloadUrl("{server}/Items/%s/Images" %itemid) - if picture: - picture = picture[0] - if picture.get("Width") > picture.get("Height"): - li.setArt( {"fanart": img_path}) #add image as fanart for use with skinhelper auto thumb/backgrund creation - li.setInfo('pictures', infoLabels={ "picturepath": img_path, "date": premieredate, "size": picture.get("Size"), "exif:width": str(picture.get("Width")), "exif:height": str(picture.get("Height")), "title": title}) - li.setThumbnailImage(img_path) - li.setProperty("plot",API.getOverview()) - li.setIconImage('DefaultPicture.png') - else: - #normal video items - li.setProperty('IsPlayable', 'true') - path = "%s?id=%s&mode=play" % (sys.argv[0], item.get("Id")) - li.setProperty("path",path) - genre = API.getGenres() - overlay = 0 - userdata = API.getUserData() - runtime = item.get("RunTimeTicks",0)/ 10000000.0 - seektime = userdata['Resume'] - if seektime: - li.setProperty("resumetime", str(seektime)) - li.setProperty("totaltime", str(runtime)) - - played = userdata['Played'] - if played: overlay = 7 - else: overlay = 6 - playcount = userdata['PlayCount'] - if playcount is None: - playcount = 0 - - rating = item.get('CommunityRating') - if not rating: rating = userdata['UserRating'] - - # Populate the extradata list and artwork - extradata = { - 'id': itemid, - 'rating': rating, - 'year': item.get('ProductionYear'), - 'genre': genre, - 'playcount': str(playcount), - 'title': title, - 'plot': API.getOverview(), - 'Overlay': str(overlay), - 'duration': runtime - } - if premieredate: - extradata["premieredate"] = premieredate - extradata["date"] = premieredate - li.setInfo('video', infoLabels=extradata) - if allart.get('Primary'): - li.setThumbnailImage(allart.get('Primary')) - else: li.setThumbnailImage('DefaultTVShows.png') - li.setIconImage('DefaultTVShows.png') - if not allart.get('Background'): #add image as fanart for use with skinhelper auto thumb/backgrund creation - li.setArt( {"fanart": allart.get('Primary') } ) - else: - pbutils.PlaybackUtils(item).setArtwork(li) - - mediastreams = API.getMediaStreams() - videostreamFound = False - if mediastreams: - for key, value in mediastreams.iteritems(): - if key == "video" and value: videostreamFound = True - if value: li.addStreamInfo(key, value[0]) - if not videostreamFound: - #just set empty streamdetails to prevent errors in the logs - li.addStreamInfo("video", {'duration': runtime}) - - return li - -##### BROWSE EMBY CHANNELS ##### -def BrowseChannels(itemid, folderid=None): - - _addon_id = int(sys.argv[1]) - _addon_url = sys.argv[0] - doUtils = downloadutils.DownloadUtils() - art = artwork.Artwork() - - xbmcplugin.setContent(int(sys.argv[1]), 'files') - if folderid: - url = ( - "{server}/emby/Channels/%s/Items?userid={UserId}&folderid=%s&format=json" - % (itemid, folderid)) - elif itemid == "0": - # id 0 is the root channels folder - url = "{server}/emby/Channels?{UserId}&format=json" - else: - url = "{server}/emby/Channels/%s/Items?UserId={UserId}&format=json" % itemid - - result = doUtils.downloadUrl(url) - if result and result.get("Items"): - for item in result.get("Items"): - itemid = item['Id'] - itemtype = item['Type'] - li = createListItemFromEmbyItem(item,art,doUtils) - - isFolder = item.get('IsFolder', False) - - channelId = item.get('ChannelId', "") - channelName = item.get('ChannelName', "") - if itemtype == "Channel": - path = "%s?id=%s&mode=channels" % (_addon_url, itemid) - xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li, isFolder=True) - elif isFolder: - path = "%s?id=%s&mode=channelsfolder&folderid=%s" % (_addon_url, channelId, itemid) - xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li, isFolder=True) - else: - path = "%s?id=%s&mode=play" % (_addon_url, itemid) - li.setProperty('IsPlayable', 'true') - xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li) - - xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) - -##### LISTITEM SETUP FOR VIDEONODES ##### -def createListItem(item): - - title = item['title'] - li = xbmcgui.ListItem(title) - li.setProperty('IsPlayable', "true") - - metadata = { - - 'Title': title, - 'duration': str(item['runtime']/60), - 'Plot': item['plot'], - 'Playcount': item['playcount'] - } - - if "episode" in item: - episode = item['episode'] - metadata['Episode'] = episode - - if "season" in item: - season = item['season'] - metadata['Season'] = season - - if season and episode: - li.setProperty('episodeno', "s%.2de%.2d" % (season, episode)) - - if "firstaired" in item: - metadata['Premiered'] = item['firstaired'] - - if "showtitle" in item: - metadata['TVshowTitle'] = item['showtitle'] - - if "rating" in item: - metadata['Rating'] = str(round(float(item['rating']),1)) - - if "director" in item: - metadata['Director'] = " / ".join(item['director']) - - if "writer" in item: - metadata['Writer'] = " / ".join(item['writer']) - - if "cast" in item: - cast = [] - castandrole = [] - for person in item['cast']: - name = person['name'] - cast.append(name) - castandrole.append((name, person['role'])) - metadata['Cast'] = cast - metadata['CastAndRole'] = castandrole - - li.setInfo(type="Video", infoLabels=metadata) - li.setProperty('resumetime', str(item['resume']['position'])) - li.setProperty('totaltime', str(item['resume']['total'])) - li.setArt(item['art']) - li.setThumbnailImage(item['art'].get('thumb','')) - li.setIconImage('DefaultTVShows.png') - li.setProperty('dbid', str(item['episodeid'])) - li.setProperty('fanart_image', item['art'].get('tvshow.fanart','')) - for key, value in item['streamdetails'].iteritems(): - for stream in value: - li.addStreamInfo(key, stream) - - return li - -##### GET NEXTUP EPISODES FOR TAGNAME ##### -def getNextUpEpisodes(tagname, limit): - - count = 0 - # if the addon is called with nextup parameter, - # we return the nextepisodes list of the given tagname - xbmcplugin.setContent(int(sys.argv[1]), 'episodes') - # First we get a list of all the TV shows - filtered by tag - query = { - - 'jsonrpc': "2.0", - 'id': "libTvShows", - 'method': "VideoLibrary.GetTVShows", - 'params': { - - 'sort': {'order': "descending", 'method': "lastplayed"}, - 'filter': { - 'and': [ - {'operator': "true", 'field': "inprogress", 'value': ""}, - {'operator': "is", 'field': "tag", 'value': "%s" % tagname} - ]}, - 'properties': ['title', 'studio', 'mpaa', 'file', 'art'] - } - } - result = xbmc.executeJSONRPC(json.dumps(query)) - result = json.loads(result) - # If we found any, find the oldest unwatched show for each one. - try: - items = result['result']['tvshows'] - except (KeyError, TypeError): - pass - else: - for item in items: - if utils.settings('ignoreSpecialsNextEpisodes') == "true": - query = { - - 'jsonrpc': "2.0", - 'id': 1, - 'method': "VideoLibrary.GetEpisodes", - 'params': { - - 'tvshowid': item['tvshowid'], - 'sort': {'method': "episode"}, - 'filter': { - 'and': [ - {'operator': "lessthan", 'field': "playcount", 'value': "1"}, - {'operator': "greaterthan", 'field': "season", 'value': "0"} - ]}, - 'properties': [ - "title", "playcount", "season", "episode", "showtitle", - "plot", "file", "rating", "resume", "tvshowid", "art", - "streamdetails", "firstaired", "runtime", "writer", - "dateadded", "lastplayed" - ], - 'limits': {"end": 1} - } - } - else: - query = { - - 'jsonrpc': "2.0", - 'id': 1, - 'method': "VideoLibrary.GetEpisodes", - 'params': { - - 'tvshowid': item['tvshowid'], - 'sort': {'method': "episode"}, - 'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"}, - 'properties': [ - "title", "playcount", "season", "episode", "showtitle", - "plot", "file", "rating", "resume", "tvshowid", "art", - "streamdetails", "firstaired", "runtime", "writer", - "dateadded", "lastplayed" - ], - 'limits': {"end": 1} - } - } - - result = xbmc.executeJSONRPC(json.dumps(query)) - result = json.loads(result) - try: - episodes = result['result']['episodes'] - except (KeyError, TypeError): - pass - else: - for episode in episodes: - li = createListItem(episode) - xbmcplugin.addDirectoryItem( - handle=int(sys.argv[1]), - url=episode['file'], - listitem=li) - count += 1 - - if count == limit: - break - - xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) - -##### GET INPROGRESS EPISODES FOR TAGNAME ##### -def getInProgressEpisodes(tagname, limit): - - count = 0 - # if the addon is called with inprogressepisodes parameter, - # we return the inprogressepisodes list of the given tagname - xbmcplugin.setContent(int(sys.argv[1]), 'episodes') - # First we get a list of all the in-progress TV shows - filtered by tag - query = { - - 'jsonrpc': "2.0", - 'id': "libTvShows", - 'method': "VideoLibrary.GetTVShows", - 'params': { - - 'sort': {'order': "descending", 'method': "lastplayed"}, - 'filter': { - 'and': [ - {'operator': "true", 'field': "inprogress", 'value': ""}, - {'operator': "is", 'field': "tag", 'value': "%s" % tagname} - ]}, - 'properties': ['title', 'studio', 'mpaa', 'file', 'art'] - } - } - result = xbmc.executeJSONRPC(json.dumps(query)) - result = json.loads(result) - # If we found any, find the oldest unwatched show for each one. - try: - items = result['result']['tvshows'] - except (KeyError, TypeError): - pass - else: - for item in items: - query = { - - 'jsonrpc': "2.0", - 'id': 1, - 'method': "VideoLibrary.GetEpisodes", - 'params': { - - 'tvshowid': item['tvshowid'], - 'sort': {'method': "episode"}, - 'filter': {'operator': "true", 'field': "inprogress", 'value': ""}, - 'properties': [ - "title", "playcount", "season", "episode", "showtitle", "plot", - "file", "rating", "resume", "tvshowid", "art", "cast", - "streamdetails", "firstaired", "runtime", "writer", - "dateadded", "lastplayed" - ] - } - } - result = xbmc.executeJSONRPC(json.dumps(query)) - result = json.loads(result) - try: - episodes = result['result']['episodes'] - except (KeyError, TypeError): - pass - else: - for episode in episodes: - li = createListItem(episode) - xbmcplugin.addDirectoryItem( - handle=int(sys.argv[1]), - url=episode['file'], - listitem=li) - count += 1 - - if count == limit: - break - - xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) - -##### GET RECENT EPISODES FOR TAGNAME ##### -def getRecentEpisodes(tagname, limit): - - count = 0 - # if the addon is called with recentepisodes parameter, - # we return the recentepisodes list of the given tagname - xbmcplugin.setContent(int(sys.argv[1]), 'episodes') - # First we get a list of all the TV shows - filtered by tag - query = { - - 'jsonrpc': "2.0", - 'id': "libTvShows", - 'method': "VideoLibrary.GetTVShows", - 'params': { - - 'sort': {'order': "descending", 'method': "dateadded"}, - 'filter': {'operator': "is", 'field': "tag", 'value': "%s" % tagname}, - 'properties': ["title","sorttitle"] - } - } - result = xbmc.executeJSONRPC(json.dumps(query)) - result = json.loads(result) - # If we found any, find the oldest unwatched show for each one. - try: - items = result['result']['tvshows'] - except (KeyError, TypeError): - pass - else: - allshowsIds = set() - for item in items: - allshowsIds.add(item['tvshowid']) - - query = { - - 'jsonrpc': "2.0", - 'id': 1, - 'method': "VideoLibrary.GetEpisodes", - 'params': { - - 'sort': {'order': "descending", 'method': "dateadded"}, - 'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"}, - 'properties': [ - "title", "playcount", "season", "episode", "showtitle", "plot", - "file", "rating", "resume", "tvshowid", "art", "streamdetails", - "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed" - ], - "limits": {"end": limit} - } - } - result = xbmc.executeJSONRPC(json.dumps(query)) - result = json.loads(result) - try: - episodes = result['result']['episodes'] - except (KeyError, TypeError): - pass - else: - for episode in episodes: - if episode['tvshowid'] in allshowsIds: - li = createListItem(episode) - xbmcplugin.addDirectoryItem( - handle=int(sys.argv[1]), - url=episode['file'], - listitem=li) - count += 1 - - if count == limit: - break - - xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) - -##### GET VIDEO EXTRAS FOR LISTITEM ##### -def getVideoFiles(embyId,embyPath): - #returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc. - emby = embyserver.Read_EmbyServer() - if not embyId: - if "plugin.video.emby" in embyPath: - embyId = embyPath.split("/")[-2] - if embyId: - item = emby.getItem(embyId) - putils = playutils.PlayUtils(item) - if putils.isDirectPlay(): - #only proceed if we can access the files directly. TODO: copy local on the fly if accessed outside - filelocation = putils.directPlay() - if not filelocation.endswith("/"): - filelocation = filelocation.rpartition("/")[0] - dirs, files = xbmcvfs.listdir(filelocation) - for file in files: - file = filelocation + file - li = xbmcgui.ListItem(file, path=file) - xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=file, listitem=li) - for dir in dirs: - dir = filelocation + dir - li = xbmcgui.ListItem(dir, path=dir) - xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=dir, listitem=li, isFolder=True) - xbmcplugin.endOfDirectory(int(sys.argv[1])) - -##### GET EXTRAFANART FOR LISTITEM ##### -def getExtraFanArt(embyId,embyPath): - - emby = embyserver.Read_EmbyServer() - art = artwork.Artwork() - - # Get extrafanart for listitem - # will be called by skinhelper script to get the extrafanart - try: - # for tvshows we get the embyid just from the path - if not embyId: - if "plugin.video.emby" in embyPath: - embyId = embyPath.split("/")[-2] - - if embyId: - #only proceed if we actually have a emby id - utils.logMsg("EMBY", "Requesting extrafanart for Id: %s" % embyId, 0) - - # We need to store the images locally for this to work - # because of the caching system in xbmc - fanartDir = xbmc.translatePath("special://thumbnails/emby/%s/" % embyId).decode('utf-8') - - if not xbmcvfs.exists(fanartDir): - # Download the images to the cache directory - xbmcvfs.mkdirs(fanartDir) - item = emby.getItem(embyId) - if item: - backdrops = art.getAllArtwork(item)['Backdrop'] - tags = item['BackdropImageTags'] - count = 0 - for backdrop in backdrops: - # Same ordering as in artwork - tag = tags[count] - if os.path.supports_unicode_filenames: - fanartFile = os.path.join(fanartDir, "fanart%s.jpg" % tag) - else: - fanartFile = os.path.join(fanartDir.encode("utf-8"), "fanart%s.jpg" % tag.encode("utf-8")) - li = xbmcgui.ListItem(tag, path=fanartFile) - xbmcplugin.addDirectoryItem( - handle=int(sys.argv[1]), - url=fanartFile, - listitem=li) - xbmcvfs.copy(backdrop, fanartFile) - count += 1 - else: - utils.logMsg("EMBY", "Found cached backdrop.", 2) - # Use existing cached images - dirs, files = xbmcvfs.listdir(fanartDir) - for file in files: - fanartFile = os.path.join(fanartDir, file.decode('utf-8')) - li = xbmcgui.ListItem(file, path=fanartFile) - xbmcplugin.addDirectoryItem( - handle=int(sys.argv[1]), - url=fanartFile, - listitem=li) - except Exception as e: - utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 0) - - # Always do endofdirectory to prevent errors in the logs +# -*- coding: utf-8 -*- + +################################################################################################# + +import json +import os +import sys +import urlparse + +import xbmc +import xbmcaddon +import xbmcgui +import xbmcvfs +import xbmcplugin + +import artwork +import utils +import clientinfo +import downloadutils +import librarysync +import read_embyserver as embyserver +import embydb_functions as embydb +import playlist +import playbackutils as pbutils +import playutils +import api + + +################################################################################################# + + +def doPlayback(itemid, dbid): + + emby = embyserver.Read_EmbyServer() + item = emby.getItem(itemid) + pbutils.PlaybackUtils(item).play(itemid, dbid) + +##### DO RESET AUTH ##### +def resetAuth(): + # User tried login and failed too many times + resp = xbmcgui.Dialog().yesno( + heading="Warning", + line1=( + "Emby might lock your account if you fail to log in too many times. " + "Proceed anyway?")) + if resp == 1: + utils.logMsg("EMBY", "Reset login attempts.", 1) + utils.window('emby_serverStatus', value="Auth") + else: + xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)') + +def addDirectoryItem(label, path, folder=True): + li = xbmcgui.ListItem(label, path=path) + li.setThumbnailImage("special://home/addons/plugin.video.emby/icon.png") + li.setArt({"fanart":"special://home/addons/plugin.video.emby/fanart.jpg"}) + li.setArt({"landscape":"special://home/addons/plugin.video.emby/fanart.jpg"}) + xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=folder) + +def doMainListing(): + xbmcplugin.setContent(int(sys.argv[1]), 'files') + # Get emby nodes from the window props + embyprops = utils.window('Emby.nodes.total') + if embyprops: + totalnodes = int(embyprops) + for i in range(totalnodes): + path = utils.window('Emby.nodes.%s.index' % i) + if not path: + path = utils.window('Emby.nodes.%s.content' % i) + label = utils.window('Emby.nodes.%s.title' % i) + node_type = utils.window('Emby.nodes.%s.type' % i) + #because we do not use seperate entrypoints for each content type, we need to figure out which items to show in each listing. + #for now we just only show picture nodes in the picture library video nodes in the video library and all nodes in any other window + if path and xbmc.getCondVisibility("Window.IsActive(Pictures)") and node_type == "photos": + addDirectoryItem(label, path) + elif path and xbmc.getCondVisibility("Window.IsActive(VideoLibrary)") and node_type != "photos": + addDirectoryItem(label, path) + elif path and not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) | Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"): + addDirectoryItem(label, path) + + #experimental live tv nodes + addDirectoryItem("Live Tv Channels (experimental)", "plugin://plugin.video.emby/?mode=browsecontent&type=tvchannels&folderid=root") + addDirectoryItem("Live Tv Recordings (experimental)", "plugin://plugin.video.emby/?mode=browsecontent&type=recordings&folderid=root") + + # some extra entries for settings and stuff. TODO --> localize the labels + addDirectoryItem("Network credentials", "plugin://plugin.video.emby/?mode=passwords") + addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings") + addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser") + addDirectoryItem("Refresh Emby playlists/nodes", "plugin://plugin.video.emby/?mode=refreshplaylist") + addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync") + addDirectoryItem("Repair local database (force update all content)", "plugin://plugin.video.emby/?mode=repair") + addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset") + addDirectoryItem("Cache all images to Kodi texture cache", "plugin://plugin.video.emby/?mode=texturecache") + addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia") + + xbmcplugin.endOfDirectory(int(sys.argv[1])) + + +##### Generate a new deviceId +def resetDeviceId(): + + dialog = xbmcgui.Dialog() + language = utils.language + + deviceId_old = utils.window('emby_deviceId') + try: + utils.window('emby_deviceId', clear=True) + deviceId = clientinfo.ClientInfo().getDeviceId(reset=True) + except Exception as e: + utils.logMsg("EMBY", "Failed to generate a new device Id: %s" % e, 1) + dialog.ok( + heading="Emby for Kodi", + line1=language(33032)) + else: + utils.logMsg("EMBY", "Successfully removed old deviceId: %s New deviceId: %s" + % (deviceId_old, deviceId), 1) + dialog.ok( + heading="Emby for Kodi", + line1=language(33033)) + xbmc.executebuiltin('RestartApp') + +##### Delete Item +def deleteItem(): + + # Serves as a keymap action + if xbmc.getInfoLabel('ListItem.Property(embyid)'): # If we already have the embyid + embyid = xbmc.getInfoLabel('ListItem.Property(embyid)') + else: + dbid = xbmc.getInfoLabel('ListItem.DBID') + itemtype = xbmc.getInfoLabel('ListItem.DBTYPE') + + if not itemtype: + + if xbmc.getCondVisibility('Container.Content(albums)'): + itemtype = "album" + elif xbmc.getCondVisibility('Container.Content(artists)'): + itemtype = "artist" + elif xbmc.getCondVisibility('Container.Content(songs)'): + itemtype = "song" + elif xbmc.getCondVisibility('Container.Content(pictures)'): + itemtype = "picture" + else: + utils.logMsg("EMBY delete", "Unknown type, unable to proceed.", 1) + return + + embyconn = utils.kodiSQL('emby') + embycursor = embyconn.cursor() + emby_db = embydb.Embydb_Functions(embycursor) + item = emby_db.getItem_byKodiId(dbid, itemtype) + embycursor.close() + + try: + embyid = item[0] + except TypeError: + utils.logMsg("EMBY delete", "Unknown embyId, unable to proceed.", 1) + return + + if utils.settings('skipContextMenu') != "true": + resp = xbmcgui.Dialog().yesno( + heading="Confirm delete", + line1=("Delete file from Emby Server? This will " + "also delete the file(s) from disk!")) + if not resp: + utils.logMsg("EMBY delete", "User skipped deletion for: %s." % embyid, 1) + return + + doUtils = downloadutils.DownloadUtils() + url = "{server}/emby/Items/%s?format=json" % embyid + utils.logMsg("EMBY delete", "Deleting request: %s" % embyid, 0) + doUtils.downloadUrl(url, type="DELETE") + +##### ADD ADDITIONAL USERS ##### +def addUser(): + + doUtils = downloadutils.DownloadUtils() + art = artwork.Artwork() + clientInfo = clientinfo.ClientInfo() + deviceId = clientInfo.getDeviceId() + deviceName = clientInfo.getDeviceName() + userid = utils.window('emby_currUser') + dialog = xbmcgui.Dialog() + + # Get session + url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId + result = doUtils.downloadUrl(url) + + try: + sessionId = result[0]['Id'] + additionalUsers = result[0]['AdditionalUsers'] + # Add user to session + userlist = {} + users = [] + url = "{server}/emby/Users?IsDisabled=false&IsHidden=false&format=json" + result = doUtils.downloadUrl(url) + + # pull the list of users + for user in result: + name = user['Name'] + userId = user['Id'] + if userid != userId: + userlist[name] = userId + users.append(name) + + # Display dialog if there's additional users + if additionalUsers: + + option = dialog.select("Add/Remove user from the session", ["Add user", "Remove user"]) + # Users currently in the session + additionalUserlist = {} + additionalUsername = [] + # Users currently in the session + for user in additionalUsers: + name = user['UserName'] + userId = user['UserId'] + additionalUserlist[name] = userId + additionalUsername.append(name) + + if option == 1: + # User selected Remove user + resp = dialog.select("Remove user from the session", additionalUsername) + if resp > -1: + selected = additionalUsername[resp] + selected_userId = additionalUserlist[selected] + url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) + doUtils.downloadUrl(url, postBody={}, type="DELETE") + dialog.notification( + heading="Success!", + message="%s removed from viewing session" % selected, + icon="special://home/addons/plugin.video.emby/icon.png", + time=1000) + + # clear picture + position = utils.window('EmbyAdditionalUserPosition.%s' % selected_userId) + utils.window('EmbyAdditionalUserImage.%s' % position, clear=True) + return + else: + return + + elif option == 0: + # User selected Add user + for adduser in additionalUsername: + try: # Remove from selected already added users. It is possible they are hidden. + users.remove(adduser) + except: pass + + elif option < 0: + # User cancelled + return + + # Subtract any additional users + utils.logMsg("EMBY", "Displaying list of users: %s" % users) + resp = dialog.select("Add user to the session", users) + # post additional user + if resp > -1: + selected = users[resp] + selected_userId = userlist[selected] + url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) + doUtils.downloadUrl(url, postBody={}, type="POST") + dialog.notification( + heading="Success!", + message="%s added to viewing session" % selected, + icon="special://home/addons/plugin.video.emby/icon.png", + time=1000) + + except: + utils.logMsg("EMBY", "Failed to add user to session.") + dialog.notification( + heading="Error", + message="Unable to add/remove user from the session.", + icon=xbmcgui.NOTIFICATION_ERROR) + + # Add additional user images + # always clear the individual items first + totalNodes = 10 + for i in range(totalNodes): + if not utils.window('EmbyAdditionalUserImage.%s' % i): + break + utils.window('EmbyAdditionalUserImage.%s' % i, clear=True) + + url = "{server}/emby/Sessions?DeviceId=%s" % deviceId + result = doUtils.downloadUrl(url) + additionalUsers = result[0]['AdditionalUsers'] + count = 0 + for additionaluser in additionalUsers: + userid = additionaluser['UserId'] + url = "{server}/emby/Users/%s?format=json" % userid + result = doUtils.downloadUrl(url) + utils.window('EmbyAdditionalUserImage.%s' % count, + value=art.getUserArtwork(result['Id'], 'Primary')) + utils.window('EmbyAdditionalUserPosition.%s' % userid, value=str(count)) + count +=1 + +##### THEME MUSIC/VIDEOS ##### +def getThemeMedia(): + + doUtils = downloadutils.DownloadUtils() + dialog = xbmcgui.Dialog() + playback = None + + # Choose playback method + resp = dialog.select("Playback method for your themes", ["Direct Play", "Direct Stream"]) + if resp == 0: + playback = "DirectPlay" + elif resp == 1: + playback = "DirectStream" + else: + return + + library = xbmc.translatePath( + "special://profile/addon_data/plugin.video.emby/library/").decode('utf-8') + # Create library directory + if not xbmcvfs.exists(library): + xbmcvfs.mkdir(library) + + # Set custom path for user + tvtunes_path = xbmc.translatePath( + "special://profile/addon_data/script.tvtunes/").decode('utf-8') + if xbmcvfs.exists(tvtunes_path): + tvtunes = xbmcaddon.Addon(id="script.tvtunes") + tvtunes.setSetting('custom_path_enable', "true") + tvtunes.setSetting('custom_path', library) + utils.logMsg("EMBY", "TV Tunes custom path is enabled and set.", 1) + else: + # if it does not exist this will not work so warn user + # often they need to edit the settings first for it to be created. + dialog.ok( + heading="Warning", + line1=( + "The settings file does not exist in tvtunes. ", + "Go to the tvtunes addon and change a setting, then come back and re-run.")) + xbmc.executebuiltin('Addon.OpenSettings(script.tvtunes)') + return + + # Get every user view Id + embyconn = utils.kodiSQL('emby') + embycursor = embyconn.cursor() + emby_db = embydb.Embydb_Functions(embycursor) + viewids = emby_db.getViews() + embycursor.close() + + # Get Ids with Theme Videos + itemIds = {} + for view in viewids: + url = "{server}/emby/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view + result = doUtils.downloadUrl(url) + if result['TotalRecordCount'] != 0: + for item in result['Items']: + itemId = item['Id'] + folderName = item['Name'] + folderName = utils.normalize_string(folderName.encode('utf-8')) + itemIds[itemId] = folderName + + # Get paths for theme videos + for itemId in itemIds: + nfo_path = xbmc.translatePath( + "special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId]) + # Create folders for each content + if not xbmcvfs.exists(nfo_path): + xbmcvfs.mkdir(nfo_path) + # Where to put the nfos + nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") + + url = "{server}/emby/Items/%s/ThemeVideos?format=json" % itemId + result = doUtils.downloadUrl(url) + + # Create nfo and write themes to it + nfo_file = xbmcvfs.File(nfo_path, 'w') + pathstowrite = "" + # May be more than one theme + for theme in result['Items']: + putils = playutils.PlayUtils(theme) + if playback == "DirectPlay": + playurl = putils.directPlay() + else: + playurl = putils.directStream() + pathstowrite += ('%s' % playurl.encode('utf-8')) + + # Check if the item has theme songs and add them + url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId + result = doUtils.downloadUrl(url) + + # May be more than one theme + for theme in result['Items']: + putils = playutils.PlayUtils(theme) + if playback == "DirectPlay": + playurl = putils.directPlay() + else: + playurl = putils.directStream() + pathstowrite += ('%s' % playurl.encode('utf-8')) + + nfo_file.write( + '%s' % pathstowrite + ) + # Close nfo file + nfo_file.close() + + # Get Ids with Theme songs + musicitemIds = {} + for view in viewids: + url = "{server}/emby/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view + result = doUtils.downloadUrl(url) + if result['TotalRecordCount'] != 0: + for item in result['Items']: + itemId = item['Id'] + folderName = item['Name'] + folderName = utils.normalize_string(folderName.encode('utf-8')) + musicitemIds[itemId] = folderName + + # Get paths + for itemId in musicitemIds: + + # if the item was already processed with video themes back out + if itemId in itemIds: + continue + + nfo_path = xbmc.translatePath( + "special://profile/addon_data/plugin.video.emby/library/%s/" % musicitemIds[itemId]) + # Create folders for each content + if not xbmcvfs.exists(nfo_path): + xbmcvfs.mkdir(nfo_path) + # Where to put the nfos + nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") + + url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId + result = doUtils.downloadUrl(url) + + # Create nfo and write themes to it + nfo_file = xbmcvfs.File(nfo_path, 'w') + pathstowrite = "" + # May be more than one theme + for theme in result['Items']: + putils = playutils.PlayUtils(theme) + if playback == "DirectPlay": + playurl = putils.directPlay() + else: + playurl = putils.directStream() + pathstowrite += ('%s' % playurl.encode('utf-8')) + + nfo_file.write( + '%s' % pathstowrite + ) + # Close nfo file + nfo_file.close() + + dialog.notification( + heading="Emby for Kodi", + message="Themes added!", + icon="special://home/addons/plugin.video.emby/icon.png", + time=1000, + sound=False) + +##### REFRESH EMBY PLAYLISTS ##### +def refreshPlaylist(): + + lib = librarysync.LibrarySync() + dialog = xbmcgui.Dialog() + try: + # First remove playlists + utils.deletePlaylists() + # Remove video nodes + utils.deleteNodes() + # Refresh views + lib.refreshViews() + dialog.notification( + heading="Emby for Kodi", + message="Emby playlists/nodes refreshed", + icon="special://home/addons/plugin.video.emby/icon.png", + time=1000, + sound=False) + + except Exception as e: + utils.logMsg("EMBY", "Refresh playlists/nodes failed: %s" % e, 1) + dialog.notification( + heading="Emby for Kodi", + message="Emby playlists/nodes refresh failed", + icon=xbmcgui.NOTIFICATION_ERROR, + time=1000, + sound=False) + +#### SHOW SUBFOLDERS FOR NODE ##### +def GetSubFolders(nodeindex): + nodetypes = ["",".recent",".recentepisodes",".inprogress",".inprogressepisodes",".unwatched",".nextepisodes",".sets",".genres",".random",".recommended"] + for node in nodetypes: + title = utils.window('Emby.nodes.%s%s.title' %(nodeindex,node)) + if title: + path = utils.window('Emby.nodes.%s%s.content' %(nodeindex,node)) + type = utils.window('Emby.nodes.%s%s.type' %(nodeindex,node)) + addDirectoryItem(title, path) + xbmcplugin.endOfDirectory(int(sys.argv[1])) + +##### BROWSE EMBY NODES DIRECTLY ##### +def BrowseContent(viewname, type="", folderid=""): + + emby = embyserver.Read_EmbyServer() + art = artwork.Artwork() + doUtils = downloadutils.DownloadUtils() + + #folderid used as filter ? + if folderid in ["recent","recentepisodes","inprogress","inprogressepisodes","unwatched","nextepisodes","sets","genres","random","recommended"]: + filter = folderid + folderid = "" + else: + filter = "" + + xbmcplugin.setPluginCategory(int(sys.argv[1]), viewname) + #get views for root level + if not folderid: + views = emby.getViews(type) + for view in views: + if view.get("name") == viewname.decode('utf-8'): + folderid = view.get("id") + + if viewname is not None: + utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname.decode('utf-8'), type.decode('utf-8'), folderid.decode('utf-8'), filter.decode('utf-8'))) + #set the correct params for the content type + #only proceed if we have a folderid + if folderid: + if type.lower() == "homevideos": + xbmcplugin.setContent(int(sys.argv[1]), 'episodes') + itemtype = "Video,Folder,PhotoAlbum" + elif type.lower() == "photos": + xbmcplugin.setContent(int(sys.argv[1]), 'files') + itemtype = "Photo,PhotoAlbum,Folder" + else: + itemtype = "" + + #get the actual listing + if type == "recordings": + listing = emby.getTvRecordings(folderid) + elif type == "tvchannels": + listing = emby.getTvChannels() + elif filter == "recent": + listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="DateCreated", recursive=True, limit=25, sortorder="Descending") + elif filter == "random": + listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="Random", recursive=True, limit=150, sortorder="Descending") + elif filter == "recommended": + listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite") + elif filter == "sets": + listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[1], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite") + else: + listing = emby.getFilteredSection(folderid, itemtype=itemtype, recursive=False) + + #process the listing + if listing: + for item in listing.get("Items"): + li = createListItemFromEmbyItem(item,art,doUtils) + if item.get("IsFolder") == True: + #for folders we add an additional browse request, passing the folderId + path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), type.decode('utf-8'), item.get("Id").decode('utf-8')) + xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True) + else: + #playable item, set plugin path and mediastreams + xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=li.getProperty("path"), listitem=li) + + + if filter == "recent": + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) + else: + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING) + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) + + xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) + +##### CREATE LISTITEM FROM EMBY METADATA ##### +def createListItemFromEmbyItem(item,art=artwork.Artwork(),doUtils=downloadutils.DownloadUtils()): + API = api.API(item) + itemid = item['Id'] + + title = item.get('Name') + li = xbmcgui.ListItem(title) + + premieredate = item.get('PremiereDate',"") + if not premieredate: premieredate = item.get('DateCreated',"") + if premieredate: + premieredatelst = premieredate.split('T')[0].split("-") + premieredate = "%s.%s.%s" %(premieredatelst[2],premieredatelst[1],premieredatelst[0]) + + li.setProperty("embyid",itemid) + + allart = art.getAllArtwork(item) + + if item["Type"] == "Photo": + #listitem setup for pictures... + img_path = allart.get('Primary') + li.setProperty("path",img_path) + picture = doUtils.downloadUrl("{server}/Items/%s/Images" %itemid) + if picture: + picture = picture[0] + if picture.get("Width") > picture.get("Height"): + li.setArt( {"fanart": img_path}) #add image as fanart for use with skinhelper auto thumb/backgrund creation + li.setInfo('pictures', infoLabels={ "picturepath": img_path, "date": premieredate, "size": picture.get("Size"), "exif:width": str(picture.get("Width")), "exif:height": str(picture.get("Height")), "title": title}) + li.setThumbnailImage(img_path) + li.setProperty("plot",API.getOverview()) + li.setIconImage('DefaultPicture.png') + else: + #normal video items + li.setProperty('IsPlayable', 'true') + path = "%s?id=%s&mode=play" % (sys.argv[0], item.get("Id")) + li.setProperty("path",path) + genre = API.getGenres() + overlay = 0 + userdata = API.getUserData() + runtime = item.get("RunTimeTicks",0)/ 10000000.0 + seektime = userdata['Resume'] + if seektime: + li.setProperty("resumetime", str(seektime)) + li.setProperty("totaltime", str(runtime)) + + played = userdata['Played'] + if played: overlay = 7 + else: overlay = 6 + playcount = userdata['PlayCount'] + if playcount is None: + playcount = 0 + + rating = item.get('CommunityRating') + if not rating: rating = userdata['UserRating'] + + # Populate the extradata list and artwork + extradata = { + 'id': itemid, + 'rating': rating, + 'year': item.get('ProductionYear'), + 'genre': genre, + 'playcount': str(playcount), + 'title': title, + 'plot': API.getOverview(), + 'Overlay': str(overlay), + 'duration': runtime + } + if premieredate: + extradata["premieredate"] = premieredate + extradata["date"] = premieredate + li.setInfo('video', infoLabels=extradata) + if allart.get('Primary'): + li.setThumbnailImage(allart.get('Primary')) + else: li.setThumbnailImage('DefaultTVShows.png') + li.setIconImage('DefaultTVShows.png') + if not allart.get('Background'): #add image as fanart for use with skinhelper auto thumb/backgrund creation + li.setArt( {"fanart": allart.get('Primary') } ) + else: + pbutils.PlaybackUtils(item).setArtwork(li) + + mediastreams = API.getMediaStreams() + videostreamFound = False + if mediastreams: + for key, value in mediastreams.iteritems(): + if key == "video" and value: videostreamFound = True + if value: li.addStreamInfo(key, value[0]) + if not videostreamFound: + #just set empty streamdetails to prevent errors in the logs + li.addStreamInfo("video", {'duration': runtime}) + + return li + +##### BROWSE EMBY CHANNELS ##### +def BrowseChannels(itemid, folderid=None): + + _addon_id = int(sys.argv[1]) + _addon_url = sys.argv[0] + doUtils = downloadutils.DownloadUtils() + art = artwork.Artwork() + + xbmcplugin.setContent(int(sys.argv[1]), 'files') + if folderid: + url = ( + "{server}/emby/Channels/%s/Items?userid={UserId}&folderid=%s&format=json" + % (itemid, folderid)) + elif itemid == "0": + # id 0 is the root channels folder + url = "{server}/emby/Channels?{UserId}&format=json" + else: + url = "{server}/emby/Channels/%s/Items?UserId={UserId}&format=json" % itemid + + result = doUtils.downloadUrl(url) + if result and result.get("Items"): + for item in result.get("Items"): + itemid = item['Id'] + itemtype = item['Type'] + li = createListItemFromEmbyItem(item,art,doUtils) + + isFolder = item.get('IsFolder', False) + + channelId = item.get('ChannelId', "") + channelName = item.get('ChannelName', "") + if itemtype == "Channel": + path = "%s?id=%s&mode=channels" % (_addon_url, itemid) + xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li, isFolder=True) + elif isFolder: + path = "%s?id=%s&mode=channelsfolder&folderid=%s" % (_addon_url, channelId, itemid) + xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li, isFolder=True) + else: + path = "%s?id=%s&mode=play" % (_addon_url, itemid) + li.setProperty('IsPlayable', 'true') + xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li) + + xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) + +##### LISTITEM SETUP FOR VIDEONODES ##### +def createListItem(item): + + title = item['title'] + li = xbmcgui.ListItem(title) + li.setProperty('IsPlayable', "true") + + metadata = { + + 'Title': title, + 'duration': str(item['runtime']/60), + 'Plot': item['plot'], + 'Playcount': item['playcount'] + } + + if "episode" in item: + episode = item['episode'] + metadata['Episode'] = episode + + if "season" in item: + season = item['season'] + metadata['Season'] = season + + if season and episode: + li.setProperty('episodeno', "s%.2de%.2d" % (season, episode)) + + if "firstaired" in item: + metadata['Premiered'] = item['firstaired'] + + if "showtitle" in item: + metadata['TVshowTitle'] = item['showtitle'] + + if "rating" in item: + metadata['Rating'] = str(round(float(item['rating']),1)) + + if "director" in item: + metadata['Director'] = " / ".join(item['director']) + + if "writer" in item: + metadata['Writer'] = " / ".join(item['writer']) + + if "cast" in item: + cast = [] + castandrole = [] + for person in item['cast']: + name = person['name'] + cast.append(name) + castandrole.append((name, person['role'])) + metadata['Cast'] = cast + metadata['CastAndRole'] = castandrole + + li.setInfo(type="Video", infoLabels=metadata) + li.setProperty('resumetime', str(item['resume']['position'])) + li.setProperty('totaltime', str(item['resume']['total'])) + li.setArt(item['art']) + li.setThumbnailImage(item['art'].get('thumb','')) + li.setIconImage('DefaultTVShows.png') + li.setProperty('dbid', str(item['episodeid'])) + li.setProperty('fanart_image', item['art'].get('tvshow.fanart','')) + for key, value in item['streamdetails'].iteritems(): + for stream in value: + li.addStreamInfo(key, stream) + + return li + +##### GET NEXTUP EPISODES FOR TAGNAME ##### +def getNextUpEpisodes(tagname, limit): + + count = 0 + # if the addon is called with nextup parameter, + # we return the nextepisodes list of the given tagname + xbmcplugin.setContent(int(sys.argv[1]), 'episodes') + # First we get a list of all the TV shows - filtered by tag + query = { + + 'jsonrpc': "2.0", + 'id': "libTvShows", + 'method': "VideoLibrary.GetTVShows", + 'params': { + + 'sort': {'order': "descending", 'method': "lastplayed"}, + 'filter': { + 'and': [ + {'operator': "true", 'field': "inprogress", 'value': ""}, + {'operator': "is", 'field': "tag", 'value': "%s" % tagname} + ]}, + 'properties': ['title', 'studio', 'mpaa', 'file', 'art'] + } + } + result = xbmc.executeJSONRPC(json.dumps(query)) + result = json.loads(result) + # If we found any, find the oldest unwatched show for each one. + try: + items = result['result']['tvshows'] + except (KeyError, TypeError): + pass + else: + for item in items: + if utils.settings('ignoreSpecialsNextEpisodes') == "true": + query = { + + 'jsonrpc': "2.0", + 'id': 1, + 'method': "VideoLibrary.GetEpisodes", + 'params': { + + 'tvshowid': item['tvshowid'], + 'sort': {'method': "episode"}, + 'filter': { + 'and': [ + {'operator': "lessthan", 'field': "playcount", 'value': "1"}, + {'operator': "greaterthan", 'field': "season", 'value': "0"} + ]}, + 'properties': [ + "title", "playcount", "season", "episode", "showtitle", + "plot", "file", "rating", "resume", "tvshowid", "art", + "streamdetails", "firstaired", "runtime", "writer", + "dateadded", "lastplayed" + ], + 'limits': {"end": 1} + } + } + else: + query = { + + 'jsonrpc': "2.0", + 'id': 1, + 'method': "VideoLibrary.GetEpisodes", + 'params': { + + 'tvshowid': item['tvshowid'], + 'sort': {'method': "episode"}, + 'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"}, + 'properties': [ + "title", "playcount", "season", "episode", "showtitle", + "plot", "file", "rating", "resume", "tvshowid", "art", + "streamdetails", "firstaired", "runtime", "writer", + "dateadded", "lastplayed" + ], + 'limits': {"end": 1} + } + } + + result = xbmc.executeJSONRPC(json.dumps(query)) + result = json.loads(result) + try: + episodes = result['result']['episodes'] + except (KeyError, TypeError): + pass + else: + for episode in episodes: + li = createListItem(episode) + xbmcplugin.addDirectoryItem( + handle=int(sys.argv[1]), + url=episode['file'], + listitem=li) + count += 1 + + if count == limit: + break + + xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) + +##### GET INPROGRESS EPISODES FOR TAGNAME ##### +def getInProgressEpisodes(tagname, limit): + + count = 0 + # if the addon is called with inprogressepisodes parameter, + # we return the inprogressepisodes list of the given tagname + xbmcplugin.setContent(int(sys.argv[1]), 'episodes') + # First we get a list of all the in-progress TV shows - filtered by tag + query = { + + 'jsonrpc': "2.0", + 'id': "libTvShows", + 'method': "VideoLibrary.GetTVShows", + 'params': { + + 'sort': {'order': "descending", 'method': "lastplayed"}, + 'filter': { + 'and': [ + {'operator': "true", 'field': "inprogress", 'value': ""}, + {'operator': "is", 'field': "tag", 'value': "%s" % tagname} + ]}, + 'properties': ['title', 'studio', 'mpaa', 'file', 'art'] + } + } + result = xbmc.executeJSONRPC(json.dumps(query)) + result = json.loads(result) + # If we found any, find the oldest unwatched show for each one. + try: + items = result['result']['tvshows'] + except (KeyError, TypeError): + pass + else: + for item in items: + query = { + + 'jsonrpc': "2.0", + 'id': 1, + 'method': "VideoLibrary.GetEpisodes", + 'params': { + + 'tvshowid': item['tvshowid'], + 'sort': {'method': "episode"}, + 'filter': {'operator': "true", 'field': "inprogress", 'value': ""}, + 'properties': [ + "title", "playcount", "season", "episode", "showtitle", "plot", + "file", "rating", "resume", "tvshowid", "art", "cast", + "streamdetails", "firstaired", "runtime", "writer", + "dateadded", "lastplayed" + ] + } + } + result = xbmc.executeJSONRPC(json.dumps(query)) + result = json.loads(result) + try: + episodes = result['result']['episodes'] + except (KeyError, TypeError): + pass + else: + for episode in episodes: + li = createListItem(episode) + xbmcplugin.addDirectoryItem( + handle=int(sys.argv[1]), + url=episode['file'], + listitem=li) + count += 1 + + if count == limit: + break + + xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) + +##### GET RECENT EPISODES FOR TAGNAME ##### +def getRecentEpisodes(tagname, limit): + + count = 0 + # if the addon is called with recentepisodes parameter, + # we return the recentepisodes list of the given tagname + xbmcplugin.setContent(int(sys.argv[1]), 'episodes') + # First we get a list of all the TV shows - filtered by tag + query = { + + 'jsonrpc': "2.0", + 'id': "libTvShows", + 'method': "VideoLibrary.GetTVShows", + 'params': { + + 'sort': {'order': "descending", 'method': "dateadded"}, + 'filter': {'operator': "is", 'field': "tag", 'value': "%s" % tagname}, + 'properties': ["title","sorttitle"] + } + } + result = xbmc.executeJSONRPC(json.dumps(query)) + result = json.loads(result) + # If we found any, find the oldest unwatched show for each one. + try: + items = result['result']['tvshows'] + except (KeyError, TypeError): + pass + else: + allshowsIds = set() + for item in items: + allshowsIds.add(item['tvshowid']) + + query = { + + 'jsonrpc': "2.0", + 'id': 1, + 'method': "VideoLibrary.GetEpisodes", + 'params': { + + 'sort': {'order': "descending", 'method': "dateadded"}, + 'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"}, + 'properties': [ + "title", "playcount", "season", "episode", "showtitle", "plot", + "file", "rating", "resume", "tvshowid", "art", "streamdetails", + "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed" + ], + "limits": {"end": limit} + } + } + result = xbmc.executeJSONRPC(json.dumps(query)) + result = json.loads(result) + try: + episodes = result['result']['episodes'] + except (KeyError, TypeError): + pass + else: + for episode in episodes: + if episode['tvshowid'] in allshowsIds: + li = createListItem(episode) + xbmcplugin.addDirectoryItem( + handle=int(sys.argv[1]), + url=episode['file'], + listitem=li) + count += 1 + + if count == limit: + break + + xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) + +##### GET VIDEO EXTRAS FOR LISTITEM ##### +def getVideoFiles(embyId,embyPath): + #returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc. + emby = embyserver.Read_EmbyServer() + if not embyId: + if "plugin.video.emby" in embyPath: + embyId = embyPath.split("/")[-2] + if embyId: + item = emby.getItem(embyId) + putils = playutils.PlayUtils(item) + if putils.isDirectPlay(): + #only proceed if we can access the files directly. TODO: copy local on the fly if accessed outside + filelocation = putils.directPlay() + if not filelocation.endswith("/"): + filelocation = filelocation.rpartition("/")[0] + dirs, files = xbmcvfs.listdir(filelocation) + for file in files: + file = filelocation + file + li = xbmcgui.ListItem(file, path=file) + xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=file, listitem=li) + for dir in dirs: + dir = filelocation + dir + li = xbmcgui.ListItem(dir, path=dir) + xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=dir, listitem=li, isFolder=True) + xbmcplugin.endOfDirectory(int(sys.argv[1])) + +##### GET EXTRAFANART FOR LISTITEM ##### +def getExtraFanArt(embyId,embyPath): + + emby = embyserver.Read_EmbyServer() + art = artwork.Artwork() + + # Get extrafanart for listitem + # will be called by skinhelper script to get the extrafanart + try: + # for tvshows we get the embyid just from the path + if not embyId: + if "plugin.video.emby" in embyPath: + embyId = embyPath.split("/")[-2] + + if embyId: + #only proceed if we actually have a emby id + utils.logMsg("EMBY", "Requesting extrafanart for Id: %s" % embyId, 0) + + # We need to store the images locally for this to work + # because of the caching system in xbmc + fanartDir = xbmc.translatePath("special://thumbnails/emby/%s/" % embyId).decode('utf-8') + + if not xbmcvfs.exists(fanartDir): + # Download the images to the cache directory + xbmcvfs.mkdirs(fanartDir) + item = emby.getItem(embyId) + if item: + backdrops = art.getAllArtwork(item)['Backdrop'] + tags = item['BackdropImageTags'] + count = 0 + for backdrop in backdrops: + # Same ordering as in artwork + tag = tags[count] + if os.path.supports_unicode_filenames: + fanartFile = os.path.join(fanartDir, "fanart%s.jpg" % tag) + else: + fanartFile = os.path.join(fanartDir.encode("utf-8"), "fanart%s.jpg" % tag.encode("utf-8")) + li = xbmcgui.ListItem(tag, path=fanartFile) + xbmcplugin.addDirectoryItem( + handle=int(sys.argv[1]), + url=fanartFile, + listitem=li) + xbmcvfs.copy(backdrop, fanartFile) + count += 1 + else: + utils.logMsg("EMBY", "Found cached backdrop.", 2) + # Use existing cached images + dirs, files = xbmcvfs.listdir(fanartDir) + for file in files: + fanartFile = os.path.join(fanartDir, file.decode('utf-8')) + li = xbmcgui.ListItem(file, path=fanartFile) + xbmcplugin.addDirectoryItem( + handle=int(sys.argv[1]), + url=fanartFile, + listitem=li) + except Exception as e: + utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 0) + + # Always do endofdirectory to prevent errors in the logs xbmcplugin.endOfDirectory(int(sys.argv[1])) \ No newline at end of file