From 5da6b4153e05183cf66d18174780deaa5a801afa Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sat, 9 Jan 2016 16:14:02 +0100 Subject: [PATCH] TV shows sync v0.1 --- resources/lib/PlexAPI.py | 99 +++++--- resources/lib/itemtypes.py | 176 ++++++------- resources/lib/librarysync.py | 462 +++++++++-------------------------- 3 files changed, 271 insertions(+), 466 deletions(-) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index e58a6a10..7e51abcd 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -1232,16 +1232,46 @@ class PlexAPI(): try: result = jsondata['_children'] except KeyError: - self.logMsg("Error retrieving all items for Plex section %s" % viewId, 1) + self.logMsg("Error retrieving all items for Plex section %s" % viewId, -1) pass return result - def GetPlexSubitems(self, key): + def GetAllPlexLeaves(self, key): """ Returns a list (raw JSON API dump) of all Plex subitems for the key. - (e.g. key=/library/metadata/194853/children pointing to a season) - """ + (e.g. /library/metadata/194853/allLeaves pointing to all episodes + of a TV show) + Input: + key Key to a Plex item, e.g. 12345 + """ + result = [] + url = "{server}/library/metadata/%s/allLeaves" % key + jsondata = self.doUtils.downloadUrl(url) + try: + result = jsondata['_children'] + except KeyError: + self.logMsg("Error retrieving all children for Plex item %s" % key, -1) + pass + return result + + def GetAllPlexChildren(self, key): + """ + Returns a list (raw JSON API dump) of all Plex children for the key. + (e.g. /library/metadata/194853/children pointing to a season) + + Input: + key Key to a Plex item, e.g. 12345 + """ + result = [] + url = "{server}/library/metadata/%s/children" % key + jsondata = self.doUtils.downloadUrl(url) + try: + result = jsondata['_children'] + except KeyError: + self.logMsg("Error retrieving all children for Plex item %s" % key, -1) + pass + return result def GetPlexMetadata(self, key): """ @@ -1270,7 +1300,7 @@ class PlexAPI(): headerOptions = {'Accept': 'application/xml'} xml = self.doUtils.downloadUrl(url, headerOptions=headerOptions) if not xml: - self.logMsg("Error retrieving metadata for %s" % url, 1) + self.logMsg("Error retrieving metadata for %s" % url, -1) return xml @@ -1388,17 +1418,23 @@ class API(): Returns the Plex unique movie id as a str, not int """ item = self.item - key_regex = re.compile(r'/(\d+)$') # XML try: item = item[self.child].attrib # JSON except KeyError: pass - key = item['key'] - key = key_regex.findall(key)[0] + key = item['ratingKey'] return str(key) + def getIndex(self): + """ + Returns the 'index' of an PMS XML reply. Depicts e.g. season number. + """ + item = self.item[self.child].attrib + index = item['index'] + return str(index) + def getDateCreated(self): """ Returns the date when this library item was created @@ -1550,33 +1586,19 @@ class API(): genre.append(child.attrib['tag']) return genre - def getProvider(self, providername): + def getProvider(self, providername=None): """ - providername: imdb, tvdb, musicBrainzArtist, musicBrainzAlbum, - musicBrainzTrackId + providername: depricated - Return IMDB: "tt1234567". Returns None if not found + Return IMDB, e.g. "imdb://tt0903624?lang=en". Returns None if not found """ item = self.item item = item[self.child].attrib - imdb_regex = re.compile(r'''( - imdb:// # imdb tag, which will be followed be tt1234567 - (tt\d{7}) # actual IMDB ID, e.g. tt1234567 - \?? # zero or one ? - (.*) # rest, e.g. language setting - )''', re.VERBOSE) + regex = re.compile(r'''com.plexapp.agents.(.+)$''') + + provider = regex.findall(item['guid']) try: - if "Imdb" in providername: - provider = imdb_regex.findall(item['guid']) - provider = provider[0][1] - elif "tvdb" in providername: - provider = item['ProviderIds']['Tvdb'] - elif "musicBrainzArtist" in providername: - provider = item['ProviderIds']['MusicBrainzArtist'] - elif "musicBrainzAlbum" in providername: - provider = item['ProviderIds']['MusicBrainzAlbum'] - elif "musicBrainzTrackId" in providername: - provider = item['ProviderIds']['MusicBrainzTrackId'] + provider = provider[0] except: provider = None return provider @@ -1752,6 +1774,25 @@ class API(): string = " / ".join(listobject) return string + def getEpisodeDetails(self): + """ + Call on a single episode. + + Output: for the corresponding the TV show and season: + [ + TV show key, Plex: 'grandparentRatingKey' + TV show title, Plex: 'grandparentTitle' + TV show season, Plex: 'parentIndex' + Episode number, Plex: 'index' + ] + """ + item = self.item[self.child].attrib + key = item['grandparentRatingKey'] + title = item['grandparentTitle'] + season = item['parentIndex'] + episode = item['index'] + return key, title, season, episode + def getFilePath(self): """ returns the path to the Plex object, e.g. "/library/metadata/221803" diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py index 581f9613..496b31fc 100644 --- a/resources/lib/itemtypes.py +++ b/resources/lib/itemtypes.py @@ -1173,12 +1173,8 @@ class MusicVideos(Items): self.logMsg("Deleted musicvideo %s from kodi database" % itemid, 1) + class TVShows(Items): - - - def __init__(self, embycursor, kodicursor): - Items.__init__(self, embycursor, kodicursor) - def added(self, items, pdialog): total = len(items) @@ -1233,15 +1229,18 @@ class TVShows(Items): emby_db = self.emby_db kodi_db = self.kodi_db artwork = self.artwork - API = api.API(item) + API = PlexAPI.API(item) - if utils.settings('syncEmptyShows') == "false" and not item['RecursiveItemCount']: - self.logMsg("Skipping empty show: %s" % item['Name'], 1) - return + # if utils.settings('syncEmptyShows') == "false" and not item['RecursiveItemCount']: + # self.logMsg("Skipping empty show: %s" % item['Name'], 1) + # return # 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 update_item = True - itemid = item['Id'] + itemid = API.getKey() + if not itemid: + self.logMsg("Cannot parse XML data for TV show", -1) + return emby_dbitem = emby_db.getItem_byId(itemid) try: showid = emby_dbitem[0] @@ -1252,32 +1251,29 @@ class TVShows(Items): update_item = False self.logMsg("showid: %s not found." % itemid, 2) - if viewtag is None or viewid is None: - # Get view tag from emby - viewtag, viewid, mediatype = emby.getView_embyId(itemid) - self.logMsg("View tag found: %s" % viewtag, 2) + # if viewtag is None or viewid is None: + # # Get view tag from emby + # viewtag, viewid, mediatype = emby.getView_embyId(itemid) + # self.logMsg("View tag found: %s" % viewtag, 2) # fileId information checksum = API.getChecksum() - dateadded = API.getDateCreated() - userdata = API.getUserData() - playcount = userdata['PlayCount'] - dateplayed = userdata['LastPlayedDate'] # item details - genres = item['Genres'] - title = item['Name'] - plot = API.getOverview() - rating = item.get('CommunityRating') + genres = API.getGenres() + title, sorttitle = API.getTitle() + plot = API.getPlot() + rating = API.getAudienceRating() premieredate = API.getPremiereDate() tvdb = API.getProvider('Tvdb') - sorttitle = item['SortName'] mpaa = API.getMpaa() - genre = " / ".join(genres) + genre = API.joinList(genres) studios = API.getStudios() - studio = " / ".join(studios) + try: + studio = studios[0] + except IndexError: + studio = None - ##### GET THE FILE AND PATH ##### playurl = API.getFilePath() @@ -1380,76 +1376,78 @@ class TVShows(Items): kodicursor.execute(query, (path, None, None, 1, pathid)) # Process cast - people = artwork.getPeopleArtwork(item['People']) + people = API.getPeopleList() kodi_db.addPeople(showid, people, "tvshow") # Process genres kodi_db.addGenres(showid, genres, "tvshow") # Process artwork - artwork.addArtwork(artwork.getAllArtwork(item), showid, "tvshow", kodicursor) + allartworks = API.getAllArtwork() + artwork.addArtwork(allartworks, showid, "tvshow", kodicursor) # Process studios kodi_db.addStudios(showid, studios, "tvshow") # Process tags: view, emby tags - tags = [viewtag] - tags.extend(item['Tags']) - if userdata['Favorite']: - tags.append("Favorite tvshows") - kodi_db.addTags(showid, tags, "tvshow") + # tags = [viewtag] + # tags.extend(item['Tags']) + # if userdata['Favorite']: + # tags.append("Favorite tvshows") + # kodi_db.addTags(showid, tags, "tvshow") # Process seasons - all_seasons = emby.getSeasons(itemid) - for season in all_seasons['Items']: - self.add_updateSeason(season, showid=showid) - else: - # Finally, refresh the all season entry - seasonid = kodi_db.addSeason(showid, -1) - # Process artwork - artwork.addArtwork(artwork.getAllArtwork(item), seasonid, "season", kodicursor) - def add_updateSeason(self, item, showid=None): + def refreshSeasonEntry(self, item, showid): + API = PlexAPI.API(item) + kodicursor = self.kodicursor + kodi_db = self.kodi_db + # Finally, refresh the all season entry + seasonid = kodi_db.addSeason(showid, -1) + # Process artwork for season + allartworks = API.getAllArtwork() + artwork.addArtwork(allartworks, seasonid, "season", kodicursor) + def add_updateSeason(self, item, viewid=None, viewtag=None): + self.logMsg("add_updateSeason called with", 1) + self.logMsg("item: %s" % item, 1) + self.logMsg("viewid: %s" % viewid, 1) + self.logMsg("viewName: %s" % viewtag, 1) + showid = viewid + itemid = viewid kodicursor = self.kodicursor emby_db = self.emby_db kodi_db = self.kodi_db artwork = self.artwork + API = PlexAPI.API(item) + self.logMsg("add_updateSeason initialization done", 1) - if item['LocationType'] == "Virtual": - self.logMsg("Skipping virtual season.") - return - - seasonnum = item.get('IndexNumber', 1) - itemid = item['Id'] - - if showid is None: - try: - seriesId = item['SeriesId'] - showid = emby_db.getItem_byId(seriesId)[0] - except KeyError: - return - except TypeError: - # Show is missing, update show instead. - show = self.emby.getItem(seriesId) - self.add_update(show) - return - + seasonnum = API.getIndex() + self.logMsg("seasonnum: %s" % seasonnum, 1) seasonid = kodi_db.addSeason(showid, seasonnum) + self.logMsg("seasonid: %s" % seasonid, 1) # Create the reference in emby table emby_db.addReference(itemid, seasonid, "Season", "season", parentid=showid) + self.logMsg("Added db reference", 1) # Process artwork - artwork.addArtwork(artwork.getAllArtwork(item), seasonid, "season", kodicursor) + allartworks = API.getAllArtwork() + self.logMsg("Gotten allartworks: %s" % allartworks, 1) + artwork.addArtwork(allartworks, seasonid, "season", kodicursor) + self.logMsg("Added Artwork reference", 1) - def add_updateEpisode(self, item): + def add_updateEpisode(self, item, viewtag=None, viewid=None): + """ + viewtag and viewid are irrelevant! + """ # Process single episode kodiversion = self.kodiversion kodicursor = self.kodicursor emby_db = self.emby_db kodi_db = self.kodi_db artwork = self.artwork - API = api.API(item) + API = PlexAPI.API(item) # 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 update_item = True - itemid = item['Id'] + itemid = API.getKey() + self.logMsg("itemid: %s" % itemid, 2) emby_dbitem = emby_db.getItem_byId(itemid) try: episodeid = emby_dbitem[0] @@ -1472,21 +1470,20 @@ class TVShows(Items): dateplayed = userdata['LastPlayedDate'] # item details - people = API.getPeople() - writer = " / ".join(people['Writer']) - director = " / ".join(people['Director']) - title = item['Name'] - plot = API.getOverview() - rating = item.get('CommunityRating') + director, writer, cast, producer = API.getPeople() + director = API.joinList(director) + writer = API.joinList(writer) + cast = API.joinList(cast) + producer = API.joinList(producer) + title, sorttitle = API.getTitle() + plot = API.getPlot() + rating = API.getAudienceRating() runtime = API.getRuntime() premieredate = API.getPremiereDate() # episode details - seriesId = item['SeriesId'] - seriesName = item['SeriesName'] - season = item.get('ParentIndexNumber') - episode = item.get('IndexNumber', -1) - + seriesId, seriesName, season, episode = API.getEpisodeDetails() + if season is None: if item.get('AbsoluteEpisodeNumber'): # Anime scenario @@ -1496,16 +1493,18 @@ class TVShows(Items): season = -1 # Specials ordering within season - if item.get('AirsAfterSeasonNumber'): - airsBeforeSeason = item['AirsAfterSeasonNumber'] - airsBeforeEpisode = 4096 # Kodi default number for afterseason ordering - else: - airsBeforeSeason = item.get('AirsBeforeSeasonNumber', "-1") - airsBeforeEpisode = item.get('AirsBeforeEpisodeNumber', "-1") + # if item.get('AirsAfterSeasonNumber'): + # airsBeforeSeason = item['AirsAfterSeasonNumber'] + # airsBeforeEpisode = 4096 # Kodi default number for afterseason ordering + # else: + # airsBeforeSeason = item.get('AirsBeforeSeasonNumber', "-1") + # airsBeforeEpisode = item.get('AirsBeforeEpisodeNumber', "-1") + airsBeforeSeason = "-1" + airsBeforeEpisode = "-1" # Append multi episodes to title - if item.get('IndexNumberEnd'): - title = "| %02d | %s" % (item['IndexNumberEnd'], title) + # if item.get('IndexNumberEnd'): + # title = "| %02d | %s" % (item['IndexNumberEnd'], title) # Get season id show = emby_db.getItem_byId(seriesId) @@ -1519,7 +1518,7 @@ class TVShows(Items): try: showid = show[0] except TypeError: - self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId)) + self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId), -1) return False seasonid = kodi_db.addSeason(showid, season) @@ -1658,12 +1657,13 @@ class TVShows(Items): "WHERE idFile = ?" )) kodicursor.execute(query, (pathid, filename, dateadded, fileid)) - + + self.logMsg("Start processing getPeopleList", 1) # Process cast - people = artwork.getPeopleArtwork(item['People']) + people = API.getPeopleList() kodi_db.addPeople(episodeid, people, "episode") # Process artwork - artworks = artwork.getAllArtwork(item) + artworks = API.getAllArtwork() artwork.addOrUpdateArt(artworks['Primary'], episodeid, "episode", "thumb", kodicursor) # Process stream details streams = API.getMediaStreams() diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py index c086ebfa..01f3ab6e 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -87,6 +87,8 @@ class ThreadedProcessMetadata(threading.Thread): data = { 'itemType': as used to call functions in itemtypes.py e.g. 'Movies' => itemtypes.Movies() + 'method' Name of method in itemtypes.py + e.g. 'add_updateSeason' 'viewName' Plex str for library view (e.g. 'Movies') 'viewId' Plex Id to identifiy the library view } @@ -108,6 +110,7 @@ class ThreadedProcessMetadata(threading.Thread): viewId = self.data['viewId'] global processMetadataCount with itemFkt() as item: + itemSubFkt = getattr(item, self.data['method']) while self.stopped() is False: # grabs item from queue try: @@ -117,7 +120,7 @@ class ThreadedProcessMetadata(threading.Thread): continue # Do the work; lock to be sure we've only got 1 Thread with self.lock: - item.add_update( + itemSubFkt( plexitem, viewtag=viewName, viewid=viewId @@ -159,19 +162,19 @@ class ThreadedShowSyncInfo(threading.Thread): downloadLock = self.locks[0] processLock = self.locks[1] self.dialog.create( - self.addonName + ": Sync " + self.viewName + - ': ' + str(total) + 'items', - "Starting" - ) + "%s: Sync %s: %s items" % (self.addonName, + self.viewName, + str(total)), + "Starting") global getMetadataCount global processMetadataCount total = 2 * total totalProgress = 0 - while self.stopped() is not True: + while self.stopped() is False: with downloadLock: getMetadataProgress = getMetadataCount - with processLock: - processMetadataProgress = processMetadataCount + #with processLock: + processMetadataProgress = processMetadataCount totalProgress = getMetadataProgress + processMetadataProgress try: percentage = int(float(totalProgress) / float(total)*100.0) @@ -182,7 +185,7 @@ class ThreadedShowSyncInfo(threading.Thread): message="Downloaded: %s, Processed: %s" % (getMetadataProgress, processMetadataProgress) ) - time.sleep(0.5) + time.sleep(1) def stopThread(self): self._shouldstop.set() @@ -345,16 +348,17 @@ class LibrarySync(threading.Thread): # embyconn.commit() self.initializeDBs() # Sync video library - process = { + # process = { - 'movies': self.movies, - 'musicvideos': self.musicvideos, - 'tvshows': self.tvshows, - 'homevideos': self.homevideos - } + # 'movies': self.movies, + # 'musicvideos': self.musicvideos, + # 'tvshows': self.tvshows, + # 'homevideos': self.homevideos + # } process = { - 'movies': self.PlexMovies + 'movies': self.PlexMovies, + 'tvshows': self.PlexTVShows # 'tvshows': self.PlexTVShows } @@ -585,30 +589,34 @@ class LibrarySync(threading.Thread): """ if self.compare: # Manual sync - for plexmovie in elementList: - if self.shouldStop(): - return False - API = PlexAPI.API(plexmovie) - plex_checksum = API.getChecksum() - itemid = API.getKey() - self.allPlexElementsId[itemid] = plex_checksum - kodi_checksum = self.allKodiElementsId.get(itemid) - if kodi_checksum != plex_checksum: - # Only update if movie is not in Kodi or checksum is - # different - self.updatelist.append(itemid) + for item in elementList: + # Only look at valid items = Plex library items + if item.get('ratingKey', False): + if self.shouldStop(): + return False + API = PlexAPI.API(item) + plex_checksum = API.getChecksum() + itemid = API.getKey() + self.allPlexElementsId[itemid] = plex_checksum + kodi_checksum = self.allKodiElementsId.get(itemid) + if kodi_checksum != plex_checksum: + # Only update if movie is not in Kodi or checksum is + # different + self.updatelist.append(itemid) else: # Initial or repair sync: get all Plex movies - for plexmovie in elementList: - API = PlexAPI.API(plexmovie) - itemid = API.getKey() - plex_checksum = API.getChecksum() - self.allPlexElementsId[itemid] = plex_checksum - self.updatelist.append(itemid) + for item in elementList: + # Only look at valid items = Plex library items + if item.get('ratingKey', False): + API = PlexAPI.API(item) + itemid = API.getKey() + plex_checksum = API.getChecksum() + self.allPlexElementsId[itemid] = plex_checksum + self.updatelist.append(itemid) # Update the Kodi popup info return self.updatelist, self.allPlexElementsId - def GetAndProcessXMLs(self, itemType, viewName, viewId): + def GetAndProcessXMLs(self, itemType, viewName, viewId, method): """ Downloads all XMLs for itemType (e.g. Movies, TV-Shows). Processes them by then calling itemtypes.() @@ -617,6 +625,8 @@ class LibrarySync(threading.Thread): itemType: to construct itemtypes.py function name: 'Movies' viewName: Plex name of library, e.g. 'My Movies' viewId: Plex Id for that library + method: name of itemtypes.py method, + e.g. 'add_updateSeason' Returns True if/when done """ @@ -639,6 +649,9 @@ class LibrarySync(threading.Thread): getMetadataCount = 0 global processMetadataCount processMetadataCount = 0 + # Populate queue: GetMetadata + for itemId in self.updatelist: + getMetadataQueue.put(itemId) # Spawn GetMetadata threads threads = [] for i in range(self.syncThreadNumber): @@ -652,13 +665,11 @@ class LibrarySync(threading.Thread): t.start() threads.append(t) self.logMsg("%s download threads spawned" % self.syncThreadNumber, 1) - # Populate queue: GetMetadata - for itemId in self.updatelist: - getMetadataQueue.put(itemId) self.logMsg("Queue populated", 1) # Spawn one more thread to process Metadata, once downloaded data = { 'itemType': itemType, + 'method': method, 'viewName': viewName, 'viewId': viewId } @@ -714,7 +725,6 @@ class LibrarySync(threading.Thread): self.logMsg("%s compare finished." % itemType, 1) return True - def PlexMovies(self): # Initialize plx = PlexAPI.PlexAPI() @@ -724,21 +734,20 @@ class LibrarySync(threading.Thread): views = plx.GetPlexCollections('movie') self.logMsg("Media folders: %s" % views, 1) - # Get movies from Plex server - embyconn = utils.kodiSQL('emby') - embycursor = embyconn.cursor() - emby_db = embydb.Embydb_Functions(embycursor) - if self.compare: + # Get movies from Plex server + embyconn = utils.kodiSQL('emby') + embycursor = embyconn.cursor() + emby_db = embydb.Embydb_Functions(embycursor) # Pull the list of movies and boxsets in Kodi try: self.allKodiElementsId = dict(emby_db.getChecksum('Movie')) except ValueError: self.allKodiElementsId = {} + embyconn.close() else: # Getting all metadata, hence set Kodi elements to {} self.allKodiElementsId = {} - embyconn.close() ##### PROCESS MOVIES ##### for view in views: @@ -752,177 +761,14 @@ class LibrarySync(threading.Thread): # Populate self.updatelist and self.allPlexElementsId self.GetUpdatelist(all_plexmovies) self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1) - # Returns True if successful - result = self.GetAndProcessXMLs('Movies', viewName, viewId) + result = self.GetAndProcessXMLs( + 'Movies', + viewName, + viewId, + 'add_update' + ) return result - - def movies(self, embycursor, kodicursor, pdialog, compare=False): - # Get movies from emby - emby = self.emby - emby_db = embydb.Embydb_Functions(embycursor) - movies = itemtypes.Movies(embycursor, kodicursor) - - views = emby_db.getView_byType('movies') - views += emby_db.getView_byType('mixed') - self.logMsg("Media folders: %s" % views, 1) - - if compare: - # Pull the list of movies and boxsets in Kodi - try: - all_kodimovies = dict(emby_db.getChecksum('Movie')) - except ValueError: - all_kodimovies = {} - - try: - all_kodisets = dict(emby_db.getChecksum('BoxSet')) - except ValueError: - all_kodisets = {} - - all_embymoviesIds = set() - all_embyboxsetsIds = set() - updatelist = [] - - ##### PROCESS MOVIES ##### - for view in views: - - if self.shouldStop(): - return False - - # Get items per view - viewId = view['id'] - viewName = view['name'] - - if pdialog: - pdialog.update( - heading="Emby for Kodi", - message="Gathering movies from view: %s..." % viewName) - - if compare: - # Manual sync - if pdialog: - pdialog.update( - heading="Emby for Kodi", - message="Comparing movies from view: %s..." % viewName) - - all_embymovies = emby.getMovies(viewId, basic=True) - for embymovie in all_embymovies['Items']: - - if self.shouldStop(): - return False - - API = api.API(embymovie) - itemid = embymovie['Id'] - all_embymoviesIds.add(itemid) - - - if all_kodimovies.get(itemid) != API.getChecksum(): - # Only update if movie is not in Kodi or checksum is different - updatelist.append(itemid) - - self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1) - embymovies = emby.getFullItems(updatelist) - total = len(updatelist) - del updatelist[:] - else: - # Initial or repair sync - all_embymovies = emby.getMovies(viewId) - total = all_embymovies['TotalRecordCount'] - embymovies = all_embymovies['Items'] - - - if pdialog: - pdialog.update(heading="Processing %s / %s items" % (viewName, total)) - - count = 0 - for embymovie in embymovies: - # Process individual movies - if self.shouldStop(): - return False - - title = embymovie['Name'] - if pdialog: - percentage = int((float(count) / float(total))*100) - pdialog.update(percentage, message=title) - count += 1 - movies.add_update(embymovie, viewName, viewId) - else: - self.logMsg("Movies finished.", 2) - - - ##### PROCESS BOXSETS ##### - if pdialog: - pdialog.update(heading="Emby for Kodi", message="Gathering boxsets from server...") - - boxsets = emby.getBoxset() - - if compare: - # Manual sync - embyboxsets = [] - - if pdialog: - pdialog.update( - heading="Emby for Kodi", - message="Comparing boxsets...") - - for boxset in boxsets['Items']: - - if self.shouldStop(): - return False - - # Boxset has no real userdata, so using etag to compare - checksum = boxset['Etag'] - itemid = boxset['Id'] - all_embyboxsetsIds.add(itemid) - - if all_kodisets.get(itemid) != checksum: - # Only update if boxset is not in Kodi or checksum is different - updatelist.append(itemid) - embyboxsets.append(boxset) - - self.logMsg("Boxsets to update: %s" % updatelist, 1) - total = len(updatelist) - else: - total = boxsets['TotalRecordCount'] - embyboxsets = boxsets['Items'] - - - if pdialog: - pdialog.update(heading="Processing Boxsets / %s items" % total) - - count = 0 - for boxset in embyboxsets: - # Process individual boxset - if self.shouldStop(): - return False - - title = boxset['Name'] - if pdialog: - percentage = int((float(count) / float(total))*100) - pdialog.update(percentage, message=title) - count += 1 - movies.add_updateBoxset(boxset) - else: - self.logMsg("Boxsets finished.", 2) - - - ##### PROCESS DELETES ##### - if compare: - # Manual sync, process deletes - for kodimovie in all_kodimovies: - if kodimovie not in all_embymoviesIds: - movies.remove(kodimovie) - else: - self.logMsg("Movies compare finished.", 1) - - for boxset in all_kodisets: - if boxset not in all_embyboxsetsIds: - movies.remove(boxset) - else: - self.logMsg("Boxsets compare finished.", 1) - - return True - def musicvideos(self, embycursor, kodicursor, pdialog, compare=False): # Get musicvideos from emby emby = self.emby @@ -1111,22 +957,21 @@ class LibrarySync(threading.Thread): return True - def PlexTVShows(self, embycursor, kodicursor): + def PlexTVShows(self): # Initialize plx = PlexAPI.PlexAPI() - compare = self.compare - pdialog = self.pDialog self.updatelist = [] self.allPlexElementsId = {} - # Get shows from emby - emby_db = embydb.Embydb_Functions(embycursor) - tvshows = itemtypes.TVShows(embycursor, kodicursor) views = plx.GetPlexCollections('show') self.logMsg("Media folders: %s" % views, 1) self.allKodiElementsId = {} - if compare: + if self.compare: + # Get movies from Plex server + embyconn = utils.kodiSQL('emby') + embycursor = embyconn.cursor() + emby_db = embydb.Embydb_Functions(embycursor) # Pull the list of TV shows already in Kodi try: all_koditvshows = dict(emby_db.getChecksum('Series')) @@ -1139,145 +984,64 @@ class LibrarySync(threading.Thread): self.allKodiElementsId.update(all_kodiepisodes) except ValueError: pass + embyconn.close() + ##### PROCESS TV Shows ##### for view in views: self.updatelist = [] + self.allPlexElementsId = {} if self.shouldStop(): return False # Get items per view viewId = view['id'] viewName = view['name'] - if pdialog: - pdialog.update( - heading=self.addonName, - message="Gathering TV Shows from view: %s..." % viewName) - all_plexTvShows = plx.GetPlexSectionResults(viewId) + allPlexTvShows = plx.GetPlexSectionResults(viewId) + # Populate self.updatelist and self.allPlexElementsId + self.GetUpdatelist(allPlexTvShows) + # Process self.updatelist + self.GetAndProcessXMLs('TVShows', + viewName, + viewId, + 'add_update') + self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1) - # Populate self.updatelist with TV shows and self.allPlexElementsId - self.GetUpdatelist(all_plexTvShows, viewName) + # Save for later use + allPlexTvShowsId = self.allPlexElementsId - # Run through self.updatelist, get XML metadata per item and safe - # to Kodi - self.ProcessUpdateList(tvshows, view) + ##### PROCESS TV Seasons ##### + # Cycle through tv shows + for tvShowId in allPlexTvShowsId: + self.updatelist = [] + self.allPlexElementsId = {} + # Grab all seasons to tvshow from PMS + seasons = plx.GetAllPlexChildren(tvShowId) + # Populate self.updatelist and self.allPlexElementsId + self.GetUpdatelist(seasons) + # Process self.updatelist + self.GetAndProcessXMLs('TVShows', + None, + tvShowId, # send showId instead of viewid + 'add_updateSeason') + XMLtvshow = plx.GetPlexMetadata(tvShowId) + with itemtypes.TVShows() as TVshow: + TVshow.refreshSeasonEntry(XMLtvshow, tvShowId) + self.logMsg("Processed all seasons of TV show with Plex Id %s" % tvShowId, 1) - - - if compare: - # Manual sync - for plexTvShow in all_plexTvShows: - if self.shouldStop(): - return False - API = plx(plexTvShow) - plex_checksum = API.getChecksum() - itemid = API.getKey() - kodi_checksum = all_kodimoviesId.get(itemid) - all_plextvshowsIds[itemid] = itemid - kodi_checksum = all_koditvshows.get(itemid) - #if kodi_checksum != plex_checksum: - - - if all_koditvshows.get(itemid) != API.getChecksum(): - # Only update if movie is not in Kodi or checksum is different - updatelist.append(itemid) - - self.logMsg("TVShows to update for %s: %s" % (viewName, updatelist), 1) - embytvshows = emby.getFullItems(updatelist) - total = len(updatelist) - del updatelist[:] - else: - all_embytvshows = emby.getShows(viewId) - total = all_embytvshows['TotalRecordCount'] - embytvshows = all_embytvshows['Items'] - - - if pdialog: - pdialog.update(heading="Processing %s / %s items" % (viewName, total)) - - count = 0 - for embytvshow in embytvshows: - # Process individual show - if self.shouldStop(): - return False - - itemid = embytvshow['Id'] - title = embytvshow['Name'] - if pdialog: - percentage = int((float(count) / float(total))*100) - pdialog.update(percentage, message=title) - count += 1 - tvshows.add_update(embytvshow, viewName, viewId) - - if not compare: - # Process episodes - all_episodes = emby.getEpisodesbyShow(itemid) - for episode in all_episodes['Items']: - - # Process individual show - if self.shouldStop(): - return False - - episodetitle = episode['Name'] - if pdialog: - pdialog.update(percentage, message="%s - %s" % (title, episodetitle)) - tvshows.add_updateEpisode(episode) - else: - if compare: - # Get all episodes in view - if pdialog: - pdialog.update( - heading="Emby for Kodi", - message="Comparing episodes from view: %s..." % viewName) - - all_embyepisodes = emby.getEpisodes(viewId, basic=True) - for embyepisode in all_embyepisodes['Items']: - - if self.shouldStop(): - return False - - API = api.API(embyepisode) - itemid = embyepisode['Id'] - all_embyepisodesIds.add(itemid) - - if all_kodiepisodes.get(itemid) != API.getChecksum(): - # Only update if movie is not in Kodi or checksum is different - updatelist.append(itemid) - - self.logMsg("Episodes to update for %s: %s" % (viewName, updatelist), 1) - embyepisodes = emby.getFullItems(updatelist) - total = len(updatelist) - del updatelist[:] - - count = 0 - for episode in embyepisodes: - - # Process individual episode - if self.shouldStop(): - return False - - title = episode['SeriesName'] - episodetitle = episode['Name'] - if pdialog: - percentage = int((float(count) / float(total))*100) - pdialog.update(percentage, message="%s - %s" % (title, episodetitle)) - count += 1 - tvshows.add_updateEpisode(episode) - else: - self.logMsg("TVShows finished.", 2) - - ##### PROCESS DELETES ##### - if compare: - # Manual sync, process deletes - for koditvshow in all_koditvshows: - if koditvshow not in all_embytvshowsIds: - tvshows.remove(koditvshow) - else: - self.logMsg("TVShows compare finished.", 1) - - for kodiepisode in all_kodiepisodes: - if kodiepisode not in all_embyepisodesIds: - tvshows.remove(kodiepisode) - else: - self.logMsg("Episodes compare finished.", 1) + ##### PROCESS TV Episodes ##### + # Cycle through tv shows + for tvShowId in allPlexTvShowsId: + self.updatelist = [] + self.allPlexElementsId = {} + # Grab all episodes to tvshow from PMS + episodes = plx.GetAllPlexLeaves(tvShowId) + # Populate self.updatelist and self.allPlexElementsId + self.GetUpdatelist(episodes) + # Process self.updatelist + self.GetAndProcessXMLs('TVShows', + None, + None, + 'add_updateEpisode') + self.logMsg("Processed all episodes of TV show with Plex Id %s" % tvShowId, 1) return True