jellyfin-kodi/resources/lib/LibrarySync.py

432 lines
17 KiB
Python

#################################################################################################
# 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
from API import API
import Utils as utils
from DownloadUtils import DownloadUtils
from ReadEmbyDB import ReadEmbyDB
from ReadKodiDB import ReadKodiDB
from WriteKodiDB import WriteKodiDB
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():
def FullLibrarySync(self):
#set some variable to check if this is the first run
addon = xbmcaddon.Addon(id='plugin.video.emby')
startupDone = WINDOW.getProperty("startup") == "done"
syncInstallRunDone = addon.getSetting("SyncInstallRunDone") == "true"
dbSyncIndication = addon.getSetting("dbSyncIndication") == "true"
WINDOW.setProperty("SyncDatabaseRunning", "true")
#show the progress dialog
pDialog = None
if (syncInstallRunDone == False or dbSyncIndication):
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
connection = utils.KodiSQL()
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)
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)
# set the install done setting
if(syncInstallRunDone == False and completed):
addon = xbmcaddon.Addon(id='plugin.video.emby') #force a new instance of the addon
addon.setSetting("SyncInstallRunDone", "true")
# Commit all DB changes at once and Force refresh the library
xbmc.executebuiltin("UpdateLibrary(video)")
# set prop to show we have run for the first time
WINDOW.setProperty("startup", "done")
finally:
WINDOW.setProperty("SyncDatabaseRunning", "false")
utils.logMsg("Sync DB", "syncDatabase Exiting", 0)
cursor.close()
if(pDialog != None):
pDialog.close()
return True
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) + ")"
pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle)
count += 1
kodiMovie = None
for kodimovie in allKodiMovies:
if kodimovie[1] == item["Id"]:
kodiMovie = kodimovie
if kodiMovie == None:
WriteKodiDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
else:
if kodiMovie[2] != API().getChecksum(item):
WriteKodiDB().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) + ")"
pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle)
count += 1
if(self.ShouldStop()):
return False
boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
WriteKodiDB().addBoxsetToKodiLibrary(boxset,connection, cursor)
for boxsetMovie in boxsetMovies:
if(self.ShouldStop()):
return False
WriteKodiDB().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")
WriteKodiDB().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) + ")"
pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle)
count += 1
kodiVideo = None
for kodivideo in allKodiMusicvideos:
if kodivideo[1] == item["Id"]:
kodiVideo = kodivideo
if kodiVideo == None:
WriteKodiDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
else:
if kodiVideo[2] != API().getChecksum(item):
WriteKodiDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
#### PROCESS DELETES #####
allEmbyMusicvideoIds = set(allEmbyMusicvideoIds)
for kodiId in allKodiMusicvideoIds:
if not kodiId in allEmbyMusicvideoIds:
WINDOW.setProperty(kodiId,"deleted")
WriteKodiDB().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) + ")"
pDialog.update(0, "Emby for Kodi - Running Sync", progressTitle)
count += 1
if 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
WriteKodiDB().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):
WriteKodiDB().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")
WriteKodiDB().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
WriteKodiDB().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):
WriteKodiDB().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")
WriteKodiDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
def IncrementalSync(self, itemList):
#this will only perform sync for items received by the websocket
addon = xbmcaddon.Addon(id='plugin.video.emby')
dbSyncIndication = addon.getSetting("dbSyncIndication") == "true"
WINDOW.setProperty("SyncDatabaseRunning", "true")
#show the progress dialog
pDialog = None
if (dbSyncIndication):
pDialog = xbmcgui.DialogProgressBG()
pDialog.create('Emby for Kodi', 'Performing incremental sync...')
connection = utils.KodiSQL()
cursor = connection.cursor()
try:
#### PROCESS MOVIES ####
views = ReadEmbyDB().getCollections("movies")
for view in views:
allEmbyMovies = ReadEmbyDB().getMovies(view.get('id'), itemList)
for item in allEmbyMovies:
if not item.get('IsFolder'):
WriteKodiDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
#### PROCESS BOX SETS #####
boxsets = ReadEmbyDB().getBoxSets()
for boxset in boxsets:
boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
WriteKodiDB().addBoxsetToKodiLibrary(boxset,connection, cursor)
for boxsetMovie in boxsetMovies:
WriteKodiDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor)
#### PROCESS TV SHOWS ####
views = ReadEmbyDB().getCollections("tvshows")
for view in views:
allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id'),itemList)
for item in allEmbyTvShows:
if item.get('IsFolder') and item.get('RecursiveItemCount') != 0:
kodiId = WriteKodiDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
#### PROCESS EPISODES ######
for item in itemList:
MBitem = ReadEmbyDB().getItem(item)
if MBitem["Type"] == "Episode":
#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:
WriteKodiDB().addOrUpdateEpisodeToKodiLibrary(MBitem["Id"], kodi_show_id, connection, cursor)
#### PROCESS MUSICVIDEOS ####
allEmbyMusicvideos = ReadEmbyDB().getMusicVideos(itemList)
for item in allEmbyMusicvideos:
if not item.get('IsFolder'):
WriteKodiDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
### commit all changes to database ###
connection.commit()
finally:
cursor.close()
xbmc.executebuiltin("UpdateLibrary(video)")
WINDOW.setProperty("SyncDatabaseRunning", "false")
#close the progress dialog
if(pDialog != None):
pDialog.close()
def ShouldStop(self):
if(xbmc.abortRequested):
return True
if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"):
return True
return False