This commit is contained in:
marcelveldt 2015-10-23 21:06:55 +02:00
commit 6950505341
14 changed files with 152 additions and 148 deletions

View file

@ -41,7 +41,7 @@ We're still in beta stage of development. Currently these features are working:
**Important note about MySQL database in kodi**
The addon is not (and will not be) compatible with the MySQL database replacement in Kodi. In fact, Emby takes over the point of having a MySQL database because it acts as a "man in the middle" for your entire media library. Offcourse you can still use MySQL for your music while music is not supported by the addon currently.
The addon is not (and will not be) compatible with the MySQL database replacement in Kodi. In fact, Emby takes over the point of having a MySQL database because it acts as a "man in the middle" for your entire media library.
**Important note about user collections/nodes**

View file

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

View file

@ -1,3 +1,9 @@
version 1.1.52
- Report playback for music
- Support Emby tags for music videos
- Fix studio icon for movies
- FORCE RESET LOCAL DATABASE IN PLACE
version 1.1.50
- Ignore channels from syncing process
- Date added can now be updated

View file

@ -394,10 +394,13 @@ class API():
maxHeight = 10000
maxWidth = 10000
quality = ""
customquery = ""
if utils.settings('compressArt') == "true":
quality = "&Quality=90"
customquery = "&Quality=90"
if utils.settings('disableCoverArt') == "true":
customquery += "&EnableImageEnhancers=false"
allartworks = {
@ -413,14 +416,14 @@ class API():
# Process backdrops
backdropIndex = 0
for backdroptag in backdrops:
artwork = "%s/mediabrowser/Items/%s/Images/Backdrop/%s?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, id, backdropIndex, maxWidth, maxHeight, backdroptag, quality)
artwork = "%s/mediabrowser/Items/%s/Images/Backdrop/%s?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, id, backdropIndex, maxWidth, maxHeight, backdroptag, customquery)
allartworks['Backdrop'].append(artwork)
backdropIndex += 1
# Process the rest of the artwork
for art in artworks:
tag = artworks[art]
artwork = "%s/mediabrowser/Items/%s/Images/%s/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, id, art, maxWidth, maxHeight, tag, quality)
artwork = "%s/mediabrowser/Items/%s/Images/%s/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, id, art, maxWidth, maxHeight, tag, customquery)
allartworks[art] = artwork
# Process parent items if the main item is missing artwork
@ -436,7 +439,7 @@ class API():
backdropIndex = 0
for parentbackdroptag in parentbackdrops:
artwork = "%s/mediabrowser/Items/%s/Images/Backdrop/%s?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, backdropIndex, maxWidth, maxHeight, parentbackdroptag, quality)
artwork = "%s/mediabrowser/Items/%s/Images/Backdrop/%s?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, backdropIndex, maxWidth, maxHeight, parentbackdroptag, customquery)
allartworks['Backdrop'].append(artwork)
backdropIndex += 1
@ -450,7 +453,7 @@ class API():
if parentId:
parentTag = item['Parent%sImageTag' % parentart]
artwork = "%s/mediabrowser/Items/%s/Images/%s/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, parentart, maxWidth, maxHeight, parentTag, quality)
artwork = "%s/mediabrowser/Items/%s/Images/%s/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, parentart, maxWidth, maxHeight, parentTag, customquery)
allartworks[parentart] = artwork
# Parent album works a bit differently
@ -460,7 +463,7 @@ class API():
if parentId and item.get('AlbumPrimaryImageTag'):
parentTag = item['AlbumPrimaryImageTag']
artwork = "%s/mediabrowser/Items/%s/Images/Primary/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, maxWidth, maxHeight, parentTag, quality)
artwork = "%s/mediabrowser/Items/%s/Images/Primary/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, maxWidth, maxHeight, parentTag, customquery)
allartworks['Primary'] = artwork

View file

@ -28,9 +28,14 @@ class Kodi_Monitor( xbmc.Monitor ):
className = self.__class__.__name__
utils.logMsg("%s %s" % ("EMBY", className), msg, int(lvl))
def onDatabaseUpdated(self, database):
pass
def onScanStarted(self, library):
utils.window('kodiScan', value="true")
self.logMsg("Kodi library scan running.", 2)
def onScanFinished(self, library):
utils.window('kodiScan', clear=True)
self.logMsg("Kodi library scan finished.", 2)
#this library monitor is used to detect a watchedstate change by the user through the library
#as well as detect when a library item has been deleted to pass the delete to the Emby server
def onNotification (self, sender, method, data):
@ -38,72 +43,48 @@ class Kodi_Monitor( xbmc.Monitor ):
WINDOW = self.WINDOW
downloadUtils = DownloadUtils()
#player started playing an item -
if ("Playlist.OnAdd" in method or "Player.OnPlay" in method) and utils.settings('useDirectPaths')=='true':
if ("Playlist.OnAdd" in method or "Player.OnPlay" in method):
jsondata = json.loads(data)
if jsondata != None:
if jsondata:
if jsondata.has_key("item"):
if jsondata.get("item").has_key("id") and jsondata.get("item").has_key("type"):
id = jsondata.get("item").get("id")
type = jsondata.get("item").get("type")
embyid = ReadKodiDB().getEmbyIdByKodiId(id,type)
if embyid != None:
playurl = xbmc.Player().getPlayingFile()
WINDOW = xbmcgui.Window( 10000 )
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
if utils.settings('useDirectPaths')=='true' or (type == "song" and utils.settings('enableMusicSync') == "true"):
url = "{server}/mediabrowser/Users/{UserId}/Items/" + embyid + "?format=json&ImageTypeLimit=1"
result = downloadUtils.downloadUrl(url)
print "Here: %s" % result
userData = result['UserData']
playurl = PlayUtils().getPlayUrl(server, embyid, result)
watchedurl = 'http://' + server + '/mediabrowser/Users/'+ userid + '/PlayedItems/' + embyid
positionurl = 'http://' + server + '/mediabrowser/Users/'+ userid + '/PlayingItems/' + embyid
deleteurl = 'http://' + server + '/mediabrowser/Items/' + embyid
if type == "song":
connection = utils.KodiSQL('music')
cursor = connection.cursor()
embyid = ReadKodiDB().getEmbyIdByKodiId(id, type, connection, cursor)
cursor.close()
else:
embyid = ReadKodiDB().getEmbyIdByKodiId(id,type)
# set the current playing info
WINDOW.setProperty(playurl+"watchedurl", watchedurl)
WINDOW.setProperty(playurl+"positionurl", positionurl)
WINDOW.setProperty(playurl+"deleteurl", "")
WINDOW.setProperty(playurl+"deleteurl", deleteurl)
if result.get("Type")=="Episode":
WINDOW.setProperty(playurl+"refresh_id", result.get("SeriesId"))
else:
WINDOW.setProperty(playurl+"refresh_id", embyid)
if embyid:
url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % embyid
result = downloadUtils.downloadUrl(url)
self.logMsg("Result: %s" % result, 2)
WINDOW.setProperty(playurl+"runtimeticks", str(result.get("RunTimeTicks")))
WINDOW.setProperty(playurl+"type", result.get("Type"))
WINDOW.setProperty(playurl+"item_id", embyid)
playurl = None
count = 0
while not playurl and count < 2:
try:
playurl = xbmc.Player().getPlayingFile()
except RuntimeError:
xbmc.sleep(200)
else:
listItem = xbmcgui.ListItem()
PlaybackUtils().setProperties(playurl, result, listItem)
if PlayUtils().isDirectPlay(result) == True:
playMethod = "DirectPlay"
else:
playMethod = "Transcode"
WINDOW.setProperty(playurl+"playmethod", playMethod)
mediaSources = result.get("MediaSources")
if(mediaSources != None):
mediaStream = mediaSources[0].get('MediaStreams')
defaultsubs = ""
for stream in mediaStream:
if u'Subtitle' in stream[u'Type'] and stream[u'IsDefault']:
if u'Language' in stream:
defaultsubs = stream[u'Language']
if type == "song" and utils.settings('directstreammusic') == "true":
utils.window('%splaymethod' % playurl, value="DirectStream")
else:
defaultsubs = stream[u'Codec']
WINDOW.setProperty("%ssubs" % playurl, defaultsubs.encode('utf-8'))
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
WINDOW.setProperty(playurl+"AudioStreamIndex", str(mediaSources[0].get('DefaultAudioStreamIndex')))
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
WINDOW.setProperty(playurl+"SubtitleStreamIndex", str(mediaSources[0].get('DefaultSubtitleStreamIndex')))
utils.window('%splaymethod' % playurl, value="DirectPlay")
count += 1
if method == "VideoLibrary.OnUpdate":
# Triggers 4 times, the following is only for manually marking as watched/unwatched

View file

@ -1078,6 +1078,15 @@ class LibrarySync(threading.Thread):
# Library sync
if not startupComplete:
# Verify the database for videos
videodb = utils.getKodiVideoDBPath()
if not xbmcvfs.exists(videodb):
# Database does not exists.
self.logMsg("The current Kodi version is incompatible with the Emby for Kodi add-on. Please visit here, to see currently supported Kodi versions: https://github.com/MediaBrowser/Emby.Kodi/wiki", 0)
xbmcgui.Dialog().ok("Emby Warning", "Cancelling the database syncing process. Current Kodi version: %s is unsupported. Please verify your logs for more info." % xbmc.getInfoLabel('System.BuildVersion'))
break
# Run full sync
self.logMsg("DB Version: " + utils.settings("dbCreatedWithVersion"), 0)
self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1)
@ -1099,21 +1108,21 @@ class LibrarySync(threading.Thread):
if len(self.updateItems) > 0:
if len(self.updateItems) > 0 and utils.window('kodiScan') != "true":
# Add or update items
self.logMsg("Processing items: %s" % (str(self.updateItems)), 1)
listItems = self.updateItems
self.updateItems = []
self.IncrementalSync(listItems)
if len(self.userdataItems) > 0:
if len(self.userdataItems) > 0 and utils.window('kodiScan') != "true":
# Process userdata changes only
self.logMsg("Processing items: %s" % (str(self.userdataItems)), 1)
listItems = self.userdataItems
self.userdataItems = []
self.setUserdata(listItems)
if len(self.removeItems) > 0:
if len(self.removeItems) > 0 and utils.window('kodiScan') != "true":
# Remove item from Kodi library
self.logMsg("Removing items: %s" % self.removeItems, 1)
listItems = self.removeItems

