# -*- coding: utf-8 -*- ################################################################################################# # WriteKodiMusicDB ################################################################################################# import sqlite3 from datetime import datetime from ntpath import split as ntsplit import xbmc import xbmcgui import xbmcaddon from ClientInformation import ClientInformation import Utils as utils from API import API from PlayUtils import PlayUtils from ReadKodiDB import ReadKodiDB from ReadEmbyDB import ReadEmbyDB from TextureCache import TextureCache class WriteKodiMusicDB(): textureCache = TextureCache() kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2]) addonName = ClientInformation().getAddonName() 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): className = self.__class__.__name__ utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl)) def addOrUpdateArtistToKodiLibrary(self, MBitem, connection, cursor): # 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 kodiVersion = self.kodiversion embyId = MBitem["Id"] cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (embyId,)) try: artistid = cursor.fetchone()[0] except: artistid = None ##### The artist details ##### lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S') dateadded = API().getDateCreated(MBitem) checksum = API().getChecksum(MBitem) name = MBitem['Name'] musicBrainzId = API().getProvider(MBitem, "musicBrainzArtist") genres = " / ".join(MBitem.get('Genres')) bio = API().getOverview(MBitem) # Associate artwork artworks = API().getAllArtwork(MBitem, parentInfo=True) thumb = artworks['Primary'] backdrops = artworks['Backdrop'] # List if thumb: thumb = "%s" % thumb if backdrops: fanart = "%s" % backdrops[0] else: fanart = "" ##### UPDATE THE ARTIST ##### if artistid: self.logMsg("UPDATE artist to Kodi library, Id: %s - Artist: %s" % (embyId, name), 1) if kodiVersion == 16: query = "UPDATE artist SET strArtist = ?, strMusicBrainzArtistID = ?, strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ? WHERE idArtist = ?" cursor.execute(query, (name, musicBrainzId, genres, bio, thumb, fanart, lastScraped, artistid)) else: query = "UPDATE artist SET strArtist = ?, strMusicBrainzArtistID = ?, strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ?, dateadded = ? WHERE idArtist = ?" cursor.execute(query, (name, musicBrainzId, genres, bio, thumb, fanart, lastScraped, dateadded, artistid)) # Update the checksum in emby table query = "UPDATE emby SET checksum = ? WHERE emby_id = ?" cursor.execute(query, (checksum, embyId)) ##### OR ADD THE ARTIST ##### else: self.logMsg("ADD artist to Kodi library, Id: %s - Artist: %s" % (embyId, name), 1) #safety checks: It looks like Emby supports the same artist multiple times in the database while Kodi doesn't allow that. In case that happens we just merge the artist in the Kodi database. # Safety check 1: does the artist already exist? cursor.execute("SELECT idArtist FROM artist WHERE strArtist = ? COLLATE NOCASE", (name,)) try: artistid = cursor.fetchone()[0] self.logMsg("Artist already exists in Kodi library - appending to existing object, Id: %s - Artist: %s - MusicBrainzId: %s - existing Kodi Id: %s" % (embyId, name, musicBrainzId, str(artistid)), 1) except: pass # Safety check 2: does the MusicBrainzArtistId already exist? cursor.execute("SELECT idArtist FROM artist WHERE strMusicBrainzArtistID = ?", (musicBrainzId,)) try: artistid = cursor.fetchone()[0] self.logMsg("Artist already exists in Kodi library - appending to existing object, Id: %s - Artist: %s - MusicBrainzId: %s - existing Kodi Id: %s" % (embyId, name, musicBrainzId, str(artistid)), 1) except: pass if not artistid: # Create the artist cursor.execute("select coalesce(max(idArtist),0) as artistid from artist") artistid = cursor.fetchone()[0] + 1 if kodiVersion == 16: query = "INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID, strGenres, strBiography, strImage, strFanart, lastScraped) values(?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(query, (artistid, name, musicBrainzId, genres, bio, thumb, fanart, lastScraped)) else: query = "INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID, strGenres, strBiography, strImage, strFanart, lastScraped, dateAdded) values(?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(query, (artistid, name, musicBrainzId, genres, bio, thumb, fanart, lastScraped, dateadded)) # Create the reference in emby table query = "INSERT INTO emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)" cursor.execute(query, (embyId, artistid, "artist", checksum)) # Update artwork self.textureCache.addArtwork(artworks, artistid, "artist", cursor) def addOrUpdateAlbumToKodiLibrary(self, MBitem, connection, cursor): kodiVersion = self.kodiversion embyId = MBitem["Id"] # 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 = ?", (embyId,)) try: albumid = cursor.fetchone()[0] except: albumid = None genres = MBitem.get('Genres') ##### The album details ##### lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S') dateadded = API().getDateCreated(MBitem) checksum = API().getChecksum(MBitem) name = MBitem['Name'] musicBrainzId = API().getProvider(MBitem, "musicBrainzAlbum") year = MBitem.get('ProductionYear') genre = " / ".join(genres) bio = API().getOverview(MBitem) MBartists = [] for item in MBitem['AlbumArtists']: MBartists.append(item['Name']) artists = " / ".join(MBartists) # Associate the artwork artworks = API().getAllArtwork(MBitem, parentInfo=True) thumb = artworks['Primary'] if thumb: thumb = "%s" % thumb ##### UPDATE THE ALBUM ##### if albumid: self.logMsg("UPDATE album to Kodi library, Id: %s - Title: %s" % (embyId, name), 1) if kodiVersion == 15: # Kodi Isengard query = "UPDATE album SET strAlbum = ?, strMusicBrainzAlbumID = ?, strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ? WHERE idAlbum = ?" cursor.execute(query, (name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded, "album", albumid)) elif kodiVersion == 16: query = "UPDATE album SET strAlbum = ?, strMusicBrainzAlbumID = ?, strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?, lastScraped = ?, strReleaseType = ? WHERE idAlbum = ?" cursor.execute(query, (name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, "album", albumid)) else: # Kodi Gotham and Helix query = "UPDATE album SET strAlbum = ?, strMusicBrainzAlbumID = ?, strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?, lastScraped = ?, dateAdded = ? WHERE idAlbum = ?" cursor.execute(query, (name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded, albumid)) # Update the checksum in emby table query = "UPDATE emby SET checksum = ? WHERE emby_id = ?" cursor.execute(query, (checksum, embyId)) ##### OR ADD THE ALBUM ##### else: self.logMsg("ADD album to Kodi library, Id: %s - Title: %s" % (embyId, name), 1) # Safety check: does the strMusicBrainzAlbumID already exist? cursor.execute("SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?", (musicBrainzId,)) try: albumid = cursor.fetchone()[0] except: # Create the album cursor.execute("select coalesce(max(idAlbum),0) as albumid from album") albumid = cursor.fetchone()[0] + 1 if kodiVersion == 15: # Kodi Isengard query = "INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, iYear, strGenres, strReview, strImage, lastScraped, dateAdded, strReleaseType) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(query, (albumid, name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded, "album")) elif kodiVersion == 16: query = "INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, iYear, strGenres, strReview, strImage, lastScraped, strReleaseType) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(query, (albumid, name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, "album")) else: # Kodi Gotham and Helix query = "INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, iYear, strGenres, strReview, strImage, lastScraped, dateAdded) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(query, (albumid, name, musicBrainzId, artists, year, genre, bio, thumb, lastScraped, dateadded)) # Create the reference in emby table query = "INSERT INTO emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)" cursor.execute(query, (embyId, albumid, "album", checksum)) # Add genres self.AddGenresToMedia(albumid, genres, "album", cursor) # Update artwork 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'] else: album_artists = MBitem.get('ArtistItems', []) for artist in album_artists: cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (artist['Id'],)) try: artistid = cursor.fetchone()[0] except: pass else: query = "INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist) values(?, ?, ?)" cursor.execute(query, (artistid, albumid, artist['Name'])) # Update discography query = "INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear) values(?, ?, ?)" cursor.execute(query, (artistid, name, str(year))) def addOrUpdateSongToKodiLibrary(self, MBitem, connection, cursor): kodiVersion = self.kodiversion embyId = MBitem["Id"] # 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 = ?", (embyId,)) try: songid = cursor.fetchone()[0] except: songid = None timeInfo = API().getTimeInfo(MBitem) userData = API().getUserData(MBitem) genres = MBitem.get('Genres') ##### The song details ##### playcount = userData.get('PlayCount') lastplayed = userData.get('LastPlayedDate') dateadded = API().getDateCreated(MBitem) checksum = API().getChecksum(MBitem) name = MBitem['Name'] musicBrainzId = API().getProvider(MBitem, "musicBrainzTrackId") genre = " / ".join(genres) artists = " / ".join(MBitem.get('Artists')) tracknumber = MBitem.get('IndexNumber', 0) disc = MBitem.get('ParentIndexNumber', 1) track = disc*2**16 + tracknumber year = MBitem.get('ProductionYear') bio = API().getOverview(MBitem) duration = timeInfo.get('TotalTime') if utils.settings('directstreammusic') == "true": WINDOW = xbmcgui.Window(10000) username = WINDOW.getProperty('currUser') server = WINDOW.getProperty('server%s' % username) playurl = PlayUtils().directStream(MBitem, server, embyId, "Audio") filename = "stream.mp3" path = playurl.replace(filename, "") else: # Get the path and filename playurl = PlayUtils().directPlay(MBitem) path, filename = ntsplit(playurl) if "/" in playurl: path = "%s/" % path elif "\\" in playurl: path = "%s\\" % path # Validate the path in database cursor.execute("SELECT idPath as pathid FROM path WHERE strPath = ?", (path,)) try: pathid = cursor.fetchone()[0] except: cursor.execute("select coalesce(max(idPath),0) as pathid from path") pathid = cursor.fetchone()[0] + 1 query = "INSERT INTO path(idPath, strPath) values(?, ?)" cursor.execute(query, (pathid, path)) # Get the album cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (MBitem.get("AlbumId"),)) try: albumid = cursor.fetchone()[0] except: # No album found, create a single's album cursor.execute("select coalesce(max(idAlbum),0) as albumid from album") albumid = cursor.fetchone()[0] + 1 if kodiVersion == 15: # Kodi Isengard query = "INSERT INTO album(idAlbum, strGenres, iYear, dateAdded, strReleaseType) values(?, ?, ?, ?, ?)" cursor.execute(query, (albumid, genre, year, dateadded, "single")) elif kodiVersion == 16: query = "INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType) values(?, ?, ?, ?)" cursor.execute(query, (albumid, genre, year, "single")) else: # Kodi Gotham and Helix query = "INSERT INTO album(idAlbum, strGenres, iYear, dateAdded) values(?, ?, ?, ?)" cursor.execute(query, (albumid, genre, year, dateadded)) finally: cursor.execute("SELECT strArtists FROM album WHERE idAlbum = ?", (albumid,)) result = cursor.fetchone() if result and result[0] == "": # Link album to artists if MBitem['AlbumArtists']: album_artists = MBitem['AlbumArtists'] else: album_artists = MBitem['ArtistItems'] MBartists = [] for artist in album_artists: MBartists.append(artist['Name']) cursor.execute("SELECT kodi_id FROM emby WHERE emby_id = ?", (artist['Id'],)) try: artistid = cursor.fetchone()[0] except: pass else: query = "INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist) values(?, ?, ?)" cursor.execute(query, (artistid, albumid, artist['Name'])) artists_onalbum = " / ".join(MBartists) if kodiVersion == 15: # Kodi Isengard query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?" cursor.execute(query, (artists_onalbum, albumid)) elif kodiVersion == 16: query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?" cursor.execute(query, (artists_onalbum, albumid)) else: # Kodi Gotham and Helix query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?" cursor.execute(query, (artists_onalbum, albumid)) ##### UPDATE THE SONG ##### if songid: self.logMsg("UPDATE song to Kodi library, Id: %s - Title: %s" % (embyId, name), 1) query = "UPDATE song SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?, iDuration = ?, iYear = ?, strFilename = ?, strMusicBrainzTrackID = ?, iTimesPlayed = ?, lastplayed = ? WHERE idSong = ?" cursor.execute(query, (albumid, artists, genre, name, track, duration, year, filename, musicBrainzId, playcount, lastplayed, songid)) # Update the checksum in emby table query = "UPDATE emby SET checksum = ? WHERE emby_id = ?" cursor.execute(query, (checksum, embyId)) ##### OR ADD THE SONG ##### else: self.logMsg("ADD song to Kodi library, Id: %s - Title: %s" % (embyId, name), 1) # Create the song cursor.execute("select coalesce(max(idSong),0) as songid from song") songid = cursor.fetchone()[0] + 1 query = "INSERT INTO song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack, iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(query, (songid, albumid, pathid, artists, genre, name, track, duration, year, filename, musicBrainzId, playcount, lastplayed)) # Create the reference in emby table query = "INSERT INTO emby(emby_id, kodi_id, media_type, checksum) values(?, ?, ?, ?)" cursor.execute(query, (embyId, songid, "song", checksum)) # Add genres self.AddGenresToMedia(songid, genres, "song", cursor) # Link song to album if albumid: query = "INSERT OR REPLACE INTO albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration) values(?, ?, ?, ?, ?)" cursor.execute(query, (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'],)) try: artistid = cursor.fetchone()[0] except: pass else: query = "INSERT OR REPLACE INTO song_artist(idArtist, idSong, strArtist) values(?, ?, ?)" cursor.execute(query, (artistid, songid, artist['Name'])) # Update artwork self.textureCache.addArtwork(API().getAllArtwork(MBitem, parentInfo=True), songid, "song", cursor) def deleteItemFromKodiLibrary(self, id, connection, cursor): cursor.execute("SELECT kodi_id, media_type FROM emby WHERE emby_id=?", (id,)) try: result = cursor.fetchone() kodi_id = result[0] media_type = result[1] except: pass else: if "artist" in media_type: self.logMsg("Deleting artist from Kodi library, Id: %s" % id, 1) cursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodi_id,)) elif "song" in media_type: self.logMsg("Deleting song from Kodi library, Id: %s" % id, 1) cursor.execute("DELETE FROM song WHERE idSong = ?", (kodi_id,)) elif "album" in media_type: self.logMsg("Deleting album from Kodi library, Id: %s" % id, 1) cursor.execute("DELETE FROM album WHERE idAlbum = ?", (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): if imageUrl: cacheimage = False cursor.execute("SELECT url FROM art WHERE media_id = ? AND media_type = ? AND type = ?", (kodiId, mediaType, imageType,)) try: url = cursor.fetchone()[0] except: # Image does not exists cacheimage = True self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 1) query = "INSERT INTO art(media_id, media_type, type, url) values(?, ?, ?, ?)" cursor.execute(query, (kodiId, mediaType, imageType, imageUrl)) else: if url != imageUrl: cacheimage = True self.logMsg("Updating Art Link for kodiId: %s (%s) -> (%s)" % (kodiId, url, imageUrl), 1) query = "UPDATE art SET url = ? WHERE media_id = ? and media_type = ? and type = ?" cursor.execute(query, (imageUrl, kodiId, mediaType, imageType)) # Cache fanart textures in Kodi texture cache if cacheimage and "fanart" in imageType: self.textureCache.CacheTexture(imageUrl) def AddGenresToMedia(self, id, genres, mediatype, cursor): if genres: for genre in genres: cursor.execute("SELECT idGenre as idGenre FROM genre WHERE strGenre = ? COLLATE NOCASE", (genre,)) try: idGenre = cursor.fetchone()[0] except: # Create the genre cursor.execute("select coalesce(max(idGenre),0) as idGenre from genre") idGenre = cursor.fetchone()[0] + 1 query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)" cursor.execute(query, (idGenre, genre)) finally: # Assign the genre to item if "album" in mediatype: query = "INSERT OR REPLACE INTO album_genre(idGenre, idAlbum) values(?, ?)" cursor.execute(query, (idGenre, id)) elif "song" in mediatype: query = "INSERT OR REPLACE INTO song_genre(idGenre, idSong) values(?, ?)" cursor.execute(query, (idGenre, id)) def updateUserdata(self, userdata, connection, cursor): # This updates: LastPlayedDate, Playcount embyId = userdata['ItemId'] MBitem = ReadEmbyDB().getItem(embyId) if not MBitem: self.logMsg("UPDATE userdata to Kodi library FAILED, Item %s not found on server!" % embyId, 1) return # Get details checksum = API().getChecksum(MBitem) userdata = API().getUserData(MBitem) # Find the Kodi Id cursor.execute("SELECT kodi_id, media_type FROM emby WHERE emby_id = ?", (embyId,)) try: result = cursor.fetchone() kodiid = result[0] mediatype = result[1] self.logMsg("Found embyId: %s in database - kodiId: %s type: %s" % (embyId, kodiid, mediatype), 1) except: self.logMsg("Id: %s not found in the emby database table." % embyId, 1) else: if mediatype in ("song"): playcount = userdata['PlayCount'] dateplayed = userdata['LastPlayedDate'] query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ? WHERE idSong = ?" cursor.execute(query, (playcount, dateplayed, kodiid)) #update the checksum in emby table query = "UPDATE emby SET checksum = ? WHERE emby_id = ?" cursor.execute(query, (checksum, embyId))