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"?>
<addon id="plugin.video.emby"
name="Emby"
version="1.0.08"
version="1.0.15"
provider-name="Emby.media">
<requires>
<import addon="xbmc.python" version="2.1.0"/>

View file

@ -144,6 +144,20 @@ def getThemeMedia():
playback = "DirectStream"
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
if not xbmcvfs.exists(library):
xbmcvfs.mkdir(library)
@ -157,18 +171,20 @@ def getThemeMedia():
userviewId = view[u'Id']
userViews.append(userviewId)
# Get Ids with Theme songs
# Get Ids with Theme Videos
itemIds = {}
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)
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'))
itemIds[itemId] = folderName
# Get paths
# 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
@ -177,6 +193,64 @@ def getThemeMedia():
# Where to put the nfos
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
result = doUtils.downloadUrl(url)
@ -189,7 +263,7 @@ def getThemeMedia():
playurl = playUtils.directPlay(theme)
else:
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(
'<tvtunes>%s</tvtunes>' % pathstowrite
@ -576,12 +650,12 @@ def doMainListing():
addDirectoryItem(label, path)
# some extra entries for settings and stuff. TODO --> localize the labels
addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings")
addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync")
addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser")
addDirectoryItem("Configure user preferences", "plugin://plugin.video.emby/?mode=userprefs")
addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset")
addDirectoryItem("Cache all images to Kodi texture cache (advanced)", "plugin://plugin.video.emby/?mode=texturecache")
addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia")
addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings", False)
addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync", False)
addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser", False)
addDirectoryItem("Configure user preferences", "plugin://plugin.video.emby/?mode=userprefs", False)
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", False)
addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia", False)
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
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:
WINDOW.setProperty("SyncDatabaseRunning", "false")
utils.logMsg("Sync DB", "syncDatabase Exiting", 0)
@ -596,6 +599,16 @@ class LibrarySync(threading.Thread):
#tv show doesn't exist
#perform full tvshow sync instead so both the show and episodes get added
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 ######
elif MBitem["Type"] == "BoxSet":
@ -630,9 +643,10 @@ class LibrarySync(threading.Thread):
cursor.close()
finally:
xbmc.executebuiltin("UpdateLibrary(video)")
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):
@ -673,4 +687,4 @@ class LibrarySync(threading.Thread):
# Abort was requested while waiting. We should exit
break
self.logMsg("--- Library Sync Thread stopped ---", 0)
self.logMsg("--- Library Sync Thread stopped ---", 0)

View file

@ -3,11 +3,11 @@
#################################################################################################
import xbmc
import xbmcaddon
import xbmc, xbmcaddon, xbmcvfs
import json
import requests
import urllib
import os
import Utils as utils
@ -42,15 +42,38 @@ class TextureCache():
def FullTextureCacheSync(self):
#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!
#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")
cursor = connection.cursor()
cursor.execute("SELECT url FROM art")
result = cursor.fetchall()
for url in result:
self.CacheTexture(url[0])
cursor.close()
#cache all entries in music DB
connection = utils.KodiSQL("music")
cursor = connection.cursor()
cursor.execute("SELECT url FROM art")
@ -73,7 +96,7 @@ class TextureCache():
except:
#extreme short timeouts so we will have a exception, but we don't need the result so pass
pass
def setKodiWebServerDetails(self):
# Get the Kodi webserver details - used to set the texture cache

View file

@ -54,6 +54,8 @@ def KodiSQL(type="video"):
if type == "music":
dbPath = getKodiMusicDBPath()
elif type == "texture":
dbPath = xbmc.translatePath("special://database/Textures13.db")
else:
dbPath = getKodiVideoDBPath()
@ -73,12 +75,7 @@ def getKodiVideoDBPath():
dbVersion = "90"
elif kodibuild.startswith("15"):
# Isengard
if "BETA1" in kodibuild:
# Beta 1
dbVersion = "92"
elif "BETA2" in kodibuild:
# Beta 2
dbVersion = "93"
dbVersion = "93"
else:
# Not a compatible build
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")
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
return dbPath
def prettifyXml(elem):
rough_string = etree.tostring(elem, "utf-8")
@ -174,6 +161,25 @@ def CleanName(filename):
validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
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():

View file

@ -39,18 +39,11 @@ class WriteKodiVideoDB():
cursor = connection.cursor()
cursor.execute("SELECT emby_id FROM emby WHERE media_type=? AND kodi_id=?",(type,id))
emby_id = cursor.fetchone()[0]
cursor.close
emby_id = cursor.fetchone()
if(emby_id != None):
emby_id = emby_id[0]
# 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')
downloadUtils = DownloadUtils()
watchedurl = "{server}/mediabrowser/Users/{UserId}/PlayedItems/%s" % emby_id
@ -58,6 +51,9 @@ class WriteKodiVideoDB():
downloadUtils.downloadUrl(watchedurl, type="POST")
else:
downloadUtils.downloadUrl(watchedurl, type="DELETE")
self.setKodiResumePoint(id, 0, 0, cursor)
cursor.close
def addOrUpdateMovieToKodiLibrary( self, embyId ,connection, cursor, viewTag):
@ -123,6 +119,13 @@ class WriteKodiVideoDB():
if(jsonData != ""):
trailerItem = jsonData
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:
dateadded = MBitem["DateCreated"].split('.')[0].replace('T', " ")
@ -142,24 +145,20 @@ class WriteKodiVideoDB():
#### ADD OR UPDATE THE FILE AND PATH ###########
#### NOTE THAT LASTPLAYED AND PLAYCOUNT ARE STORED AT THE FILE ENTRY
if addon.getSetting('useDirectPaths')=='true':
if PlayUtils().isDirectPlay(MBitem):
playurl = PlayUtils().directPlay(MBitem)
#use the direct file path
if "\\" in playurl:
filename = playurl.rsplit("\\",1)[-1]
path = playurl.replace(filename,"")
elif "/" in playurl:
filename = playurl.rsplit("/",1)[-1]
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"
if addon.getSetting('useDirectPaths') == 'true':
playurl = PlayUtils().directPlay(MBitem)
if playurl == False:
return
elif "\\" in playurl:
filename = playurl.rsplit("\\",1)[-1]
path = playurl.replace(filename, "")
elif "/" in playurl:
filename = playurl.rsplit("/",1)[-1]
path = playurl.replace(filename, "")
else:
path = "plugin://plugin.video.emby/movies/%s/" % MBitem["Id"]
filename = "plugin://plugin.video.emby/movies/%s/?id=%s&mode=play" % (MBitem["Id"],MBitem["Id"])
#create the path
cursor.execute("SELECT idPath as pathid FROM path WHERE strPath = ?",(path,))
result = cursor.fetchone()
@ -757,7 +756,22 @@ class WriteKodiVideoDB():
self.addOrUpdateArt(imageUrl, seasonid, "season", "poster", cursor)
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):
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))
#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)
def setKodiResumePoint(self, fileid, resume_seconds, total_seconds, cursor):