View file

@ -20,12 +20,12 @@ class Lock:
break;
except OSError as e:
if (time.time() - start_time) >= self.timeout:
xbmc.log("File_Lock_On " + self.filename + " timed out")
xbmc.log("File_Lock_On " + self.filename.encode('utf-8') + " timed out")
return False
#xbmc.log("File_Lock_On " + self.filename + " error " + str(e))
time.sleep(self.delay)
self.is_locked = True
xbmc.log("File_Lock_On " + self.filename + " obtained")
xbmc.log("File_Lock_On " + self.filename.encode('utf-8') + " obtained")
return True
def release(self):
@ -33,7 +33,7 @@ class Lock:
os.close(self.fd)
os.unlink(self.filename)
self.is_locked = False
xbmc.log("File_Lock_On " + self.filename + " released")
xbmc.log("File_Lock_On " + self.filename.encode('utf-8') + " released")
def __del__(self):
self.release()

View file

@ -125,25 +125,35 @@ class PlaybackUtils():
if utils.settings('disableCinema') == "false" and not seekTime:
# if we have any play them when the movie/show is not being resumed
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
intros = doUtils.downloadUrl(url)
getTrailers = True
if intros['TotalRecordCount'] != 0:
for intro in intros['Items']:
# The server randomly returns intros, process them.
introId = intro['Id']
introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
introListItem = xbmcgui.ListItem()
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
if utils.settings('askCinema') == "true":
resp = xbmcgui.Dialog().yesno("Emby Cinema Mode", "Play trailers?")
if not resp:
# User selected to not play trailers
getTrailers = False
self.logMsg("Skip trailers.", 1)
# Set listitem and properties for intros
self.setProperties(introPlayurl, intro, introListItem)
self.setListItemProps(server, introId, introListItem, intro)
playlist.add(introPlayurl, introListItem, index=currentPosition)
introsPlaylist = True
currentPosition += 1
if getTrailers:
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
intros = doUtils.downloadUrl(url)
if intros['TotalRecordCount'] != 0:
for intro in intros['Items']:
# The server randomly returns intros, process them.
introId = intro['Id']
introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
introListItem = xbmcgui.ListItem()
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
# Set listitem and properties for intros
self.setProperties(introPlayurl, intro, introListItem)
self.setListItemProps(server, introId, introListItem, intro)
playlist.add(introPlayurl, introListItem, index=currentPosition)
introsPlaylist = True
currentPosition += 1
############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ###############

