################################################################################################# # LibrarySync ################################################################################################# import xbmc import xbmcgui import xbmcaddon import xbmcvfs import json import sqlite3 import inspect import threading import urllib from datetime import datetime, timedelta, time from itertools import chain import urllib2 import os import KodiMonitor from API import API import Utils as utils from ClientInformation import ClientInformation 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')) dataPath = os.path.join(addondir,"library") movieLibrary = os.path.join(dataPath,'movies') tvLibrary = os.path.join(dataPath,'tvshows') WINDOW = xbmcgui.Window( 10000 ) class LibrarySync(threading.Thread): _shared_state = {} KodiMonitor = KodiMonitor.Kodi_Monitor() clientInfo = ClientInformation() addonName = clientInfo.getAddonName() updateItems = [] userdataItems = [] removeItems = [] def __init__(self, *args): self.__dict__ = self._shared_state threading.Thread.__init__(self, *args) def logMsg(self, msg, lvl=1): className = self.__class__.__name__ utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl)) def FullLibrarySync(self,manualRun=False): startupDone = WINDOW.getProperty("startup") == "done" syncInstallRunDone = utils.settings("SyncInstallRunDone") == "true" performMusicSync = utils.settings("enableMusicSync") == "true" dbSyncIndication = utils.settings("dbSyncIndication") == "true" ### BUILD VIDEO NODES LISTING ### VideoNodes().buildVideoNodesListing() ### CREATE SOURCES ### if utils.settings("Sources") != "true": # Only create sources once self.logMsg("Sources.xml created.", 0) utils.createSources() utils.settings("Sources", "true") # just do a incremental sync if that is what is required if(utils.settings("useIncSync") == "true" and utils.settings("SyncInstallRunDone") == "true") and manualRun == False: utils.logMsg("Sync Database", "Using incremental sync instead of full sync useIncSync=True)", 0) du = DownloadUtils() lastSync = utils.settings("LastIncrenetalSync") if(lastSync == None or len(lastSync) == 0): lastSync = "2010-01-01T00:00:00Z" utils.logMsg("Sync Database", "Incremental Sync Setting Last Run Time Loaded : " + lastSync, 0) lastSync = urllib2.quote(lastSync) url = "{server}/Emby.Kodi.SyncQueue/{UserId}/GetItems?LastUpdateDT=" + lastSync + "&format=json" utils.logMsg("Sync Database", "Incremental Sync Get Items URL : " + url, 0) results = du.downloadUrl(url) utils.logMsg("Sync Database", "Incremental Sync Changes : " + str(results), 0) changedItems = results["ItemsUpdated"] + results["ItemsAdded"] removedItems = results["ItemsRemoved"] userChanges = results["UserDataChanged"] WINDOW.setProperty("startup", "done") LibrarySync().remove_items(removedItems) LibrarySync().update_items(changedItems) LibrarySync().user_data_update(userChanges) self.SaveLastSync() return True #set some variable to check if this is the first run WINDOW.setProperty("SyncDatabaseRunning", "true") #show the progress dialog pDialog = None if (syncInstallRunDone == False or dbSyncIndication or manualRun): pDialog = xbmcgui.DialogProgressBG() pDialog.create('Emby for Kodi', 'Performing full sync') if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"): utils.logMsg("Sync Database", "Can not start SyncDatabaseShouldStop=True", 0) return True try: completed = True ### PROCESS VIDEO LIBRARY ### #create the sql connection to video db connection = utils.KodiSQL("video") cursor = connection.cursor() #Add the special emby table cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER, kodi_file_id INTEGER)") try: cursor.execute("ALTER TABLE emby ADD COLUMN kodi_file_id INTEGER") except: pass connection.commit() # sync movies self.MoviesFullSync(connection,cursor,pDialog) if (self.ShouldStop()): return False #sync Tvshows and episodes self.TvShowsFullSync(connection,cursor,pDialog) if (self.ShouldStop()): return False # 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 cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER, kodi_file_id INTEGER)") try: cursor.execute("ALTER TABLE emby ADD COLUMN kodi_file_id INTEGER") except: pass connection.commit() self.MusicFullSync(connection,cursor,pDialog) cursor.close() # set the install done setting if(syncInstallRunDone == False and completed): utils.settings("SyncInstallRunDone", "true") self.SaveLastSync() # Commit all DB changes at once and Force refresh the library xbmc.executebuiltin("UpdateLibrary(video)") #xbmc.executebuiltin("UpdateLibrary(music)") # 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) if(pDialog != None): pDialog.close() return True def SaveLastSync(self): # save last sync time lastSync = (datetime.utcnow() - timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%SZ') self.logMsg("Sync Database, Incremental Sync Setting Last Run Time Saved: %s" % lastSync, 1) utils.settings("LastIncrenetalSync", lastSync) def MoviesFullSync(self,connection,cursor, pDialog): views = ReadEmbyDB().getCollections("movies") allKodiMovieIds = list() allEmbyMovieIds = list() for view in views: allEmbyMovies = ReadEmbyDB().getMovies(view.get('id')) allKodiMovies = ReadKodiDB().getKodiMovies(connection, cursor) for kodimovie in allKodiMovies: allKodiMovieIds.append(kodimovie[1]) total = len(allEmbyMovies) + 1 count = 1 #### PROCESS ADDS AND UPDATES ### for item in allEmbyMovies: if (self.ShouldStop()): return False if not item.get('IsFolder'): allEmbyMovieIds.append(item["Id"]) if(pDialog != None): progressTitle = "Processing " + view.get('title') + " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle) count += 1 kodiMovie = None for kodimovie in allKodiMovies: if kodimovie[1] == item["Id"]: kodiMovie = kodimovie if kodiMovie == None: WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title')) else: if kodiMovie[2] != API().getChecksum(item): WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title')) #### PROCESS BOX SETS ##### if(pDialog != None): utils.logMsg("Sync Movies", "BoxSet Sync Started", 1) boxsets = ReadEmbyDB().getBoxSets() total = len(boxsets) + 1 count = 1 for boxset in boxsets: progressTitle = "Processing BoxSets"+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle) count += 1 if(self.ShouldStop()): return False boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"]) WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset,connection, cursor) for boxsetMovie in boxsetMovies: if(self.ShouldStop()): return False WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor) utils.logMsg("Sync Movies", "BoxSet Sync Finished", 1) #### PROCESS DELETES ##### allEmbyMovieIds = set(allEmbyMovieIds) for kodiId in allKodiMovieIds: if not kodiId in allEmbyMovieIds: WINDOW.setProperty(kodiId,"deleted") WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) ### commit all changes to database ### connection.commit() def MusicVideosFullSync(self,connection,cursor, pDialog): allKodiMusicvideoIds = list() allEmbyMusicvideoIds = list() allEmbyMusicvideos = ReadEmbyDB().getMusicVideos() allKodiMusicvideos = ReadKodiDB().getKodiMusicVideos(connection, cursor) for kodivideo in allKodiMusicvideos: allKodiMusicvideoIds.append(kodivideo[1]) total = len(allEmbyMusicvideos) + 1 count = 1 #### PROCESS ADDS AND UPDATES ### for item in allEmbyMusicvideos: if (self.ShouldStop()): return False if not item.get('IsFolder'): allEmbyMusicvideoIds.append(item["Id"]) if(pDialog != None): progressTitle = "Processing MusicVideos (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle) count += 1 kodiVideo = None for kodivideo in allKodiMusicvideos: if kodivideo[1] == item["Id"]: kodiVideo = kodivideo if kodiVideo == None: WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor) else: if kodiVideo[2] != API().getChecksum(item): WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor) #### PROCESS DELETES ##### allEmbyMusicvideoIds = set(allEmbyMusicvideoIds) for kodiId in allKodiMusicvideoIds: if not kodiId in allEmbyMusicvideoIds: WINDOW.setProperty(kodiId,"deleted") WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) ### commit all changes to database ### connection.commit() def TvShowsFullSync(self,connection,cursor,pDialog): views = ReadEmbyDB().getCollections("tvshows") allKodiTvShowIds = list() allEmbyTvShowIds = list() for view in views: allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id')) allKodiTvShows = ReadKodiDB().getKodiTvShows(connection, cursor) total = len(allEmbyTvShows) + 1 count = 1 for kodishow in allKodiTvShows: allKodiTvShowIds.append(kodishow[1]) #### TVSHOW: PROCESS ADDS AND UPDATES ### for item in allEmbyTvShows: if (self.ShouldStop()): return False if(pDialog != None): progressTitle = "Processing " + view.get('title') + " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle) count += 1 if utils.settings('syncEmptyShows') == "true" or (item.get('IsFolder') and item.get('RecursiveItemCount') != 0): allEmbyTvShowIds.append(item["Id"]) #build a list with all Id's and get the existing entry (if exists) in Kodi DB kodiShow = None for kodishow in allKodiTvShows: if kodishow[1] == item["Id"]: kodiShow = kodishow if kodiShow == None: # Tv show doesn't exist in Kodi yet so proceed and add it WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, view.get('title')) else: # If there are changes to the item, perform a full sync of the item if kodiShow[2] != API().getChecksum(item): WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, view.get('title')) #### PROCESS EPISODES ###### self.EpisodesFullSync(connection,cursor,item["Id"]) #### TVSHOW: PROCESS DELETES ##### allEmbyTvShowIds = set(allEmbyTvShowIds) for kodiId in allKodiTvShowIds: if not kodiId in allEmbyTvShowIds: WINDOW.setProperty(kodiId,"deleted") WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) ### commit all changes to database ### connection.commit() def EpisodesFullSync(self,connection,cursor,showId): WINDOW = xbmcgui.Window( 10000 ) allKodiEpisodeIds = list() allEmbyEpisodeIds = list() #get the kodi parent id cursor.execute("SELECT kodi_id FROM emby WHERE emby_id=?",(showId,)) kodiShowId = cursor.fetchone()[0] allEmbyEpisodes = ReadEmbyDB().getEpisodes(showId) allKodiEpisodes = ReadKodiDB().getKodiEpisodes(connection, cursor, kodiShowId) for kodiepisode in allKodiEpisodes: allKodiEpisodeIds.append(kodiepisode[1]) #### EPISODES: PROCESS ADDS AND UPDATES ### for item in allEmbyEpisodes: if (self.ShouldStop()): return False allEmbyEpisodeIds.append(item["Id"]) #get the existing entry (if exists) in Kodi DB kodiEpisode = None for kodiepisode in allKodiEpisodes: if kodiepisode[1] == item["Id"]: kodiEpisode = kodiepisode if kodiEpisode == None: # Episode doesn't exist in Kodi yet so proceed and add it WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(item["Id"], kodiShowId, connection, cursor) else: # If there are changes to the item, perform a full sync of the item if kodiEpisode[2] != API().getChecksum(item): WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(item["Id"], kodiShowId, connection, cursor) #### EPISODES: PROCESS DELETES ##### allEmbyEpisodeIds = set(allEmbyEpisodeIds) for kodiId in allKodiEpisodeIds: if (not kodiId in allEmbyEpisodeIds): WINDOW.setProperty(kodiId,"deleted") WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor) def MusicFullSync(self, connection,cursor, pDialog): self.ProcessMusicArtists(connection,cursor,pDialog) connection.commit() self.ProcessMusicAlbums(connection,cursor,pDialog) connection.commit() self.ProcessMusicSongs(connection,cursor,pDialog) ### commit all changes to database ### connection.commit() def ProcessMusicSongs(self,connection,cursor,pDialog): allKodiSongIds = list() allEmbySongIds = list() allEmbySongs = ReadEmbyDB().getMusicSongsTotal() 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) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "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,connection, cursor) else: if kodiSong[2] != API().getChecksum(item): WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item,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().getMusicArtistsTotal() allKodiArtists = ReadKodiDB().getKodiMusicArtists(connection, cursor) for kodiartist in allKodiArtists: allKodiArtistIds.append(kodiartist[1]) total = len(allEmbyArtists) + 1 count = 1 #### PROCESS ARTIST 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) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "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,connection, cursor) else: if kodiArtist[2] != API().getChecksum(item): WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item,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().getMusicAlbumsTotal() 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) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "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,connection, cursor) else: if kodiAlbum[2] != API().getChecksum(item): WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item,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): startupDone = WINDOW.getProperty("startup") == "done" #only perform incremental scan when full scan is completed if startupDone: #this will only perform sync for items received by the websocket dbSyncIndication = utils.settings("dbSyncIndication") == "true" performMusicSync = utils.settings("enableMusicSync") == "true" WINDOW.setProperty("SyncDatabaseRunning", "true") #show the progress dialog pDialog = None if (dbSyncIndication and xbmc.Player().isPlaying() == False): pDialog = xbmcgui.DialogProgressBG() pDialog.create('Emby for Kodi', 'Incremental Sync') self.logMsg("Doing LibraryChanged : Show Progress IncrementalSync()", 0); connection = utils.KodiSQL("video") cursor = connection.cursor() try: #### PROCESS MOVIES #### views = ReadEmbyDB().getCollections("movies") for view in views: allEmbyMovies = ReadEmbyDB().getMovies(view.get('id'), itemList) count = 1 total = len(allEmbyMovies) + 1 for item in allEmbyMovies: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync Movies", progressTitle) count = count + 1 if not item.get('IsFolder'): WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title')) #### PROCESS BOX SETS ##### boxsets = ReadEmbyDB().getBoxSets() count = 1 total = len(boxsets) + 1 for boxset in boxsets: boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"]) WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset,connection, cursor) if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync BoxSet", progressTitle) count = count + 1 for boxsetMovie in boxsetMovies: WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor) #### PROCESS TV SHOWS #### views = ReadEmbyDB().getCollections("tvshows") for view in views: allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id'),itemList) count = 1 total = len(allEmbyTvShows) + 1 for item in allEmbyTvShows: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync Tv", progressTitle) count = count + 1 if utils.settings('syncEmptyShows') == "true" or (item.get('IsFolder') and item.get('RecursiveItemCount') != 0): kodiId = WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, view.get('title')) #### PROCESS OTHERS BY THE ITEMLIST ###### count = 1 total = len(itemList) + 1 for item in itemList: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync Items", progressTitle) count = count + 1 MBitem = ReadEmbyDB().getItem(item) itemType = MBitem.get('Type', "") #### PROCESS EPISODES ###### if "Episode" in itemType: #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] else: kodi_show_id = None if kodi_show_id: WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(MBitem["Id"], kodi_show_id, connection, cursor) else: #tv show doesn't exist #perform full tvshow sync instead so both the show and episodes get added self.TvShowsFullSync(connection,cursor,None) elif "Season" in itemType: #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 "BoxSet" in itemType: boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"]) WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset,connection, cursor) for boxsetMovie in boxsetMovies: WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor) #### PROCESS MUSICVIDEOS #### elif "MusicVideo" in itemType: if not MBitem.get('IsFolder'): WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(MBitem["Id"],connection, cursor) ### commit all changes to database ### connection.commit() cursor.close() ### PROCESS MUSIC LIBRARY ### if performMusicSync: connection = utils.KodiSQL("music") cursor = connection.cursor() for item in itemList: MBitem = ReadEmbyDB().getItem(item) itemType = MBitem.get('Type', "") if "MusicArtist" in itemType: WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(MBitem, connection, cursor) if "MusicAlbum" in itemType: WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(MBitem, connection, cursor) if "Audio" in itemType: WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(MBitem, connection, cursor) connection.commit() cursor.close() finally: if(pDialog != None): pDialog.close() self.SaveLastSync() 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 removefromDB(self, itemList, deleteEmbyItem = False): dbSyncIndication = utils.settings("dbSyncIndication") == "true" #show the progress dialog pDialog = None if (dbSyncIndication and xbmc.Player().isPlaying() == False): pDialog = xbmcgui.DialogProgressBG() pDialog.create('Emby for Kodi', 'Incremental Sync') self.logMsg("Doing LibraryChanged : Show Progress removefromDB()", 0); # Delete from Kodi before Emby # To be able to get mediaType doUtils = DownloadUtils() video = {} music = [] # Database connection to myVideosXX.db connectionvideo = utils.KodiSQL() cursorvideo = connectionvideo.cursor() # Database connection to myMusicXX.db connectionmusic = utils.KodiSQL("music") cursormusic = connectionmusic.cursor() count = 1 total = len(itemList) + 1 for item in itemList: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync Delete ", progressTitle) count = count + 1 # Sort by type for database deletion try: # Search video database self.logMsg("Check video database.", 1) cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,)) mediatype = cursorvideo.fetchone()[0] video[item] = mediatype #video.append(itemtype) except: self.logMsg("Check music database.", 1) try: # Search music database cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,)) cursormusic.fetchone()[0] music.append(item) except: self.logMsg("Item %s is not found in Kodi database." % item, 1) if len(video) > 0: connection = connectionvideo cursor = cursorvideo # Process video library count = 1 total = len(video) + 1 for item in video: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync Delete ", progressTitle) count = count + 1 type = video[item] self.logMsg("Doing LibraryChanged: Items Removed: Calling deleteItemFromKodiLibrary: %s" % item, 1) if "episode" in type: # Get the TV Show Id for reference later showId = ReadKodiDB().getShowIdByEmbyId(item, connection, cursor) self.logMsg("ShowId: %s" % showId, 1) WriteKodiVideoDB().deleteItemFromKodiLibrary(item, connection, cursor) # Verification if "episode" in type: showTotalCount = ReadKodiDB().getShowTotalCount(showId, connection, cursor) self.logMsg("ShowTotalCount: %s" % showTotalCount, 1) # If there are no episodes left if showTotalCount == 0 or showTotalCount == None: # Delete show embyId = ReadKodiDB().getEmbyIdByKodiId(showId, "tvshow", connection, cursor) self.logMsg("Message: Doing LibraryChanged: Deleting show: %s" % embyId, 1) WriteKodiVideoDB().deleteItemFromKodiLibrary(embyId, connection, cursor) connection.commit() # Close connection cursorvideo.close() if len(music) > 0: connection = connectionmusic cursor = cursormusic #Process music library if utils.settings('enableMusicSync') == "true": for item in music: self.logMsg("Message : Doing LibraryChanged : Items Removed : Calling deleteItemFromKodiLibrary (musiclibrary): " + item, 0) WriteKodiMusicDB().deleteItemFromKodiLibrary(item, connection, cursor) connection.commit() # Close connection cursormusic.close() if deleteEmbyItem: for item in itemList: url = "{server}/mediabrowser/Items/%s" % item self.logMsg('Deleting via URL: %s' % url) doUtils.downloadUrl(url, type = "DELETE") xbmc.executebuiltin("Container.Refresh") if(pDialog != None): pDialog.close() self.SaveLastSync() def setUserdata(self, listItems): dbSyncIndication = utils.settings("dbSyncIndication") == "true" musicenabled = utils.settings('enableMusicSync') == "true" #show the progress dialog pDialog = None if (dbSyncIndication and xbmc.Player().isPlaying() == False): pDialog = xbmcgui.DialogProgressBG() pDialog.create('Emby for Kodi', 'Incremental Sync') self.logMsg("Doing LibraryChanged : Show Progress setUserdata()", 0); # We need to sort between video and music database video = [] music = [] # Database connection to myVideosXX.db connectionvideo = utils.KodiSQL() cursorvideo = connectionvideo.cursor() # Database connection to myMusicXX.db connectionmusic = utils.KodiSQL('music') cursormusic = connectionmusic.cursor() count = 1 total = len(listItems) + 1 for userdata in listItems: # Sort between video and music itemId = userdata['ItemId'] if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle) count = count + 1 cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (itemId,)) try: # Search video database self.logMsg("Check video database.", 2) mediatype = cursorvideo.fetchone()[0] video.append(userdata) except: if musicenabled: cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (itemId,)) try: # Search music database self.logMsg("Check the music database.", 2) mediatype = cursormusic.fetchone()[0] music.append(userdata) except: self.logMsg("Item %s is not found in Kodi database." % itemId, 1) else: self.logMsg("Item %s is not found in Kodi database." % itemId, 1) if len(video) > 0: connection = connectionvideo cursor = cursorvideo # Process the userdata update for video library count = 1 total = len(video) + 1 for userdata in video: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle) count = count + 1 WriteKodiVideoDB().updateUserdata(userdata, connection, cursor) connection.commit() xbmc.executebuiltin("UpdateLibrary(video)") # Close connection cursorvideo.close() if len(music) > 0: connection = connectionmusic cursor = cursormusic #Process music library count = 1 total = len(video) + 1 # Process the userdata update for music library if musicenabled: for userdata in music: if(pDialog != None): progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")" percentage = int(((float(count) / float(total)) * 100)) pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle) count = count + 1 WriteKodiMusicDB().updateUserdata(userdata, connection, cursor) connection.commit() #xbmc.executebuiltin("UpdateLibrary(music)") # Close connection cursormusic.close() if(pDialog != None): pDialog.close() self.SaveLastSync() def remove_items(self, itemsRemoved): # websocket client if(len(itemsRemoved) > 0): self.logMsg("Doing LibraryChanged : Processing Deleted : " + str(itemsRemoved), 0) self.removeItems.extend(itemsRemoved) def update_items(self, itemsToUpdate): # websocket client if(len(itemsToUpdate) > 0): self.logMsg("Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0) self.updateItems.extend(itemsToUpdate) def user_data_update(self, userDataList): # websocket client if(len(userDataList) > 0): self.logMsg("Doing LibraryChanged : Processing User Data Changed : " + str(userDataList), 0) self.userdataItems.extend(userDataList) def ShouldStop(self): if(xbmc.abortRequested): return True if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"): return True return False def run(self): self.logMsg("--- Starting Library Sync Thread ---", 0) WINDOW = xbmcgui.Window(10000) startupComplete = False while not self.KodiMonitor.abortRequested(): # In the event the server goes offline after # the thread has already been started. while self.suspendClient == True: # The service.py will change self.suspendClient to False if self.KodiMonitor.waitForAbort(5): # Abort was requested while waiting. We should exit break # Library sync if not startupComplete: # Run full sync self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1) startTime = datetime.now() libSync = self.FullLibrarySync() elapsedTime = datetime.now() - startTime self.logMsg("Doing_Db_Sync: syncDatabase (Finished in: %s) %s" % (str(elapsedTime).split('.')[0], libSync), 1) if libSync: startupComplete = True # Set via Kodi Monitor event if WINDOW.getProperty("OnWakeSync") == "true" and WINDOW.getProperty('Server_online') == "true": WINDOW.clearProperty("OnWakeSync") if WINDOW.getProperty("SyncDatabaseRunning") != "true": self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Started)", 0) libSync = self.FullLibrarySync() self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Finished) " + str(libSync), 0) if len(self.updateItems) > 0: # 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: # 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: # Remove item from Kodi library self.logMsg("Removing items: %s" % self.removeItems, 1) listItems = self.removeItems self.removeItems = [] self.removefromDB(listItems) if self.KodiMonitor.waitForAbort(1): # Abort was requested while waiting. We should exit break self.logMsg("--- Library Sync Thread stopped ---", 0) def suspendClient(self): self.suspendClient = True self.logMsg("--- Library Sync Thread paused ---", 0) def resumeClient(self): self.suspendClient = False self.logMsg("--- Library Sync Thread resumed ---", 0)