Merge remote-tracking branch 'origin/master' into clean-up

This commit is contained in:
angelblue05 2015-06-27 23:37:40 -05:00
commit dc3dbb202a
6 changed files with 193 additions and 61 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.emby" <addon id="plugin.video.emby"
name="Emby" name="Emby"
version="1.0.08" version="1.0.15"
provider-name="Emby.media"> provider-name="Emby.media">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>

View file

@ -144,6 +144,20 @@ def getThemeMedia():
playback = "DirectStream" playback = "DirectStream"
else:return else:return
# Set custom path for user
tvtunes_path = xbmc.translatePath("special://profile/addon_data/script.tvtunes/")
if xbmcvfs.exists(tvtunes_path):
tvtunes = xbmcaddon.Addon(id="script.tvtunes")
tvtunes.setSetting('custom_path_enable', "true")
tvtunes.setSetting('custom_path', library)
xbmc.log("TV Tunes custom path is enabled and set.")
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 = xbmcgui.Dialog()
dialog.ok('Warning', 'The settings file does not exist in tvtunes. Go to the tvtunes addon and change a setting, then come back and re-run')
return
# Create library directory # Create library directory
if not xbmcvfs.exists(library): if not xbmcvfs.exists(library):
xbmcvfs.mkdir(library) xbmcvfs.mkdir(library)
@ -157,18 +171,20 @@ def getThemeMedia():
userviewId = view[u'Id'] userviewId = view[u'Id']
userViews.append(userviewId) userViews.append(userviewId)
# Get Ids with Theme songs
# Get Ids with Theme Videos
itemIds = {} itemIds = {}
for view in userViews: for view in userViews:
url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view
result = doUtils.downloadUrl(url) result = doUtils.downloadUrl(url)
if result[u'TotalRecordCount'] != 0: if result[u'TotalRecordCount'] != 0:
for item in result[u'Items']: for item in result[u'Items']:
itemId = item[u'Id'] itemId = item[u'Id']
folderName = item[u'Name'] folderName = item[u'Name']
folderName = utils.normalize_string(folderName.encode('utf-8'))
itemIds[itemId] = folderName itemIds[itemId] = folderName
# Get paths # Get paths for theme videos
for itemId in itemIds: for itemId in itemIds:
nfo_path = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId]) nfo_path = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId])
# Create folders for each content # Create folders for each content
@ -177,6 +193,64 @@ def getThemeMedia():
# Where to put the nfos # Where to put the nfos
nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo")
url = "{server}/mediabrowser/Items/%s/ThemeVideos?format=json" % itemId
result = doUtils.downloadUrl(url)
# Create nfo and write themes to it
nfo_file = open(nfo_path, 'w')
pathstowrite = ""
# May be more than one theme
for theme in result[u'Items']:
if playback == "DirectPlay":
playurl = playUtils.directPlay(theme)
else:
playurl = playUtils.directStream(result, server, theme[u'Id'])
pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
# Check if the item has theme songs and add them
url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
result = doUtils.downloadUrl(url)
# May be more than one theme
for theme in result[u'Items']:
if playback == "DirectPlay":
playurl = playUtils.directPlay(theme)
else:
playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
nfo_file.write(
'<tvtunes>%s</tvtunes>' % pathstowrite
)
# Close nfo file
nfo_file.close()
# Get Ids with Theme songs
musicitemIds = {}
for view in userViews:
url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view
result = doUtils.downloadUrl(url)
if result[u'TotalRecordCount'] != 0:
for item in result[u'Items']:
itemId = item[u'Id']
folderName = item[u'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}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
result = doUtils.downloadUrl(url) result = doUtils.downloadUrl(url)
@ -189,7 +263,7 @@ def getThemeMedia():
playurl = playUtils.directPlay(theme) playurl = playUtils.directPlay(theme)
else: else:
playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio") playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
pathstowrite += ('<file>%s</file>' % playurl) pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
nfo_file.write( nfo_file.write(
'<tvtunes>%s</tvtunes>' % pathstowrite '<tvtunes>%s</tvtunes>' % pathstowrite
@ -576,12 +650,12 @@ def doMainListing():
addDirectoryItem(label, path) addDirectoryItem(label, path)
# some extra entries for settings and stuff. TODO --> localize the labels # some extra entries for settings and stuff. TODO --> localize the labels
addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings") addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings", False)
addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync") addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync", False)
addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser") addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser", False)
addDirectoryItem("Configure user preferences", "plugin://plugin.video.emby/?mode=userprefs") addDirectoryItem("Configure user preferences", "plugin://plugin.video.emby/?mode=userprefs", False)
addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset") addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset", False)
addDirectoryItem("Cache all images to Kodi texture cache (advanced)", "plugin://plugin.video.emby/?mode=texturecache") addDirectoryItem("Cache all images to Kodi texture cache (advanced)", "plugin://plugin.video.emby/?mode=texturecache", False)
addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia") addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia", False)
xbmcplugin.endOfDirectory(int(sys.argv[1])) xbmcplugin.endOfDirectory(int(sys.argv[1]))