View file

@ -176,13 +176,14 @@ class ReadEmbyDB():
url = "{server}/mediabrowser/Users/{UserId}/Items?StartIndex=%s&Limit=%s&Fields=Etag,Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&SortBy=DateCreated&Recursive=true&IncludeItemTypes=MusicAlbum&format=json" % (index, jump)
jsondata = self.doUtils.downloadUrl(url)
tempresult = []
#tempresult = []
# Only return valid albums - which have artists
for item in jsondata['Items']:
'''for item in jsondata['Items']:
if item['AlbumArtists']:
tempresult.append(item)
result.extend(tempresult)
result.extend(tempresult)'''
result.extend(jsondata['Items'])
index += jump
return result

View file

@ -56,59 +56,44 @@ def KodiSQL(type="video"):
if type == "music":
dbPath = getKodiMusicDBPath()
elif type == "texture":
dbPath = xbmc.translatePath("special://database/Textures13.db")
dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
else:
dbPath = getKodiVideoDBPath()
connection = sqlite3.connect(dbPath)
return connection
def getKodiVideoDBPath():
kodibuild = xbmc.getInfoLabel("System.BuildVersion")
kodibuild = xbmc.getInfoLabel('System.BuildVersion')[:2]
dbVersion = {
if kodibuild.startswith("13"):
# Gotham
dbVersion = "78"
elif kodibuild.startswith("14"):
# Helix
dbVersion = "90"
elif kodibuild.startswith("15"):
# Isengard
dbVersion = "93"
elif kodibuild.startswith("16"):
# Jarvis
dbVersion = "99"
else:
# Not a compatible build
xbmc.log("This Kodi version is incompatible. Current version: %s" % kodibuild)
"13": 78, # Gotham
"14": 90, # Helix
"15": 93, # Isengard
"16": 99 # Jarvis
}
dbPath = xbmc.translatePath("special://profile/Database/MyVideos" + dbVersion + ".db")
return dbPath
dbPath = xbmc.translatePath(
"special://database/MyVideos%s.db"
% dbVersion.get(kodibuild, "")).decode('utf-8')
return dbPath
def getKodiMusicDBPath():
if xbmc.getInfoLabel("System.BuildVersion").startswith("13"):
#gotham
dbVersion = "46"
elif xbmc.getInfoLabel("System.BuildVersion").startswith("14"):
#helix
dbVersion = "48"
elif xbmc.getInfoLabel("System.BuildVersion").startswith("15"):
#isengard
dbVersion = "52"
elif xbmc.getInfoLabel("System.BuildVersion").startswith("16"):
#jarvis
dbVersion = "55"
else:
# Not a compatible build
xbmc.log("This Kodi version is incompatible. Current version: %s" % kodibuild)
dbPath = xbmc.translatePath("special://profile/Database/MyMusic" + dbVersion + ".db")
return dbPath
kodibuild = xbmc.getInfoLabel('System.BuildVersion')[:2]
dbVersion = {
"13": 46, # Gotham
"14": 48, # Helix
"15": 52, # Isengard
"16": 55 # Jarvis
}
dbPath = xbmc.translatePath(
"special://database/MyMusic%s.db"
% dbVersion.get(kodibuild, "")).decode('utf-8')
return dbPath
def prettifyXml(elem):
rough_string = etree.tostring(elem, "utf-8")

