From 8839b3b323c77fdbd2ab3e75c8247250b9b6b389 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 8 May 2015 00:04:40 +0200 Subject: [PATCH] add first support for music library sync --- resources/lib/API.py | 2 +- resources/lib/LibrarySync.py | 198 ++++++++++++- resources/lib/ReadEmbyDB.py | 76 +++++ resources/lib/ReadKodiDB.py | 21 +- resources/lib/Utils.py | 28 +- resources/lib/WriteKodiMusicDB.py | 447 ++++++++++++++++++++++++++++++ resources/settings.xml | 1 + 7 files changed, 760 insertions(+), 13 deletions(-) create mode 100644 resources/lib/WriteKodiMusicDB.py diff --git a/resources/lib/API.py b/resources/lib/API.py index 239b4b89..5c9a6e24 100644 --- a/resources/lib/API.py +++ b/resources/lib/API.py @@ -332,7 +332,7 @@ class API(): if(data.get("ImageTags") != None and data.get("ImageTags").get(type) != None): imageTag = data.get("ImageTags").get(type) - if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Logo": + if (data.get("Type") == "Episode" or data.get("Type") == "Season" or data.get("Type") == "MusicAlbum") and type=="Logo": imageTag = data.get("ParentLogoImageTag") if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Art": imageTag = data.get("ParentArtImageTag") diff --git a/resources/lib/LibrarySync.py b/resources/lib/LibrarySync.py index 9c47615d..3430b92a 100644 --- a/resources/lib/LibrarySync.py +++ b/resources/lib/LibrarySync.py @@ -22,6 +22,7 @@ from DownloadUtils import DownloadUtils from ReadEmbyDB import ReadEmbyDB from ReadKodiDB import ReadKodiDB from WriteKodiVideoDB import WriteKodiVideoDB +from WriteKodiMusicDB import WriteKodiMusicDB from VideoNodes import VideoNodes addondir = xbmc.translatePath(xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('profile')) @@ -40,6 +41,7 @@ class LibrarySync(): startupDone = WINDOW.getProperty("startup") == "done" syncInstallRunDone = addon.getSetting("SyncInstallRunDone") == "true" + performMusicSync = addon.getSetting("enableMusicSync") == "true" dbSyncIndication = addon.getSetting("dbSyncIndication") == "true" WINDOW.setProperty("SyncDatabaseRunning", "true") @@ -56,16 +58,21 @@ class LibrarySync(): try: completed = True - connection = utils.KodiSQL() + + + ### BUILD VIDEO NODES LISTING ### + VideoNodes().buildVideoNodesListing() + + ### PROCESS VIDEO LIBRARY ### + + #create the sql connection to video db + connection = utils.KodiSQL("video") cursor = connection.cursor() #Add the special emby table if not startupDone: cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER)") connection.commit() - - ### BUILD VIDEO NODES LISTING ### - VideoNodes().buildVideoNodesListing() # sync movies self.MoviesFullSync(connection,cursor,pDialog) @@ -82,6 +89,23 @@ class LibrarySync(): # sync musicvideos self.MusicVideosFullSync(connection,cursor,pDialog) + #close sql connection + cursor.close() + + ### PROCESS MUSIC LIBRARY ### + if performMusicSync: + #create the sql connection to music db + connection = utils.KodiSQL("music") + cursor = connection.cursor() + + #Add the special emby table + if not startupDone: + cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER)") + connection.commit() + + self.MusicFullSync(connection,cursor,pDialog) + cursor.close() + # set the install done setting if(syncInstallRunDone == False and completed): addon = xbmcaddon.Addon(id='plugin.video.emby') #force a new instance of the addon @@ -97,7 +121,6 @@ class LibrarySync(): WINDOW.setProperty("SyncDatabaseRunning", "false") utils.logMsg("Sync DB", "syncDatabase Exiting", 0) - cursor.close() if(pDialog != None): pDialog.close() @@ -336,6 +359,149 @@ class LibrarySync(): WINDOW.setProperty(kodiId,"deleted") WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) + def MusicFullSync(self, connection,cursor, pDialog): + + self.ProcessMusicArtists(connection,cursor,pDialog) + self.ProcessMusicAlbums(connection,cursor,pDialog) + self.ProcessMusicSongs(connection,cursor,pDialog) + + ### commit all changes to database ### + connection.commit() + + def ProcessMusicSongs(self,connection,cursor,pDialog): + + allKodiSongIds = list() + allEmbySongIds = list() + + allEmbySongs = ReadEmbyDB().getMusicSongs() + allKodiSongs = ReadKodiDB().getKodiMusicSongs(connection, cursor) + + for kodisong in allKodiSongs: + allKodiSongIds.append(kodisong[1]) + + total = len(allEmbySongs) + 1 + count = 1 + + #### PROCESS SONGS ADDS AND UPDATES ### + for item in allEmbySongs: + + if (self.ShouldStop()): + return False + + allEmbySongIds.append(item["Id"]) + + if(pDialog != None): + progressTitle = "Processing Music Songs (" + str(count) + " of " + str(total) + ")" + pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle) + count += 1 + + kodiSong = None + for kodisong in allKodiSongs: + if kodisong[1] == item["Id"]: + kodiSong = kodisong + + if kodiSong == None: + WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item["Id"],connection, cursor) + else: + if kodiSong[2] != API().getChecksum(item): + WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item["Id"],connection, cursor) + + #### PROCESS DELETES ##### + allEmbySongIds = set(allEmbySongIds) + for kodiId in allKodiSongIds: + if not kodiId in allEmbySongIds: + WINDOW.setProperty(kodiId,"deleted") + WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) + + def ProcessMusicArtists(self,connection,cursor,pDialog): + + allKodiArtistIds = list() + allEmbyArtistIds = list() + + allEmbyArtists = ReadEmbyDB().getMusicArtists() + allKodiArtists = ReadKodiDB().getKodiMusicArtists(connection, cursor) + + for kodiartist in allKodiArtists: + allKodiArtistIds.append(kodiartist[1]) + + total = len(allEmbyArtists) + 1 + count = 1 + + #### PROCESS SONGS ADDS AND UPDATES ### + for item in allEmbyArtists: + + if (self.ShouldStop()): + return False + + allEmbyArtistIds.append(item["Id"]) + + if(pDialog != None): + progressTitle = "Processing Music Artists (" + str(count) + " of " + str(total) + ")" + pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle) + count += 1 + + kodiArtist = None + for kodiartist in allKodiArtists: + if kodiartist[1] == item["Id"]: + kodiArtist = kodiartist + + if kodiArtist == None: + WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item["Id"],connection, cursor) + else: + if kodiArtist[2] != API().getChecksum(item): + WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item["Id"],connection, cursor) + + #### PROCESS DELETES ##### + allEmbyArtistIds = set(allEmbyArtistIds) + for kodiId in allKodiArtistIds: + if not kodiId in allEmbyArtistIds: + WINDOW.setProperty(kodiId,"deleted") + WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) + + def ProcessMusicAlbums(self,connection,cursor,pDialog): + + allKodiAlbumIds = list() + allEmbyAlbumIds = list() + + allEmbyAlbums = ReadEmbyDB().getMusicAlbums() + allKodiAlbums = ReadKodiDB().getKodiMusicAlbums(connection, cursor) + + for kodialbum in allKodiAlbums: + allKodiAlbumIds.append(kodialbum[1]) + + total = len(allEmbyAlbums) + 1 + count = 1 + + #### PROCESS SONGS ADDS AND UPDATES ### + for item in allEmbyAlbums: + + if (self.ShouldStop()): + return False + + allEmbyAlbumIds.append(item["Id"]) + + if(pDialog != None): + progressTitle = "Processing Music Albums (" + str(count) + " of " + str(total) + ")" + pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle) + count += 1 + + kodiAlbum = None + for kodialbum in allKodiAlbums: + if kodialbum[1] == item["Id"]: + kodiAlbum = kodialbum + + if kodiAlbum == None: + WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item["Id"],connection, cursor) + else: + if kodiAlbum[2] != API().getChecksum(item): + WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item["Id"],connection, cursor) + + #### PROCESS DELETES ##### + allEmbyAlbumIds = set(allEmbyAlbumIds) + for kodiId in allKodiAlbumIds: + if not kodiId in allEmbyAlbumIds: + WINDOW.setProperty(kodiId,"deleted") + WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) def IncrementalSync(self, itemList): #this will only perform sync for items received by the websocket @@ -349,7 +515,7 @@ class LibrarySync(): pDialog = xbmcgui.DialogProgressBG() pDialog.create('Emby for Kodi', 'Performing incremental sync...') - connection = utils.KodiSQL() + connection = utils.KodiSQL("video") cursor = connection.cursor() try: @@ -407,9 +573,25 @@ class LibrarySync(): ### commit all changes to database ### connection.commit() - - finally: cursor.close() + + ### PROCESS MUSIC LIBRARY ### + if performMusicSync: + connection = utils.KodiSQL("music") + cursor = connection.cursor() + for item in itemList: + MBitem = ReadEmbyDB().getItem(item) + if MBitem["Type"] == "MusicArtist": + WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(MBitem["Id"],connection, cursor) + if MBitem["Type"] == "MusicAlbum": + WriteKodiMusicDB().addOrUpdateAlbumToKodiLibraryToKodiLibrary(MBitem["Id"],connection, cursor) + if MBitem["Type"] == "Audio": + WriteKodiMusicDB().addOrUpdateSongToKodiLibraryToKodiLibrary(MBitem["Id"],connection, cursor) + connection.commit() + cursor.close() + + finally: + xbmc.executebuiltin("UpdateLibrary(video)") WINDOW.setProperty("SyncDatabaseRunning", "false") diff --git a/resources/lib/ReadEmbyDB.py b/resources/lib/ReadEmbyDB.py index 3d4e7680..6a98af91 100644 --- a/resources/lib/ReadEmbyDB.py +++ b/resources/lib/ReadEmbyDB.py @@ -65,7 +65,83 @@ class ReadEmbyDB(): result = newResult return result + + def getMusicArtists(self, itemList = []): + result = None + doUtils = DownloadUtils() + + #only get basic info for our sync-compares + url = "{server}/Artists?Fields=Name,CumulativeRunTimeTicks,Etag&Recursive=true&format=json" + + jsonData = doUtils.downloadUrl(url) + if (jsonData == ""): + return result + + if (jsonData[u'Items'] != ""): + result = jsonData[u'Items'] + + # Work around to only return items from the given list + if (result != None and len(result) > 0 and len(itemList) > 0): + newResult = [] + for item in result: + if (item[u'Id'] in itemList): + newResult.append(item) + result = newResult + + return result + + def getMusicSongs(self, itemList = []): + + result = None + doUtils = DownloadUtils() + + #only get basic info for our sync-compares + url = "{server}/mediabrowser/Users/{UserId}/Items?Fields=Name,CumulativeRunTimeTicks,Etag&Recursive=true&IncludeItemTypes=Audio&format=json" + + jsonData = doUtils.downloadUrl(url) + if (jsonData == ""): + return result + + if (jsonData[u'Items'] != ""): + result = jsonData[u'Items'] + + # Work around to only return items from the given list + if (result != None and len(result) > 0 and len(itemList) > 0): + newResult = [] + for item in result: + if (item[u'Id'] in itemList): + newResult.append(item) + result = newResult + + return result + + def getMusicAlbums(self, itemList = []): + + result = None + doUtils = DownloadUtils() + + #only get basic info for our sync-compares + url = "{server}/mediabrowser/Users/{UserId}/Items?Fields=Name,CumulativeRunTimeTicks,Etag&Recursive=true&IncludeItemTypes=MusicAlbum&format=json" + + jsonData = doUtils.downloadUrl(url) + if (jsonData == ""): + return result + + if (jsonData[u'Items'] != ""): + result = jsonData[u'Items'] + + # Work around to only return items from the given list + if (result != None and len(result) > 0 and len(itemList) > 0): + newResult = [] + for item in result: + if (item[u'Id'] in itemList): + newResult.append(item) + result = newResult + + return result + + def getItem(self, id): result = None diff --git a/resources/lib/ReadKodiDB.py b/resources/lib/ReadKodiDB.py index 5393872f..a12b5feb 100644 --- a/resources/lib/ReadKodiDB.py +++ b/resources/lib/ReadKodiDB.py @@ -61,4 +61,23 @@ class ReadKodiDB(): else: return None - \ No newline at end of file + def getKodiMusicArtists(self, connection, cursor): + #returns all artists in Kodi db + cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type='artist'") + allartists = cursor.fetchall() + #this will return a list with tuples of all items returned from the database + return allartists + + def getKodiMusicAlbums(self, connection, cursor): + #returns all artists in Kodi db + cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type='album'") + allalbums = cursor.fetchall() + #this will return a list with tuples of all items returned from the database + return allalbums + + def getKodiMusicSongs(self, connection, cursor): + #returns all songs in Kodi db + cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type='song'") + allsongs = cursor.fetchall() + #this will return a list with tuples of all items returned from the database + return allsongs \ No newline at end of file diff --git a/resources/lib/Utils.py b/resources/lib/Utils.py index 86587874..7d3daba5 100644 --- a/resources/lib/Utils.py +++ b/resources/lib/Utils.py @@ -48,12 +48,18 @@ def convertEncoding(data): except: return data -def KodiSQL(): - connection = sqlite3.connect(getKodiDBPath()) +def KodiSQL(type="video"): + + if type == "music": + dbPath = getKodiMusicDBPath() + else: + dbPath = getKodiVideoDBPath() + + connection = sqlite3.connect(dbPath) return connection -def getKodiDBPath(): +def getKodiVideoDBPath(): if xbmc.getInfoLabel("System.BuildVersion").startswith("13"): #gotham dbVersion = "78" @@ -68,6 +74,22 @@ def getKodiDBPath(): return dbPath +def getKodiMusicDBPath(): + if xbmc.getInfoLabel("System.BuildVersion").startswith("13"): + #gotham + dbVersion = "48" + if xbmc.getInfoLabel("System.BuildVersion").startswith("15"): + #isengard + dbVersion = "52" + else: + #helix + dbVersion = "48" + + dbPath = xbmc.translatePath("special://profile/Database/MyMusic" + dbVersion + ".db") + + return dbPath + + def checkAuthentication(): #check authentication if addonSettings.getSetting('username') != "" and addonSettings.getSetting('ipaddress') != "": diff --git a/resources/lib/WriteKodiMusicDB.py b/resources/lib/WriteKodiMusicDB.py new file mode 100644 index 00000000..7aa0e321 --- /dev/null +++ b/resources/lib/WriteKodiMusicDB.py @@ -0,0 +1,447 @@ +################################################################################################# +# WriteKodiVideoDB +################################################################################################# + + +import xbmc +import xbmcgui +import xbmcaddon +import xbmcvfs +import json +import urllib +import sqlite3 +import os +from decimal import Decimal + +from DownloadUtils import DownloadUtils +from PlayUtils import PlayUtils +from ReadKodiDB import ReadKodiDB +from ReadEmbyDB import ReadEmbyDB +from API import API +import Utils as utils + +from xml.etree.ElementTree import Element, SubElement, Comment, tostring +from xml.etree import ElementTree +from xml.dom import minidom +import xml.etree.cElementTree as ET + +addon = xbmcaddon.Addon(id='plugin.video.emby') +addondir = xbmc.translatePath(addon.getAddonInfo('profile')) +dataPath = os.path.join(addondir,",musicfiles") + + +class WriteKodiMusicDB(): + + + + def updatePlayCountFromKodi(self, id, type, playcount=0): + #when user marks item watched from kodi interface update this in Emby + + utils.logMsg("Emby", "updatePlayCountFromKodi Called") + connection = utils.KodiSQL() + 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 + + if(emby_id != None): + addon = xbmcaddon.Addon(id='plugin.video.emby') + downloadUtils = DownloadUtils() + watchedurl = "{server}/mediabrowser/Users/{UserId}/PlayedItems/%s" % emby_id + if playcount != 0: + downloadUtils.downloadUrl(watchedurl, type="POST") + else: + downloadUtils.downloadUrl(watchedurl, type="DELETE") + + def addOrUpdateArtistToKodiLibrary( self, embyId ,connection, cursor): + + addon = xbmcaddon.Addon(id='plugin.video.emby') + WINDOW = xbmcgui.Window(10000) + username = WINDOW.getProperty('currUser') + userid = WINDOW.getProperty('userId%s' % username) + server = WINDOW.getProperty('server%s' % username) + downloadUtils = DownloadUtils() + + MBitem = ReadEmbyDB().getFullItem(embyId) + + # If the item already exist in the local Kodi DB we'll perform a full item update + # If the item doesn't exist, we'll add it to the database + + cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?",(MBitem["Id"],)) + result = cursor.fetchone() + if result != None: + artistid = result[0] + else: + artistid = None + + + #### The artist details ######### + name = utils.convertEncoding(MBitem["Name"]) + musicBrainsId = None + if MBitem.get("ProviderIds"): + if MBitem.get("ProviderIds").get("MusicBrainzArtist"): + musicBrainsId = MBitem.get("ProviderIds").get("MusicBrainzArtist") + + genres = " / ".join(MBitem.get("Genres")) + bio = utils.convertEncoding(API().getOverview(MBitem)) + dateadded = None + if MBitem.get("DateCreated"): + dateadded = MBitem["DateCreated"].split('.')[0].replace('T', " ") + + #safety check: does the musicbrainzartistId already exist? + cursor.execute("SELECT idArtist FROM artist WHERE strMusicBrainzArtistID = ?",(musicBrainsId,)) + result = cursor.fetchone() + if result != None: + artistid = result[0] + else: + artistid = None + + ##### ADD THE ARTIST ############ + if artistid == None: + + utils.logMsg("ADD artist to Kodi library","Id: %s - Title: %s" % (embyId, name)) + + #create the artist + cursor.execute("select coalesce(max(idArtist),0) as artistid from artist") + artistid = cursor.fetchone()[0] + artistid = artistid + 1 + pathsql="insert into artist(idArtist, strArtist, strMusicBrainzArtistID, strGenres, strBiography, dateAdded) values(?, ?, ?, ?, ?, ?)" + cursor.execute(pathsql, (artistid, name, musicBrainsId, genres, bio, dateadded)) + + #create the reference in emby table + pathsql = "INSERT into emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)" + cursor.execute(pathsql, (MBitem["Id"], artistid, "artist", API().getChecksum(MBitem))) + + #### UPDATE THE ARTIST ##### + else: + utils.logMsg("UPDATE artist to Kodi library","Id: %s - Title: %s" % (embyId, name)) + pathsql="update artist SET strArtist = ?, strMusicBrainzArtistID = ?, strGenres = ?, strBiography = ?, dateAdded = ? WHERE idArtist = ?" + cursor.execute(pathsql, (name, musicBrainsId, genres, bio, dateadded, artistid)) + + #update the checksum in emby table + cursor.execute("UPDATE emby SET checksum = ? WHERE emby_id = ?", (API().getChecksum(MBitem),MBitem["Id"])) + + #update artwork + self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), artistid, "artist", "thumb", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), artistid, "artist", "poster", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Banner"), artistid, "artist", "banner", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Logo"), artistid, "artist", "clearlogo", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Art"), artistid, "artist", "clearart", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Thumb"), artistid, "artist", "landscape", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Disc"), artistid, "artist", "discart", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Backdrop"), artistid, "artist", "fanart", cursor) + + def addOrUpdateAlbumToKodiLibrary( self, embyId ,connection, cursor, isSingle=False): + + addon = xbmcaddon.Addon(id='plugin.video.emby') + WINDOW = xbmcgui.Window(10000) + username = WINDOW.getProperty('currUser') + userid = WINDOW.getProperty('userId%s' % username) + server = WINDOW.getProperty('server%s' % username) + downloadUtils = DownloadUtils() + + MBitem = ReadEmbyDB().getFullItem(embyId) + + # If the item already exist in the local Kodi DB we'll perform a full item update + # If the item doesn't exist, we'll add it to the database + + cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?",(MBitem["Id"],)) + result = cursor.fetchone() + if result != None: + albumid = result[0] + else: + albumid = None + + + #### The album details ######### + name = utils.convertEncoding(MBitem["Name"]) + + MBartists = [] + for item in MBitem.get("AlbumArtists"): + MBartists.append(item["Name"]) + + artists = " / ".join(MBartists) + year = MBitem.get("ProductionYear") + musicBrainsId = None + if MBitem.get("ProviderIds"): + if MBitem.get("ProviderIds").get("MusicBrainzAlbum"): + musicBrainsId = MBitem.get("ProviderIds").get("MusicBrainzAlbum") + + genres = " / ".join(MBitem.get("Genres")) + bio = utils.convertEncoding(API().getOverview(MBitem)) + dateadded = None + if MBitem.get("DateCreated"): + dateadded = MBitem["DateCreated"].split('.')[0].replace('T', " ") + + if isSingle: + releasetype = "single" + name = None + else: + releasetype = "album" + + ##### ADD THE ALBUM ############ + if albumid == None: + + utils.logMsg("ADD album to Kodi library","Id: %s - Title: %s" % (embyId, name)) + + #create the album + cursor.execute("select coalesce(max(idAlbum),0) as albumid from album") + albumid = cursor.fetchone()[0] + albumid = albumid + 1 + pathsql="insert into album(idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, iYear, strGenres, dateAdded) values(?, ?, ?, ?, ?, ?, ?)" + cursor.execute(pathsql, (albumid, name, musicBrainsId, artists, year, genres, dateadded)) + + #create the reference in emby table + pathsql = "INSERT into emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)" + cursor.execute(pathsql, (MBitem["Id"], albumid, "album", API().getChecksum(MBitem))) + + #### UPDATE THE ALBUM ##### + else: + utils.logMsg("UPDATE album to Kodi library","Id: %s - Title: %s" % (embyId, name)) + pathsql="update album SET strAlbum = ?, strMusicBrainzAlbumID = ?, strArtists = ?, strGenres = ?, iYear = ?, dateAdded = ? WHERE idAlbum = ?" + cursor.execute(pathsql, (name, musicBrainsId, artists, genres, year, dateadded, albumid)) + + #update the checksum in emby table + cursor.execute("UPDATE emby SET checksum = ? WHERE emby_id = ?", (API().getChecksum(MBitem),MBitem["Id"])) + + #update artwork + self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), albumid, "album", "thumb", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "BoxRear"), albumid, "album", "poster", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Banner"), albumid, "album", "banner", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Logo"), albumid, "album", "clearlogo", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Art"), albumid, "album", "clearart", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Thumb"), albumid, "album", "landscape", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Disc"), albumid, "album", "discart", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Backdrop"), albumid, "album", "fanart", cursor) + + #link album to artist + artistid = None + for artist in MBitem.get("AlbumArtists"): + cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?",(artist["Id"],)) + result = cursor.fetchone() + if result: + artistid = result[0] + sql="INSERT OR REPLACE into album_artist(idArtist, idAlbum, strArtist) values(?, ?, ?)" + cursor.execute(sql, (artistid, albumid, artist["Name"])) + #update discography + sql="INSERT OR REPLACE into discography(idArtist, strAlbum, strYear) values(?, ?, ?)" + cursor.execute(sql, (artistid, name, str(year))) + + #return the album id + return albumid + + def addOrUpdateSongToKodiLibrary( self, embyId ,connection, cursor): + + addon = xbmcaddon.Addon(id='plugin.video.emby') + WINDOW = xbmcgui.Window(10000) + username = WINDOW.getProperty('currUser') + userid = WINDOW.getProperty('userId%s' % username) + server = WINDOW.getProperty('server%s' % username) + downloadUtils = DownloadUtils() + + MBitem = ReadEmbyDB().getFullItem(embyId) + + timeInfo = API().getTimeInfo(MBitem) + userData=API().getUserData(MBitem) + + # If the item already exist in the local Kodi DB we'll perform a full item update + # If the item doesn't exist, we'll add it to the database + + cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?",(MBitem["Id"],)) + result = cursor.fetchone() + if result != None: + songid = result[0] + else: + songid = None + + + #### The song details ######### + name = utils.convertEncoding(MBitem["Name"]) + musicBrainzId = None + if MBitem.get("ProviderIds"): + if MBitem.get("ProviderIds").get("MusicBrainzTrackId"): + musicBrainzId = MBitem.get("ProviderIds").get("MusicBrainzTrackId") + + genres = " / ".join(MBitem.get("Genres")) + artists = " / ".join(MBitem.get("Artists")) + track = MBitem.get("IndexNumber") + duration = int(timeInfo.get('Duration'))*60 + year = MBitem.get("ProductionYear") + bio = utils.convertEncoding(API().getOverview(MBitem)) + dateadded = None + if MBitem.get("DateCreated"): + dateadded = MBitem["DateCreated"].split('.')[0].replace('T', " ") + + if userData.get("LastPlayedDate") != None: + lastplayed = userData.get("LastPlayedDate") + else: + lastplayed = None + + playcount = None + if userData.get("PlayCount"): + playcount = int(userData.get("PlayCount")) + + #get the album + albumid = None + if MBitem.get("AlbumId"): + cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?",(MBitem.get("AlbumId"),)) + result = cursor.fetchone() + if result: + albumid = result[0] + if not albumid: + #no album = single in kodi, we need to create a single album for that + albumid = self.addOrUpdateAlbumToKodiLibrary(MBitem["Id"],connection, cursor, True) + + playurl = PlayUtils().getPlayUrl(server, MBitem["Id"], MBitem) + #for transcoding we need to create a fake strm file because I couldn't figure out how to set a http or plugin path in the music DB + if playurl.startswith("http"): + #create fake strm file + filename = item["Id"] + ".strm" + path = dataPath + strmFile = os.path.join(dataPath,filename) + text_file = open(strmFile, "w") + text_file.writelines(playurl) + text_file.close() + else: + #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,"") + + #get the path + cursor.execute("SELECT idPath as pathid FROM path WHERE strPath = ?",(path,)) + result = cursor.fetchone() + if result != None: + pathid = result[0] + else: + cursor.execute("select coalesce(max(idPath),0) as pathid from path") + pathid = cursor.fetchone()[0] + pathid = pathid + 1 + pathsql = "insert into path(idPath, strPath) values(?, ?)" + cursor.execute(pathsql, (pathid,path)) + + + ##### ADD THE SONG ############ + if songid == None: + + utils.logMsg("ADD song to Kodi library","Id: %s - Title: %s" % (embyId, name)) + + #create the song + cursor.execute("select coalesce(max(idSong),0) as songid from song") + songid = cursor.fetchone()[0] + songid = songid + 1 + pathsql="insert into song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack, iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cursor.execute(pathsql, (songid, albumid, pathid, artists, genres, name, track, duration, year, filename, musicBrainzId, playcount, lastplayed)) + + #create the reference in emby table + pathsql = "INSERT into emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)" + cursor.execute(pathsql, (MBitem["Id"], songid, "song", API().getChecksum(MBitem))) + + #### UPDATE THE SONG ##### + else: + utils.logMsg("UPDATE song to Kodi library","Id: %s - Title: %s" % (embyId, name)) + pathsql="update song SET idAlbum=?, strArtists=?, strGenres=?, strTitle=?, iTrack=?, iDuration=?, iYear=?, strFileName=?, strMusicBrainzTrackID=?, iTimesPlayed=?, lastplayed=? WHERE idSong = ?" + cursor.execute(pathsql, (albumid, artists, genres, name, track, duration, year, filename, musicBrainzId, playcount, lastplayed, songid)) + + #update the checksum in emby table + cursor.execute("UPDATE emby SET checksum = ? WHERE emby_id = ?", (API().getChecksum(MBitem),MBitem["Id"])) + + #add genres + self.AddGenresToMedia(songid, MBitem.get("Genres"), "song", cursor) + + #link song to album + if albumid: + sql="INSERT OR REPLACE into albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration) values(?, ?, ?, ?, ?)" + cursor.execute(sql, (songid, albumid, track, name, duration)) + + #link song to artist + for artist in MBitem.get("ArtistItems"): + cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?",(artist["Id"],)) + result = cursor.fetchone() + if result: + artistid = result[0] + sql="INSERT OR REPLACE into song_artist(idArtist, idSong, strArtist) values(?, ?, ?)" + cursor.execute(sql, (artistid, songid, artist["Name"])) + + #update artwork + self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), songid, "song", "thumb", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Primary"), songid, "song", "poster", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Banner"), songid, "song", "banner", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Logo"), songid, "song", "clearlogo", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Art"), songid, "song", "clearart", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Thumb"), songid, "song", "landscape", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Disc"), songid, "song", "discart", cursor) + self.addOrUpdateArt(API().getArtwork(MBitem, "Backdrop"), songid, "song", "fanart", cursor) + + def deleteItemFromKodiLibrary(self, id, connection, cursor ): + + cursor.execute("SELECT kodi_id, media_type FROM emby WHERE emby_id=?", (id,)) + result = cursor.fetchone() + if result: + kodi_id = result[0] + media_type = result[1] + if media_type == "movie": + utils.logMsg("deleting movie from Kodi library --> ",id) + cursor.execute("DELETE FROM movie WHERE idMovie = ?", (kodi_id,)) + if media_type == "episode": + utils.logMsg("deleting episode from Kodi library --> ",id) + cursor.execute("DELETE FROM episode WHERE idEpisode = ?", (kodi_id,)) + if media_type == "tvshow": + utils.logMsg("deleting tvshow from Kodi library --> ",id) + cursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,)) + if media_type == "musicvideo": + utils.logMsg("deleting musicvideo from Kodi library --> ",id) + cursor.execute("DELETE FROM musicvideo WHERE idMVideo = ?", (kodi_id,)) + + #delete the record in emby table + cursor.execute("DELETE FROM emby WHERE emby_id = ?", (id,)) + + def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor): + updateDone = False + if imageUrl: + cursor.execute("SELECT url FROM art WHERE media_id = ? AND media_type = ? AND type = ?", (kodiId, mediaType, imageType)) + result = cursor.fetchone() + if(result == None): + utils.logMsg("ArtworkSync", "Adding Art Link for kodiId: " + str(kodiId) + " (" + imageUrl + ")") + cursor.execute("INSERT INTO art(media_id, media_type, type, url) values(?, ?, ?, ?)", (kodiId, mediaType, imageType, imageUrl)) + else: + url = result[0]; + if(url != imageUrl): + utils.logMsg("ArtworkSync", "Updating Art Link for kodiId: " + str(kodiId) + " (" + url + ") -> (" + imageUrl + ")") + cursor.execute("UPDATE art set url = ? WHERE media_id = ? AND media_type = ? AND type = ?", (imageUrl, kodiId, mediaType, imageType)) + + def AddGenresToMedia(self, id, genres, mediatype, cursor): + + if genres: + + for genre in genres: + + idGenre = None + cursor.execute("SELECT idGenre as idGenre FROM genre WHERE strGenre = ?",(genre,)) + result = cursor.fetchone() + if result != None: + idGenre = result[0] + #create genre + if idGenre == None: + cursor.execute("select coalesce(max(idGenre),0) as idGenre from genre") + idGenre = cursor.fetchone()[0] + idGenre = idGenre + 1 + sql="insert into genre(idGenre, strGenre) values(?, ?)" + cursor.execute(sql, (idGenre,genre)) + + #assign genre to item + if mediatype == "album": + sql="INSERT OR REPLACE into album_genre(idGenre, idAlbum) values(?, ?)" + cursor.execute(sql, (idGenre,id)) + elif mediatype == "song": + sql="INSERT OR REPLACE into song_genre(idGenre, idSong) values(?, ?)" + cursor.execute(sql, (idGenre,id)) + else: + return + + + diff --git a/resources/settings.xml b/resources/settings.xml index ec3d8736..96677c2c 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -18,6 +18,7 @@ +