View file

@ -131,6 +131,9 @@ class LibrarySync(threading.Thread):
# set prop to show we have run for the first time # set prop to show we have run for the first time
WINDOW.setProperty("startup", "done") WINDOW.setProperty("startup", "done")
# tell any widgets to refresh because the content has changed
WINDOW.setProperty("widgetreload", datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
finally: finally:
WINDOW.setProperty("SyncDatabaseRunning", "false") WINDOW.setProperty("SyncDatabaseRunning", "false")
utils.logMsg("Sync DB", "syncDatabase Exiting", 0) utils.logMsg("Sync DB", "syncDatabase Exiting", 0)
@ -596,6 +599,16 @@ class LibrarySync(threading.Thread):
#tv show doesn't exist #tv show doesn't exist
#perform full tvshow sync instead so both the show and episodes get added #perform full tvshow sync instead so both the show and episodes get added
self.TvShowsFullSync(connection,cursor,None) self.TvShowsFullSync(connection,cursor,None)
elif u"Season" in MBitem['Type']:
#get the tv show
cursor.execute("SELECT kodi_id FROM emby WHERE media_type='tvshow' AND emby_id=?", (MBitem["SeriesId"],))
result = cursor.fetchone()
if result:
kodi_show_id = result[0]
# update season
WriteKodiVideoDB().updateSeasons(MBitem["SeriesId"], kodi_show_id, connection, cursor)
#### PROCESS BOXSETS ###### #### PROCESS BOXSETS ######
elif MBitem["Type"] == "BoxSet": elif MBitem["Type"] == "BoxSet":
@ -630,9 +643,10 @@ class LibrarySync(threading.Thread):
cursor.close() cursor.close()
finally: finally:
xbmc.executebuiltin("UpdateLibrary(video)") xbmc.executebuiltin("UpdateLibrary(video)")
WINDOW.setProperty("SyncDatabaseRunning", "false") WINDOW.setProperty("SyncDatabaseRunning", "false")
# tell any widgets to refresh because the content has changed
WINDOW.setProperty("widgetreload", datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
def ShouldStop(self): def ShouldStop(self):
@ -673,4 +687,4 @@ class LibrarySync(threading.Thread):
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
break break
self.logMsg("--- Library Sync Thread stopped ---", 0) self.logMsg("--- Library Sync Thread stopped ---", 0)

View file

@ -3,11 +3,11 @@
################################################################################################# #################################################################################################
import xbmc import xbmc, xbmcaddon, xbmcvfs
import xbmcaddon
import json import json
import requests import requests
import urllib import urllib
import os
import Utils as utils import Utils as utils
@ -42,15 +42,38 @@ class TextureCache():
def FullTextureCacheSync(self): def FullTextureCacheSync(self):
#this method can be called from the plugin to sync all Kodi textures to the texture cache. #this method can be called from the plugin to sync all Kodi textures to the texture cache.
#Warning: this means that every image will be cached locally, this takes diskspace! #Warning: this means that every image will be cached locally, this takes diskspace!
#remove all existing textures first
path = "special://thumbnails/"
if xbmcvfs.exists(path):
allDirs, allFiles = xbmcvfs.listdir(path)
for dir in allDirs:
allDirs, allFiles = xbmcvfs.listdir(path+dir)
for file in allFiles:
xbmcvfs.delete(os.path.join(path+dir,file))
textureconnection = utils.KodiSQL("texture")
texturecursor = textureconnection.cursor()
texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = texturecursor.fetchall()
for row in rows:
tableName = row[0]
if(tableName != "version"):
texturecursor.execute("DELETE FROM " + tableName)
textureconnection.commit()
texturecursor.close()
#cache all entries in video DB
connection = utils.KodiSQL("video") connection = utils.KodiSQL("video")
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT url FROM art") cursor.execute("SELECT url FROM art")
result = cursor.fetchall() result = cursor.fetchall()
for url in result: for url in result:
self.CacheTexture(url[0]) self.CacheTexture(url[0])
cursor.close() cursor.close()
#cache all entries in music DB
connection = utils.KodiSQL("music") connection = utils.KodiSQL("music")
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT url FROM art") cursor.execute("SELECT url FROM art")
@ -73,7 +96,7 @@ class TextureCache():
except: except:
#extreme short timeouts so we will have a exception, but we don't need the result so pass #extreme short timeouts so we will have a exception, but we don't need the result so pass
pass pass
def setKodiWebServerDetails(self): def setKodiWebServerDetails(self):
# Get the Kodi webserver details - used to set the texture cache # Get the Kodi webserver details - used to set the texture cache

View file

@ -54,6 +54,8 @@ def KodiSQL(type="video"):
if type == "music": if type == "music":
dbPath = getKodiMusicDBPath() dbPath = getKodiMusicDBPath()
elif type == "texture":
dbPath = xbmc.translatePath("special://database/Textures13.db")
else: else:
dbPath = getKodiVideoDBPath() dbPath = getKodiVideoDBPath()
@ -73,12 +75,7 @@ def getKodiVideoDBPath():
dbVersion = "90" dbVersion = "90"
elif kodibuild.startswith("15"): elif kodibuild.startswith("15"):
# Isengard # Isengard
if "BETA1" in kodibuild: dbVersion = "93"
# Beta 1
dbVersion = "92"
elif "BETA2" in kodibuild:
# Beta 2
dbVersion = "93"
else: else:
# Not a compatible build # Not a compatible build
xbmc.log("This Kodi version is incompatible. Current version: %s" % kodibuild) xbmc.log("This Kodi version is incompatible. Current version: %s" % kodibuild)
@ -100,17 +97,7 @@ def getKodiMusicDBPath():
dbPath = xbmc.translatePath("special://profile/Database/MyMusic" + dbVersion + ".db") dbPath = xbmc.translatePath("special://profile/Database/MyMusic" + dbVersion + ".db")
return dbPath return dbPath
def checkAuthentication():
#check authentication
if addonSettings.getSetting('username') != "" and addonSettings.getSetting('ipaddress') != "":
try:
downloadUtils.authenticate()
except Exception, e:
logMsg("Emby authentication failed",e)
pass
def prettifyXml(elem): def prettifyXml(elem):
rough_string = etree.tostring(elem, "utf-8") rough_string = etree.tostring(elem, "utf-8")
@ -174,6 +161,25 @@ def CleanName(filename):
validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits) validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore') cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
return ''.join(c for c in cleanedFilename if c in validFilenameChars) return ''.join(c for c in cleanedFilename if c in validFilenameChars)
def normalize_string(text):
try:
text = text.replace(":", "")
text = text.replace("/", "-")
text = text.replace("\\", "-")
text = text.replace("<", "")
text = text.replace(">", "")
text = text.replace("*", "")
text = text.replace("?", "")
text = text.replace('|', "")
text = text.strip()
# Remove dots from the last character as windows can not have directories
# with dots at the end
text = text.rstrip('.')
text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
except:
pass
return text
def reset(): def reset():

View file

@ -39,18 +39,11 @@ class WriteKodiVideoDB():
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT emby_id FROM emby WHERE media_type=? AND kodi_id=?",(type,id)) cursor.execute("SELECT emby_id FROM emby WHERE media_type=? AND kodi_id=?",(type,id))
emby_id = cursor.fetchone()[0] emby_id = cursor.fetchone()
cursor.close
if(emby_id != None): if(emby_id != None):
emby_id = emby_id[0]
# Erase resume point when user marks watched/unwatched to follow Emby behavior # Erase resume point when user marks watched/unwatched to follow Emby behavior
# Also force sets the playcount to instantly reflect the appropriate playstate.
if type == "episode":
resume = '{"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": {"episodeid": %d, "playcount": %d, "resume": {"position": 0}}, "id": "setResumePoint"}' % (id, playcount)
elif type == "movie":
resume = '{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": %d, "playcount": %d, "resume": {"position": 0}}, "id": "setResumePoint"}' % (id, playcount)
xbmc.executeJSONRPC(resume)
addon = xbmcaddon.Addon(id='plugin.video.emby') addon = xbmcaddon.Addon(id='plugin.video.emby')
downloadUtils = DownloadUtils() downloadUtils = DownloadUtils()
watchedurl = "{server}/mediabrowser/Users/{UserId}/PlayedItems/%s" % emby_id watchedurl = "{server}/mediabrowser/Users/{UserId}/PlayedItems/%s" % emby_id
@ -58,6 +51,9 @@ class WriteKodiVideoDB():
downloadUtils.downloadUrl(watchedurl, type="POST") downloadUtils.downloadUrl(watchedurl, type="POST")
else: else:
downloadUtils.downloadUrl(watchedurl, type="DELETE") downloadUtils.downloadUrl(watchedurl, type="DELETE")
self.setKodiResumePoint(id, 0, 0, cursor)
cursor.close
def addOrUpdateMovieToKodiLibrary( self, embyId ,connection, cursor, viewTag): def addOrUpdateMovieToKodiLibrary( self, embyId ,connection, cursor, viewTag):
@ -123,6 +119,13 @@ class WriteKodiVideoDB():
if(jsonData != ""): if(jsonData != ""):
trailerItem = jsonData trailerItem = jsonData
trailerUrl = "plugin://plugin.video.emby/trailer/?id=%s&mode=play" % trailerItem[0][u'Id'] trailerUrl = "plugin://plugin.video.emby/trailer/?id=%s&mode=play" % trailerItem[0][u'Id']
elif MBitem.get("RemoteTrailers"):
try:
trailerUrl = MBitem.get("RemoteTrailers")[0].get("Url")
trailerId = trailerUrl.split('=')[1]
trailerUrl = "plugin://plugin.video.youtube/play/?video_id=%s" % trailerId
except:
trailerUrl = MBitem.get("RemoteTrailers")[0].get("Url")
if MBitem.get("DateCreated") != None: if MBitem.get("DateCreated") != None:
dateadded = MBitem["DateCreated"].split('.')[0].replace('T', " ") dateadded = MBitem["DateCreated"].split('.')[0].replace('T', " ")
@ -142,24 +145,20 @@ class WriteKodiVideoDB():
#### ADD OR UPDATE THE FILE AND PATH ########### #### ADD OR UPDATE THE FILE AND PATH ###########
#### NOTE THAT LASTPLAYED AND PLAYCOUNT ARE STORED AT THE FILE ENTRY #### NOTE THAT LASTPLAYED AND PLAYCOUNT ARE STORED AT THE FILE ENTRY
if addon.getSetting('useDirectPaths')=='true': if addon.getSetting('useDirectPaths') == 'true':
if PlayUtils().isDirectPlay(MBitem): playurl = PlayUtils().directPlay(MBitem)
playurl = PlayUtils().directPlay(MBitem) if playurl == False:
#use the direct file path return
if "\\" in playurl: elif "\\" in playurl:
filename = playurl.rsplit("\\",1)[-1] filename = playurl.rsplit("\\",1)[-1]
path = playurl.replace(filename,"") path = playurl.replace(filename, "")
elif "/" in playurl: elif "/" in playurl:
filename = playurl.rsplit("/",1)[-1] filename = playurl.rsplit("/",1)[-1]
path = playurl.replace(filename,"") path = playurl.replace(filename, "")
else:
#for transcoding we just use the server's streaming path because I couldn't figure out how to set the plugin path in the music DB
path = server + "/Video/%s/" %MBitem["Id"]
filename = "stream.mp4"
else: else:
path = "plugin://plugin.video.emby/movies/%s/" % MBitem["Id"] path = "plugin://plugin.video.emby/movies/%s/" % MBitem["Id"]
filename = "plugin://plugin.video.emby/movies/%s/?id=%s&mode=play" % (MBitem["Id"],MBitem["Id"]) filename = "plugin://plugin.video.emby/movies/%s/?id=%s&mode=play" % (MBitem["Id"],MBitem["Id"])
#create the path #create the path
cursor.execute("SELECT idPath as pathid FROM path WHERE strPath = ?",(path,)) cursor.execute("SELECT idPath as pathid FROM path WHERE strPath = ?",(path,))
result = cursor.fetchone() result = cursor.fetchone()
@ -757,7 +756,22 @@ class WriteKodiVideoDB():
self.addOrUpdateArt(imageUrl, seasonid, "season", "poster", cursor) self.addOrUpdateArt(imageUrl, seasonid, "season", "poster", cursor)
imageUrl = API().getArtwork(season, "Banner") imageUrl = API().getArtwork(season, "Banner")
self.addOrUpdateArt(imageUrl, seasonid, "season", "banner", cursor) self.addOrUpdateArt(imageUrl, seasonid, "season", "banner", cursor)
# Set All season poster
MBitem = ReadEmbyDB().getFullItem(embyTvShowId)
seasonNum = -1
cursor.execute("SELECT idSeason as seasonid FROM seasons WHERE idShow = ? and season = ?", (kodiTvShowId, seasonNum))
result = cursor.fetchone()
if result == None:
# Create the all-season
cursor.execute("select coalesce(max(idSeason),0) as seasonid from seasons")
seasonid = cursor.fetchone()[0]
seasonid = seasonid + 1
cursor.execute("INSERT into seasons(idSeason, idShow, season) values(?, ?, ?)", (seasonid, kodiTvShowId, seasonNum))
else:
seasonid = result[0]
imageUrl = API().getArtwork(MBitem, "Primary")
self.addOrUpdateArt(imageUrl, seasonid, "season", "poster", cursor)
def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor): def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor):
updateDone = False updateDone = False
@ -774,7 +788,8 @@ class WriteKodiVideoDB():
cursor.execute("UPDATE art set url = ? WHERE media_id = ? AND media_type = ? AND type = ?", (imageUrl, kodiId, mediaType, imageType)) cursor.execute("UPDATE art set url = ? WHERE media_id = ? AND media_type = ? AND type = ?", (imageUrl, kodiId, mediaType, imageType))
#cache fanart and poster in Kodi texture cache #cache fanart and poster in Kodi texture cache
if imageType == "fanart" or imageType == "poster": if "fanart" in imageType or "poster" in imageType:
utils.logMsg("ArtworkSync", "Adding or Updating Fanart: %s" % imageUrl)
self.textureCache.CacheTexture(imageUrl) self.textureCache.CacheTexture(imageUrl)
def setKodiResumePoint(self, fileid, resume_seconds, total_seconds, cursor): def setKodiResumePoint(self, fileid, resume_seconds, total_seconds, cursor):