View file

@ -27,13 +27,12 @@ class WriteKodiMusicDB():
kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
addonName = ClientInformation().getAddonName()
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
directpath = utils.settings('useDirectPaths') == "true"
def __init__(self):
username = utils.window('currUser')
self.userid = utils.window('userId%s' % username)
self.server = utils.window('server%s' % username)
def logMsg(self, msg, lvl = 1):
@ -221,8 +220,16 @@ class WriteKodiMusicDB():
self.AddGenresToMedia(albumid, genres, "album", cursor)
# Update artwork
self.textureCache.addArtwork(artworks, albumid, "album", cursor)
if artworks['Primary']:
self.textureCache.addOrUpdateArt(artworks['Primary'], albumid, "album", "thumb", cursor)
artworks['Primary'] = ""
if artworks.get('BoxRear'):
self.textureCache.addOrUpdateArt(artworks['BoxRear'], albumid, "album", "poster", cursor)
artworks['BoxRear'] = ""
self.textureCache.addArtwork(artworks, albumid, "album", cursor)
# Link album to artists
if MBartists:
album_artists = MBitem['AlbumArtists']
@ -323,7 +330,7 @@ class WriteKodiMusicDB():
# Kodi Gotham and Helix
query = "INSERT INTO album(idAlbum, strArtists, strGenres, iYear, dateAdded) values(?, ?, ?, ?, ?)"
cursor.execute(query, (albumid, artists, genre, year, dateadded))
finally:
# Link album to artists
for artist in MBitem['ArtistItems']:
cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (artist['Id'],))

View file

@ -27,13 +27,14 @@ class WriteKodiVideoDB():
kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
addonName = ClientInformation().getAddonName()
WINDOW = xbmcgui.Window(10000)
def __init__(self):
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
directpath = utils.settings('useDirectPaths') == "true"
username = utils.window('currUser')
self.userid = utils.window('userId%s' % username)
self.server = utils.window('server%s' % username)
self.directpath = utils.settings('useDirectPaths') == "true"
def logMsg(self, msg, lvl = 1):

View file

@ -36,6 +36,7 @@
<setting id="smbpassword" type="text" label="30008" default="" option="hidden" visible="true" enable="true" />
<setting type="sep" />
<setting id="disableCinema" type="bool" label="Disable Emby cinema mode" default="false" visible="true" enable="true" />
<setting id="askCinema" type="bool" label="Ask to play trailers" default="false" visible="eq(-1,false)" enable="true" subsetting="true" />
<setting id="offerDelete" type="bool" label="30114" visible="true" enable="true" default="false" />
<setting id="offerDeleteTV" type="bool" label="30115" visible="eq(-1,true)" enable="true" default="false" subsetting="true" />
<setting id="offerDeleteMovies" type="bool" label="30116" visible="eq(-2,true)" enable="true" default="false" subsetting="true" />

View file

@ -73,7 +73,7 @@ class Service():
utils.window('minDBVersionCheck', clear=True)
# Set min DB version
utils.window('minDBVersion', value="1.1.40")
utils.window('minDBVersion', value="1.1.52")
embyProperty = utils.window('Emby.nodes.total')
propNames = [