Plex Music!

Playing does not work yet, but Lib sync is up
This commit is contained in:
tomkat83 2016-02-12 16:53:49 +01:00
parent f162b6ea8a
commit bcbbb1b42f
6 changed files with 421 additions and 455 deletions

View file

@ -1420,8 +1420,8 @@ class API():
# localtime = time.strftime('%H:%M', date_time) # localtime = time.strftime('%H:%M', date_time)
# return localtime + ' ' + localdate # return localtime + ' ' + localdate
try: try:
DATEFORMAT = xbmc.getRegion('dateshort') # DATEFORMAT = xbmc.getRegion('dateshort')
TIMEFORMAT = xbmc.getRegion('meridiem') # TIMEFORMAT = xbmc.getRegion('meridiem')
date_time = time.localtime(float(stamp)) date_time = time.localtime(float(stamp))
localdate = time.strftime('%Y-%m-%d %H:%M:%S', date_time) localdate = time.strftime('%Y-%m-%d %H:%M:%S', date_time)
except: except:
@ -1447,7 +1447,7 @@ class API():
""" """
Returns the Plex key such as '246922' as a string Returns the Plex key such as '246922' as a string
""" """
return self.item.attrib.get('ratingKey', '') return self.item.attrib.get('ratingKey', None)
def getKey(self): def getKey(self):
""" """
@ -1491,7 +1491,7 @@ class API():
try: try:
playcount = int(item['viewCount']) playcount = int(item['viewCount'])
except KeyError: except:
playcount = None playcount = None
if playcount: if playcount:
@ -1499,9 +1499,14 @@ class API():
try: try:
lastPlayedDate = self.DateToKodi(int(item['lastViewedAt'])) lastPlayedDate = self.DateToKodi(int(item['lastViewedAt']))
except KeyError: except:
lastPlayedDate = None lastPlayedDate = None
try:
userrating = int(item['userRating'])
except:
userrating = None
resume, runtime = self.getRuntime() resume, runtime = self.getRuntime()
return { return {
'Favorite': favorite, 'Favorite': favorite,
@ -1510,7 +1515,8 @@ class API():
'LastPlayedDate': lastPlayedDate, 'LastPlayedDate': lastPlayedDate,
'Resume': resume, 'Resume': resume,
'Runtime': runtime, 'Runtime': runtime,
'Rating': rating 'Rating': rating,
'UserRating': userrating
} }
def getPeople(self): def getPeople(self):

View file

@ -3,10 +3,8 @@ from urllib import urlencode
from ast import literal_eval from ast import literal_eval
from urlparse import urlparse, parse_qs from urlparse import urlparse, parse_qs
import re import re
import json
from xbmcaddon import Addon from xbmcaddon import Addon
import xbmc
import downloadutils import downloadutils
from utils import logMsg, settings from utils import logMsg, settings
@ -35,7 +33,10 @@ def GetItemClassFromType(itemType):
'movie': 'Movies', 'movie': 'Movies',
'episodes': 'TVShows', 'episodes': 'TVShows',
'episode': 'TVShows', 'episode': 'TVShows',
'show': 'TVShows' 'show': 'TVShows',
'artist': 'Music',
'album': 'Music',
'track': 'Music'
} }
return classes[itemType] return classes[itemType]
@ -97,7 +98,10 @@ def GetMethodFromPlexType(plexType):
'movie': 'add_update', 'movie': 'add_update',
'episode': 'add_updateEpisode', 'episode': 'add_updateEpisode',
'show': 'add_update', 'show': 'add_update',
'season': 'add_updateSeason' 'season': 'add_updateSeason',
'track': 'add_updateSong',
'album': 'add_updateAlbum',
'artist': 'add_updateArtist'
} }
return methods[plexType] return methods[plexType]
@ -199,17 +203,22 @@ def GetAllPlexChildren(key):
return xml return xml
def GetPlexSectionResults(viewId, headerOptions={}): def GetPlexSectionResults(viewId, args=None):
""" """
Returns a list (XML API dump) of all Plex items in the Plex Returns a list (XML API dump) of all Plex items in the Plex
section with key = viewId. section with key = viewId.
Input:
args: optional dict to be urlencoded
Returns None if something went wrong Returns None if something went wrong
""" """
result = [] result = []
url = "{server}/library/sections/%s/all" % viewId url = "{server}/library/sections/%s/all" % viewId
result = downloadutils.DownloadUtils().downloadUrl( if args:
url, headerOptions=headerOptions) url += "?" + urlencode(args)
result = downloadutils.DownloadUtils().downloadUrl(url)
try: try:
result.tag result.tag

View file

@ -270,8 +270,6 @@ class Artwork():
def CacheTexture(self, url): def CacheTexture(self, url):
# Cache a single image url to the texture cache # Cache a single image url to the texture cache
if url and self.enableTextureCache: if url and self.enableTextureCache:
self.logMsg("Processing: %s" % url, 2)
if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None): if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None):
#Add image to texture cache by simply calling it at the http endpoint #Add image to texture cache by simply calling it at the http endpoint
@ -389,8 +387,6 @@ class Artwork():
except TypeError: # Add the artwork except TypeError: # Add the artwork
cacheimage = True cacheimage = True
self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2)
query = ( query = (
''' '''
INSERT INTO art(media_id, media_type, type, url) INSERT INTO art(media_id, media_type, type, url)
@ -410,10 +406,6 @@ class Artwork():
# Delete current entry before updating with the new one # Delete current entry before updating with the new one
self.deleteCachedArtwork(url) self.deleteCachedArtwork(url)
self.logMsg(
"Updating Art url for %s kodiId: %s (%s) -> (%s)"
% (imageType, kodiId, url, imageUrl), 1)
query = ' '.join(( query = ' '.join((
"UPDATE art", "UPDATE art",

View file

@ -19,6 +19,7 @@ import kodidb_functions as kodidb
import read_embyserver as embyserver import read_embyserver as embyserver
import musicutils as musicutils import musicutils as musicutils
import PlexAPI import PlexAPI
from PlexFunctions import GetPlexMetadata
################################################################################################## ##################################################################################################
@ -26,13 +27,15 @@ import PlexAPI
@utils.logging @utils.logging
class Items(object): class Items(object):
""" """
Items to be called with "with Items as xxx:" to ensure that __enter__ Items to be called with "with Items() as xxx:" to ensure that __enter__
method is called (opens db connections) method is called (opens db connections)
Input:
kodiType: optional argument; e.g. 'video' or 'music'
""" """
def __init__(self): def __init__(self):
self.doUtils = downloadutils.DownloadUtils() self.doUtils = downloadutils.DownloadUtils()
self.kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2]) self.kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
self.directpath = utils.settings('useDirectPaths') == "1" self.directpath = utils.settings('useDirectPaths') == "1"
self.music_enabled = utils.settings('enableMusic') == "true" self.music_enabled = utils.settings('enableMusic') == "true"
@ -283,7 +286,6 @@ class Movies(Items):
self.add_updateBoxset(boxset) self.add_updateBoxset(boxset)
def add_update(self, item, viewtag=None, viewid=None): def add_update(self, item, viewtag=None, viewid=None):
self.logMsg("Entering add_update", 1)
# Process single movie # Process single movie
kodicursor = self.kodicursor kodicursor = self.kodicursor
emby_db = self.emby_db emby_db = self.emby_db
@ -291,7 +293,8 @@ class Movies(Items):
artwork = self.artwork artwork = self.artwork
API = PlexAPI.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 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 # If the item doesn't exist, we'll add it to the database
update_item = True update_item = True
itemid = API.getRatingKey() itemid = API.getRatingKey()
@ -306,16 +309,14 @@ class Movies(Items):
pathid = emby_dbitem[2] pathid = emby_dbitem[2]
except TypeError: except TypeError:
update_item = False
self.logMsg("movieid: %s not found." % itemid, 2)
# movieid # movieid
update_item = False
kodicursor.execute("select coalesce(max(idMovie),0) from movie") kodicursor.execute("select coalesce(max(idMovie),0) from movie")
movieid = kodicursor.fetchone()[0] + 1 movieid = kodicursor.fetchone()[0] + 1
if not viewtag or not viewid: if not viewtag or not viewid:
# Get view tag from emby # Get view tag from emby
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid) viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
self.logMsg("View tag found: %s" % viewtag, 2)
# fileId information # fileId information
checksum = API.getChecksum() checksum = API.getChecksum()
@ -349,7 +350,6 @@ class Movies(Items):
studio = studios[0] studio = studios[0]
except IndexError: except IndexError:
studio = None studio = None
self.logMsg("Retrieved metadata for %s" % itemid, 2)
# Find one trailer # Find one trailer
trailer = None trailer = None
@ -470,33 +470,24 @@ class Movies(Items):
# Process cast # Process cast
people = API.getPeopleList() people = API.getPeopleList()
kodi_db.addPeople(movieid, people, "movie") kodi_db.addPeople(movieid, people, "movie")
self.logMsg('People added', 2)
# Process genres # Process genres
kodi_db.addGenres(movieid, genres, "movie") kodi_db.addGenres(movieid, genres, "movie")
self.logMsg('Genres added', 2)
# Process artwork # Process artwork
allartworks = API.getAllArtwork() allartworks = API.getAllArtwork()
self.logMsg('Artwork processed', 2)
artwork.addArtwork(allartworks, movieid, "movie", kodicursor) artwork.addArtwork(allartworks, movieid, "movie", kodicursor)
self.logMsg('Artwork added', 2)
# Process stream details # Process stream details
streams = API.getMediaStreams() streams = API.getMediaStreams()
self.logMsg('Streames processed', 2)
kodi_db.addStreams(fileid, streams, runtime) kodi_db.addStreams(fileid, streams, runtime)
self.logMsg('Streames added', 2)
# Process studios # Process studios
kodi_db.addStudios(movieid, studios, "movie") kodi_db.addStudios(movieid, studios, "movie")
self.logMsg('Studios added', 2)
# Process tags: view, emby tags # Process tags: view, emby tags
tags = [viewtag] tags = [viewtag]
# tags.extend(item['Tags']) # tags.extend(item['Tags'])
# if userdata['Favorite']: # if userdata['Favorite']:
# tags.append("Favorite movies") # tags.append("Favorite movies")
kodi_db.addTags(movieid, tags, "movie") kodi_db.addTags(movieid, tags, "movie")
self.logMsg('Tags added', 2)
# Process playstates # Process playstates
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed) kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
self.logMsg('Done processing %s' % itemid, 2)
def remove(self, itemid): def remove(self, itemid):
# Remove movieid, fileid, emby reference # Remove movieid, fileid, emby reference
@ -900,11 +891,6 @@ class TVShows(Items):
artwork = self.artwork artwork = self.artwork
API = PlexAPI.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 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 update_item = True
itemid = API.getRatingKey() itemid = API.getRatingKey()
if not itemid: if not itemid:
@ -917,8 +903,6 @@ class TVShows(Items):
except TypeError: except TypeError:
update_item = False update_item = False
self.logMsg("View tag found: %s" % viewtag, 2)
# fileId information # fileId information
checksum = API.getChecksum() checksum = API.getChecksum()
@ -937,7 +921,7 @@ class TVShows(Items):
except IndexError: except IndexError:
studio = None studio = None
##### GET THE FILE AND PATH ##### # GET THE FILE AND PATH #####
playurl = API.getKey() playurl = API.getKey()
if self.directpath: if self.directpath:
@ -971,8 +955,7 @@ class TVShows(Items):
toplevelpath = "plugin://plugin.video.plexkodiconnect.tvshows/" toplevelpath = "plugin://plugin.video.plexkodiconnect.tvshows/"
path = "%s%s/" % (toplevelpath, itemid) path = "%s%s/" % (toplevelpath, itemid)
# UPDATE THE TVSHOW #####
##### UPDATE THE TVSHOW #####
if update_item: if update_item:
self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1) self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
@ -1066,6 +1049,9 @@ class TVShows(Items):
API = PlexAPI.API(item) API = PlexAPI.API(item)
showid = viewid showid = viewid
itemid = API.getRatingKey() itemid = API.getRatingKey()
if not itemid:
self.logMsg('Error getting itemid for season, skipping', -1)
return
kodicursor = self.kodicursor kodicursor = self.kodicursor
emby_db = self.emby_db emby_db = self.emby_db
kodi_db = self.kodi_db kodi_db = self.kodi_db
@ -1078,16 +1064,12 @@ class TVShows(Items):
emby_dbitem = emby_db.getItem_byId(itemid) emby_dbitem = emby_db.getItem_byId(itemid)
try: try:
embyDbItemId = emby_dbitem[0] embyDbItemId = emby_dbitem[0]
self.logMsg("Updating Season: %s" % itemid, 2)
except TypeError: except TypeError:
update_item = False update_item = False
self.logMsg("Season: %s not found." % itemid, 2)
# Process artwork # Process artwork
allartworks = API.getAllArtwork() allartworks = API.getAllArtwork()
artwork.addArtwork(allartworks, seasonid, "season", kodicursor) artwork.addArtwork(allartworks, seasonid, "season", kodicursor)
self.logMsg("Updated season %s, Plex Id: %s of Plex show Id: %s" % (
seasonnum, itemid, showid), 2)
if update_item: if update_item:
# Update a reference: checksum in emby table # Update a reference: checksum in emby table
@ -1108,20 +1090,21 @@ class TVShows(Items):
artwork = self.artwork artwork = self.artwork
API = PlexAPI.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 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 # If the item doesn't exist, we'll add it to the database
update_item = True update_item = True
itemid = API.getRatingKey() itemid = API.getRatingKey()
if not itemid:
self.logMsg('Error getting itemid for episode, skipping', -1)
return
emby_dbitem = emby_db.getItem_byId(itemid) emby_dbitem = emby_db.getItem_byId(itemid)
self.logMsg("Processing episode with Plex Id: %s" % itemid, 2)
try: try:
episodeid = emby_dbitem[0] episodeid = emby_dbitem[0]
fileid = emby_dbitem[1] fileid = emby_dbitem[1]
pathid = emby_dbitem[2] pathid = emby_dbitem[2]
except TypeError: except TypeError:
update_item = False update_item = False
self.logMsg("episodeid: %s not found." % itemid, 2)
# episodeid # episodeid
kodicursor.execute("select coalesce(max(idEpisode),0) from episode") kodicursor.execute("select coalesce(max(idEpisode),0) from episode")
episodeid = kodicursor.fetchone()[0] + 1 episodeid = kodicursor.fetchone()[0] + 1
@ -1145,12 +1128,8 @@ class TVShows(Items):
resume, runtime = API.getRuntime() resume, runtime = API.getRuntime()
premieredate = API.getPremiereDate() premieredate = API.getPremiereDate()
self.logMsg("Retrieved metadata for %s" % itemid, 2)
# episode details # episode details
seriesId, seriesName, season, episode = API.getEpisodeDetails() seriesId, seriesName, season, episode = API.getEpisodeDetails()
self.logMsg("Got episode details: %s %s: s%se%s"
% (seriesId, seriesName, season, episode), 2)
if season is None: if season is None:
if item.get('AbsoluteEpisodeNumber'): if item.get('AbsoluteEpisodeNumber'):
@ -1190,11 +1169,9 @@ class TVShows(Items):
# self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId), -1) # self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId), -1)
self.logMsg("Parent tvshow now found, skip item", 2) self.logMsg("Parent tvshow now found, skip item", 2)
return False return False
self.logMsg("showid: %s" % showid, 2)
seasonid = kodi_db.addSeason(showid, season) seasonid = kodi_db.addSeason(showid, season)
self.logMsg("seasonid: %s" % seasonid, 2)
##### GET THE FILE AND PATH ##### # GET THE FILE AND PATH #####
playurl = API.getKey() playurl = API.getKey()
filename = playurl filename = playurl
@ -1235,7 +1212,7 @@ class TVShows(Items):
} }
filename = "%s?%s" % (path, urllib.urlencode(params)) filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE EPISODE ##### # UPDATE THE EPISODE #####
if update_item: if update_item:
self.logMsg("UPDATE episode itemid: %s" % (itemid), 1) self.logMsg("UPDATE episode itemid: %s" % (itemid), 1)
@ -1484,12 +1461,11 @@ class TVShows(Items):
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,)) kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
self.logMsg("Removed episode: %s." % kodiid, 2) self.logMsg("Removed episode: %s." % kodiid, 2)
class Music(Items): class Music(Items):
def __init__(self):
def __init__(self, embycursor, musiccursor): Items.__init__(self)
Items.__init__(self, embycursor, musiccursor)
self.directstream = utils.settings('streamMusic') == "true" self.directstream = utils.settings('streamMusic') == "true"
self.enableimportsongrating = utils.settings('enableImportSongRating') == "true" self.enableimportsongrating = utils.settings('enableImportSongRating') == "true"
@ -1498,6 +1474,20 @@ class Music(Items):
self.userid = utils.window('emby_currUser') self.userid = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userid) self.server = utils.window('emby_server%s' % self.userid)
def __enter__(self):
"""
OVERWRITE this method, because we need to open another DB.
Open DB connections and cursors
"""
self.embyconn = utils.kodiSQL('emby')
self.embycursor = self.embyconn.cursor()
# Here it is, not 'video' but 'music'
self.kodiconn = utils.kodiSQL('music')
self.kodicursor = self.kodiconn.cursor()
self.emby_db = embydb.Embydb_Functions(self.embycursor)
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
return self
def added(self, items, pdialog): def added(self, items, pdialog):
total = len(items) total = len(items)
@ -1545,36 +1535,35 @@ class Music(Items):
if not pdialog and self.contentmsg: if not pdialog and self.contentmsg:
self.contentPop(title, self.newmusic_time) self.contentPop(title, self.newmusic_time)
def add_updateArtist(self, item, artisttype="MusicArtist"): def add_updateArtist(self, item, viewtag=None, viewid=None,
# Process a single artist artisttype="MusicArtist"):
kodiversion = self.kodiversion
kodicursor = self.kodicursor kodicursor = self.kodicursor
emby_db = self.emby_db emby_db = self.emby_db
kodi_db = self.kodi_db kodi_db = self.kodi_db
artwork = self.artwork artwork = self.artwork
API = api.API(item) API = PlexAPI.API(item)
update_item = True update_item = True
itemid = item['Id'] itemid = API.getRatingKey()
emby_dbitem = emby_db.getItem_byId(itemid) emby_dbitem = emby_db.getItem_byId(itemid)
try: try:
artistid = emby_dbitem[0] artistid = emby_dbitem[0]
except TypeError: except TypeError:
update_item = False update_item = False
self.logMsg("artistid: %s not found." % itemid, 2)
##### The artist details ##### # The artist details #####
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S') lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
dateadded = API.getDateCreated() dateadded = API.getDateCreated()
checksum = API.getChecksum() checksum = API.getChecksum()
name = item['Name'] name, sortname = API.getTitle()
musicBrainzId = API.getProvider('MusicBrainzArtist') # musicBrainzId = API.getProvider('MusicBrainzArtist')
genres = " / ".join(item.get('Genres')) musicBrainzId = None
bio = API.getOverview() genres = API.joinList(API.getGenres())
bio = API.getPlot()
# Associate artwork # Associate artwork
artworks = artwork.getAllArtwork(item, parentInfo=True) artworks = API.getAllArtwork(parentInfo=True)
thumb = artworks['Primary'] thumb = artworks['Primary']
backdrops = artworks['Backdrop'] # List backdrops = artworks['Backdrop'] # List
@ -1585,22 +1574,24 @@ class Music(Items):
else: else:
fanart = "" fanart = ""
# UPDATE THE ARTIST #####
##### UPDATE THE ARTIST #####
if update_item: if update_item:
self.logMsg("UPDATE artist itemid: %s - Name: %s" % (itemid, name), 1) self.logMsg("UPDATE artist itemid: %s - Name: %s"
% (itemid, name), 1)
# Update the checksum in emby table # Update the checksum in emby table
emby_db.updateReference(itemid, checksum) emby_db.updateReference(itemid, checksum)
##### OR ADD THE ARTIST ##### # OR ADD THE ARTIST #####
else: else:
self.logMsg("ADD artist itemid: %s - Name: %s" % (itemid, name), 1) self.logMsg("ADD artist itemid: %s - Name: %s" % (itemid, name), 1)
# safety checks: It looks like Emby supports the same artist multiple times. # safety checks: It looks like Emby supports the same artist
# Kodi doesn't allow that. In case that happens we just merge the artist entries. # multiple times.
# Kodi doesn't allow that. In case that happens we just merge the
# artist entries.
artistid = kodi_db.addArtist(name, musicBrainzId) artistid = kodi_db.addArtist(name, musicBrainzId)
# Create the reference in emby table # Create the reference in emby table
emby_db.addReference(itemid, artistid, artisttype, "artist", checksum=checksum) emby_db.addReference(
itemid, artistid, artisttype, "artist", checksum=checksum)
# Process the artist # Process the artist
if self.kodiversion in (16, 17): if self.kodiversion in (16, 17):
@ -1611,7 +1602,8 @@ class Music(Items):
"lastScraped = ?", "lastScraped = ?",
"WHERE idArtist = ?" "WHERE idArtist = ?"
)) ))
kodicursor.execute(query, (genres, bio, thumb, fanart, lastScraped, artistid)) kodicursor.execute(query, (genres, bio, thumb, fanart,
lastScraped, artistid))
else: else:
query = ' '.join(( query = ' '.join((
@ -1623,71 +1615,77 @@ class Music(Items):
kodicursor.execute(query, (genres, bio, thumb, fanart, lastScraped, kodicursor.execute(query, (genres, bio, thumb, fanart, lastScraped,
dateadded, artistid)) dateadded, artistid))
# Update artwork # Update artwork
artwork.addArtwork(artworks, artistid, "artist", kodicursor) artwork.addArtwork(artworks, artistid, "artist", kodicursor)
def add_updateAlbum(self, item): def add_updateAlbum(self, item, viewtag=None, viewid=None):
# Process a single artist
emby = self.emby
kodiversion = self.kodiversion kodiversion = self.kodiversion
kodicursor = self.kodicursor kodicursor = self.kodicursor
emby_db = self.emby_db emby_db = self.emby_db
kodi_db = self.kodi_db kodi_db = self.kodi_db
artwork = self.artwork artwork = self.artwork
API = api.API(item) API = PlexAPI.API(item)
update_item = True update_item = True
itemid = item['Id'] itemid = API.getRatingKey()
if not itemid:
self.logMsg('Error processing Album, skipping', -1)
return
emby_dbitem = emby_db.getItem_byId(itemid) emby_dbitem = emby_db.getItem_byId(itemid)
try: try:
albumid = emby_dbitem[0] albumid = emby_dbitem[0]
except TypeError: except TypeError:
# Albumid not found
update_item = False update_item = False
self.logMsg("albumid: %s not found." % itemid, 2)
##### The album details ##### # The album details #####
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S') lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
dateadded = API.getDateCreated() dateadded = API.getDateCreated()
userdata = API.getUserData() userdata = API.getUserData()
checksum = API.getChecksum() checksum = API.getChecksum()
name = item['Name'] name, sorttitle = API.getTitle()
musicBrainzId = API.getProvider('MusicBrainzAlbum') # musicBrainzId = API.getProvider('MusicBrainzAlbum')
year = item.get('ProductionYear') musicBrainzId = None
genres = item.get('Genres') year = API.getYear()
genre = " / ".join(genres) genres = API.getGenres()
bio = API.getOverview() genre = API.joinList(genres)
bio = API.getPlot()
rating = userdata['UserRating'] rating = userdata['UserRating']
artists = item['AlbumArtists'] # artists = item['AlbumArtists']
if not artists: # if not artists:
artists = item['ArtistItems'] # artists = item['ArtistItems']
artistname = [] # artistname = []
for artist in artists: # for artist in artists:
artistname.append(artist['Name']) # artistname.append(artist['Name'])
artistname = " / ".join(artistname) artistname = item.attrib.get('parentTitle')
if not artistname:
artistname = item.attrib.get('originalTitle')
# Associate artwork # Associate artwork
artworks = artwork.getAllArtwork(item, parentInfo=True) artworks = API.getAllArtwork(parentInfo=True)
thumb = artworks['Primary'] thumb = artworks['Primary']
if thumb: if thumb:
thumb = "<thumb>%s</thumb>" % thumb thumb = "<thumb>%s</thumb>" % thumb
##### UPDATE THE ALBUM ##### # UPDATE THE ALBUM #####
if update_item: if update_item:
self.logMsg("UPDATE album itemid: %s - Name: %s" % (itemid, name), 1) self.logMsg("UPDATE album itemid: %s - Name: %s"
% (itemid, name), 1)
# Update the checksum in emby table # Update the checksum in emby table
emby_db.updateReference(itemid, checksum) emby_db.updateReference(itemid, checksum)
##### OR ADD THE ALBUM ##### # OR ADD THE ALBUM #####
else: else:
self.logMsg("ADD album itemid: %s - Name: %s" % (itemid, name), 1) self.logMsg("ADD album itemid: %s - Name: %s" % (itemid, name), 1)
# safety checks: It looks like Emby supports the same artist multiple times. # safety checks: It looks like Emby supports the same artist
# Kodi doesn't allow that. In case that happens we just merge the artist entries. # multiple times.
# Kodi doesn't allow that. In case that happens we just merge the
# artist entries.
albumid = kodi_db.addAlbum(name, musicBrainzId) albumid = kodi_db.addAlbum(name, musicBrainzId)
# Create the reference in emby table # Create the reference in emby table
emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum) emby_db.addReference(
itemid, albumid, "MusicAlbum", "album", checksum=checksum)
# Process the album info # Process the album info
if kodiversion == 17: if kodiversion == 17:
@ -1699,8 +1697,8 @@ class Music(Items):
"iUserrating = ?, lastScraped = ?, strReleaseType = ?", "iUserrating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?" "WHERE idAlbum = ?"
)) ))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped, kodicursor.execute(query, (artistname, year, genre, bio, thumb,
"album", albumid)) rating, lastScraped, "album", albumid))
elif kodiversion == 16: elif kodiversion == 16:
# Kodi Jarvis # Kodi Jarvis
query = ' '.join(( query = ' '.join((
@ -1710,8 +1708,8 @@ class Music(Items):
"iRating = ?, lastScraped = ?, strReleaseType = ?", "iRating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?" "WHERE idAlbum = ?"
)) ))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped, kodicursor.execute(query, (artistname, year, genre, bio, thumb,
"album", albumid)) rating, lastScraped, "album", albumid))
elif kodiversion == 15: elif kodiversion == 15:
# Kodi Isengard # Kodi Isengard
query = ' '.join(( query = ' '.join((
@ -1721,8 +1719,9 @@ class Music(Items):
"iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?", "iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?",
"WHERE idAlbum = ?" "WHERE idAlbum = ?"
)) ))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped, kodicursor.execute(query, (artistname, year, genre, bio, thumb,
dateadded, "album", albumid)) rating, lastScraped, dateadded,
"album", albumid))
else: else:
# Kodi Helix # Kodi Helix
query = ' '.join(( query = ' '.join((
@ -1732,44 +1731,52 @@ class Music(Items):
"iRating = ?, lastScraped = ?, dateAdded = ?", "iRating = ?, lastScraped = ?, dateAdded = ?",
"WHERE idAlbum = ?" "WHERE idAlbum = ?"
)) ))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped, kodicursor.execute(query, (artistname, year, genre, bio, thumb,
dateadded, albumid)) rating, lastScraped, dateadded,
albumid))
# Associate the parentid for emby reference # Associate the parentid for emby reference
parentId = item.get('ParentId') parentId = item.attrib.get('parentRatingKey')
if parentId is not None: if parentId is not None:
emby_dbartist = emby_db.getItem_byId(parentId) emby_dbartist = emby_db.getItem_byId(parentId)
try: try:
artistid = emby_dbartist[0] artistid = emby_dbartist[0]
except TypeError: except TypeError:
# Artist does not exist in emby database. self.logMsg('Artist %s does not exist in emby database'
artist = emby.getItem(parentId) % parentId, 1)
artist = GetPlexMetadata(parentId)
# Item may not be an artist, verification necessary. # Item may not be an artist, verification necessary.
if artist['Type'] == "MusicArtist": if artist:
if artist[0].attrib.get('type') == "artist":
# Update with the parentId, for remove reference # Update with the parentId, for remove reference
emby_db.addReference(parentId, parentId, "MusicArtist", "artist") emby_db.addReference(
parentId, parentId, "MusicArtist", "artist")
emby_db.updateParentId(itemid, parentId) emby_db.updateParentId(itemid, parentId)
else: else:
# Update emby reference with the artistid # Update emby reference with the artistid
emby_db.updateParentId(itemid, artistid) emby_db.updateParentId(itemid, artistid)
# Assign main artists to album # Assign main artists to album
for artist in artists: # Plex unfortunately only supports 1 artist :-(
artistname = artist['Name'] artistId = parentId
artistId = artist['Id']
emby_dbartist = emby_db.getItem_byId(artistId) emby_dbartist = emby_db.getItem_byId(artistId)
try: try:
artistid = emby_dbartist[0] artistid = emby_dbartist[0]
except TypeError: except TypeError:
# Artist does not exist in emby database, create the reference # Artist does not exist in emby database, create the reference
artist = emby.getItem(artistId) self.logMsg('Artist %s does not exist in emby database'
self.add_updateArtist(artist, artisttype="AlbumArtist") % artistId, 1)
artist = GetPlexMetadata(artistId)
if artist:
self.add_updateArtist(artist[0], artisttype="AlbumArtist")
emby_dbartist = emby_db.getItem_byId(artistId) emby_dbartist = emby_db.getItem_byId(artistId)
artistid = emby_dbartist[0] artistid = emby_dbartist[0]
else: else:
# Best take this name over anything else. # Best take this name over anything else.
query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?" query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?"
kodicursor.execute(query, (artistname, artistid,)) kodicursor.execute(query, (artistname, artistid,))
self.logMsg("UPDATE artist: strArtist: %s, idArtist: %s"
% (artistname, artistid), 1)
# Add artist to album # Add artist to album
query = ( query = (
@ -1791,33 +1798,37 @@ class Music(Items):
kodicursor.execute(query, (artistid, name, year)) kodicursor.execute(query, (artistid, name, year))
# Update emby reference with parentid # Update emby reference with parentid
emby_db.updateParentId(artistId, albumid) emby_db.updateParentId(artistId, albumid)
# Add genres # Add genres
kodi_db.addMusicGenres(albumid, genres, "album") kodi_db.addMusicGenres(albumid, genres, "album")
# Update artwork # Update artwork
artwork.addArtwork(artworks, albumid, "album", kodicursor) artwork.addArtwork(artworks, albumid, "album", kodicursor)
def add_updateSong(self, item): def add_updateSong(self, item, viewtag=None, viewid=None):
# Process single song # Process single song
kodiversion = self.kodiversion kodiversion = self.kodiversion
kodicursor = self.kodicursor kodicursor = self.kodicursor
emby_db = self.emby_db emby_db = self.emby_db
kodi_db = self.kodi_db kodi_db = self.kodi_db
artwork = self.artwork artwork = self.artwork
API = api.API(item) API = PlexAPI.API(item)
update_item = True update_item = True
itemid = item['Id'] itemid = API.getRatingKey()
if not itemid:
self.logMsg('Error processing Song; skipping', -1)
return
emby_dbitem = emby_db.getItem_byId(itemid) emby_dbitem = emby_db.getItem_byId(itemid)
try: try:
songid = emby_dbitem[0] songid = emby_dbitem[0]
pathid = emby_dbitem[2] pathid = emby_dbitem[2]
albumid = emby_dbitem[3] albumid = emby_dbitem[3]
except TypeError: except TypeError:
# Songid not found
update_item = False update_item = False
self.logMsg("songid: %s not found." % itemid, 2) kodicursor.execute("select coalesce(max(idSong),0) from song")
songid = kodicursor.fetchone()[0] + 1
##### The song details ##### # The song details #####
checksum = API.getChecksum() checksum = API.getChecksum()
dateadded = API.getDateCreated() dateadded = API.getDateCreated()
userdata = API.getUserData() userdata = API.getUserData()
@ -1825,85 +1836,65 @@ class Music(Items):
dateplayed = userdata['LastPlayedDate'] dateplayed = userdata['LastPlayedDate']
# item details # item details
title = item['Name'] title, sorttitle = API.getTitle()
musicBrainzId = API.getProvider('MusicBrainzTrackId') # musicBrainzId = API.getProvider('MusicBrainzTrackId')
genres = item.get('Genres') musicBrainzId = None
genre = " / ".join(genres) genres = API.getGenres()
artists = " / ".join(item['Artists']) genre = API.joinList(genres)
tracknumber = item.get('IndexNumber', 0) artists = item.attrib.get('grandparentTitle')
disc = item.get('ParentIndexNumber', 1) tracknumber = int(item.attrib.get('index', 0))
disc = int(item.attrib.get('parentIndex', 1))
if disc == 1: if disc == 1:
track = tracknumber track = tracknumber
else: else:
track = disc*2**16 + tracknumber track = disc*2**16 + tracknumber
year = item.get('ProductionYear') year = API.getYear()
duration = API.getRuntime() resume, duration = API.getRuntime()
rating = userdata['UserRating'] rating = userdata['UserRating']
#if enabled, try to get the rating from file and/or emby comment = None
if not self.directstream:
rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
else:
hasEmbeddedCover = False
comment = API.getOverview()
# Plex works a bit differently
##### GET THE FILE AND PATH #####
if self.directstream: if self.directstream:
path = "%s/emby/Audio/%s/" % (self.server, itemid) paths = "%s%s" % (self.server, item[0][0][0].attrib.get('key'))
filename = "stream.mp3" paths = paths.rsplit('/', 1)
path = paths[0]
filename = paths[1]
else: else:
playurl = API.getKey() path = "plugin://plugin.video.plexkodiconnect.movies/"
filename = API.getKey()
params = {
'filename': filename,
'id': itemid,
'dbid': songid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
if "\\" in playurl: # UPDATE THE SONG #####
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
# Direct paths is set the Kodi way
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(playurl):
# Validate the path is correct with user intervention
utils.window('emby_directPath', clear=True)
resp = xbmcgui.Dialog().yesno(
heading="Can't validate path",
line1=(
"Kodi can't locate file: %s. Verify the path. "
"You may to verify your network credentials in the "
"add-on settings or use the emby path substitution "
"to format your path correctly. Stop syncing?"
% playurl))
if resp:
utils.window('emby_shouldStop', value="true")
return False
path = playurl.replace(filename, "")
utils.window('emby_pathverified', value="true")
##### UPDATE THE SONG #####
if update_item: if update_item:
self.logMsg("UPDATE song itemid: %s - Title: %s" % (itemid, title), 1) self.logMsg("UPDATE song itemid: %s - Title: %s"
% (itemid, title), 1)
# Update path # Update path
query = "UPDATE path SET strPath = ? WHERE idPath = ?" query = "UPDATE path SET strPath = ? WHERE idPath = ?"
kodicursor.execute(query, (path, pathid)) kodicursor.execute(query, (path, pathid))
# Update the song entry # Update the song entry
query = ' '.join(( query = ' '.join((
"UPDATE song", "UPDATE song",
"SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,", "SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,", "iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
"rating = ?, comment = ?", "rating = ?, comment = ?",
"WHERE idSong = ?" "WHERE idSong = ?"
)) ))
kodicursor.execute(query, (albumid, artists, genre, title, track, duration, year, kodicursor.execute(query, (albumid, artists, genre, title, track,
filename, playcount, dateplayed, rating, comment, songid)) duration, year, filename, playcount,
dateplayed, rating, comment, songid))
# Update the checksum in emby table # Update the checksum in emby table
emby_db.updateReference(itemid, checksum) emby_db.updateReference(itemid, checksum)
##### OR ADD THE SONG ##### # OR ADD THE SONG #####
else: else:
self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1) self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1)
@ -1912,7 +1903,8 @@ class Music(Items):
try: try:
# Get the album # Get the album
emby_dbalbum = emby_db.getItem_byId(item['AlbumId']) emby_dbalbum = emby_db.getItem_byId(
item.attrib.get('parentRatingKey'))
albumid = emby_dbalbum[0] albumid = emby_dbalbum[0]
except KeyError: except KeyError:
# No album Id associated to the song. # No album Id associated to the song.
@ -1921,8 +1913,8 @@ class Music(Items):
except TypeError: except TypeError:
# No album found. Let's create it # No album found. Let's create it
self.logMsg("Album database entry missing.", 1) self.logMsg("Album database entry missing.", 1)
emby_albumId = item['AlbumId'] emby_albumId = item.attrib.get('parentRatingKey')
album = self.emby.getItem(emby_albumId) album = GetPlexMetadata(emby_albumId)
self.add_updateAlbum(album) self.add_updateAlbum(album)
emby_dbalbum = emby_db.getItem_byId(emby_albumId) emby_dbalbum = emby_db.getItem_byId(emby_albumId)
try: try:
@ -1965,8 +1957,6 @@ class Music(Items):
kodicursor.execute(query, (albumid, genre, year, dateadded)) kodicursor.execute(query, (albumid, genre, year, dateadded))
# Create the song entry # Create the song entry
kodicursor.execute("select coalesce(max(idSong),0) from song")
songid = kodicursor.fetchone()[0] + 1
query = ( query = (
''' '''
INSERT INTO song( INSERT INTO song(
@ -1977,14 +1967,17 @@ class Music(Items):
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''' '''
) )
kodicursor.execute(query, (songid, albumid, pathid, artists, genre, title, track, kodicursor.execute(
duration, year, filename, musicBrainzId, playcount, dateplayed, rating)) query, (songid, albumid, pathid, artists, genre, title, track,
duration, year, filename, musicBrainzId, playcount,
dateplayed, rating))
# Create the reference in emby table # Create the reference in emby table
emby_db.addReference(itemid, songid, "Audio", "song", pathid=pathid, parentid=albumid, emby_db.addReference(
itemid, songid, "Audio", "song", pathid=pathid,
parentid=albumid,
checksum=checksum) checksum=checksum)
# Link song to album # Link song to album
query = ( query = (
''' '''
@ -1995,7 +1988,6 @@ class Music(Items):
''' '''
) )
kodicursor.execute(query, (songid, albumid, track, title, duration)) kodicursor.execute(query, (songid, albumid, track, title, duration))
# Verify if album has artists # Verify if album has artists
addArtist = False addArtist = False
query = ' '.join(( query = ' '.join((
@ -2009,20 +2001,19 @@ class Music(Items):
if result and result[0] == "": if result and result[0] == "":
addArtist = True addArtist = True
if item['AlbumArtists']: # if item['AlbumArtists']:
album_artists = item['AlbumArtists'] # album_artists = item['AlbumArtists']
else: # else:
album_artists = item['ArtistItems'] # album_artists = item['ArtistItems']
# Link song to artist # Link song to artist
artists_name = [] artist_name = item.attrib.get('grandparentTitle')
for artist in album_artists: emby_dbartist = emby_db.getItem_byId(
artist_name = artist['Name'] item.attrib.get('grandparentRatingKey'))
artists_name.append(artist_name)
emby_dbartist = emby_db.getItem_byId(artist['Id'])
try: try:
artistid = emby_dbartist[0] artistid = emby_dbartist[0]
except: pass except:
pass
else: else:
query = ( query = (
''' '''
@ -2042,79 +2033,27 @@ class Music(Items):
''' '''
) )
kodicursor.execute(query, (artistid, albumid, artist_name)) kodicursor.execute(query, (artistid, albumid, artist_name))
else:
if addArtist: if addArtist:
artists_onalbum = " / ".join(artists_name)
if kodiversion in (16, 17): if kodiversion in (16, 17):
# Kodi Jarvis, Krypton # Kodi Jarvis, Krypton
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?" query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
kodicursor.execute(query, (artists_onalbum, albumid)) kodicursor.execute(query, (artist_name, albumid))
elif kodiversion == 15: elif kodiversion == 15:
# Kodi Isengard # Kodi Isengard
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?" query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
kodicursor.execute(query, (artists_onalbum, albumid)) kodicursor.execute(query, (artist_name, albumid))
else: else:
# Kodi Helix # Kodi Helix
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?" query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
kodicursor.execute(query, (artists_onalbum, albumid)) kodicursor.execute(query, (artist_name, albumid))
# Add genres # Add genres
kodi_db.addMusicGenres(songid, genres, "song") kodi_db.addMusicGenres(songid, genres, "song")
# Update artwork # Update artwork
allart = artwork.getAllArtwork(item, parentInfo=True) allart = API.getAllArtwork(parentInfo=True)
if hasEmbeddedCover:
allart["Primary"] = "image://music@" + artwork.single_urlencode( playurl )
artwork.addArtwork(allart, songid, "song", kodicursor) artwork.addArtwork(allart, songid, "song", kodicursor)
def updateUserdata(self, item):
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
# Poster with progress bar
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
API = api.API(item)
# Get emby information
itemid = item['Id']
checksum = API.getChecksum()
userdata = API.getUserData()
runtime = API.getRuntime()
rating = userdata['UserRating']
# Get Kodi information
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
mediatype = emby_dbitem[4]
self.logMsg("Update playstate for %s: %s" % (mediatype, item['Name']), 1)
except TypeError:
return
if mediatype == "song":
#should we ignore this item ?
#happens when userdata updated by ratings method
if utils.window("ignore-update-%s" %itemid):
utils.window("ignore-update-%s" %itemid,clear=True)
return
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
#process item ratings
rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ?, rating = ? WHERE idSong = ?"
kodicursor.execute(query, (playcount, dateplayed, rating, kodiid))
elif mediatype == "album":
# Process playstates
query = "UPDATE album SET iRating = ? WHERE idAlbum = ?"
kodicursor.execute(query, (rating, kodiid))
emby_db.updateReference(itemid, checksum)
def remove(self, itemid): def remove(self, itemid):
# Remove kodiid, fileid, pathid, emby reference # Remove kodiid, fileid, pathid, emby reference
emby_db = self.emby_db emby_db = self.emby_db

View file

@ -740,7 +740,6 @@ class Kodidb_Functions():
cursor.execute(query, (kodiid, mediatype)) cursor.execute(query, (kodiid, mediatype))
# Add tags # Add tags
self.logMsg("Adding Tags: %s" % tags, 2)
for tag in tags: for tag in tags:
self.addTag(kodiid, tag, mediatype) self.addTag(kodiid, tag, mediatype)

View file

@ -20,7 +20,6 @@ import read_embyserver as embyserver
import userclient import userclient
import videonodes import videonodes
import PlexAPI
import PlexFunctions import PlexFunctions
############################################################################### ###############################################################################
@ -125,6 +124,11 @@ class ThreadedProcessMetadata(Thread):
with lock: with lock:
# Get the one child entry in the xml and process # Get the one child entry in the xml and process
for child in plexitem: for child in plexitem:
if method == 'add_updateAlbum':
item.add_updateAlbum(child,
viewtag=viewName,
viewid=viewId)
else:
itemSubFkt(child, itemSubFkt(child,
viewtag=viewName, viewtag=viewName,
viewid=viewId) viewid=viewId)
@ -219,6 +223,8 @@ class LibrarySync(Thread):
utils.settings('SyncInstallRunDone') == 'true' else False utils.settings('SyncInstallRunDone') == 'true' else False
self.showDbSync = True if \ self.showDbSync = True if \
utils.settings('dbSyncIndicator') == 'true' else False utils.settings('dbSyncIndicator') == 'true' else False
self.enableMusic = True if utils.settings('enableMusic') == "true" \
else False
Thread.__init__(self) Thread.__init__(self)
@ -250,6 +256,13 @@ class LibrarySync(Thread):
Using /library/recentlyAdded is NOT working as changes to lib items are Using /library/recentlyAdded is NOT working as changes to lib items are
not reflected not reflected
This will NOT remove items from Kodi db that were removed from the PMS
(happens only during fullsync)
Currently, ALL items returned by the PMS (because they've just been
edited by the PMS or have been watched) will be processed. This will
probably happen several times.
""" """
self.compare = True self.compare = True
# Get last sync time # Get last sync time
@ -262,29 +275,22 @@ class LibrarySync(Thread):
# Set new timestamp NOW because sync might take a while # Set new timestamp NOW because sync might take a while
self.saveLastSync() self.saveLastSync()
# Get all PMS items already saved in Kodi # Original idea: Get all PMS items already saved in Kodi
# Also get checksums of every Plex items already saved in Kodi # Also get checksums of every Plex items already saved in Kodi
allKodiElementsId = {} # NEW idea: process every item returned by the PMS
with embydb.GetEmbyDB() as emby_db: self.allKodiElementsId = {}
for itemtype in PlexFunctions.EmbyItemtypes():
try:
allKodiElementsId.update(
dict(emby_db.getChecksum(itemtype)))
except ValueError:
pass
self.allKodiElementsId = allKodiElementsId
# Run through views and get latest changed elements using time diff # Run through views and get latest changed elements using time diff
self.updatelist = []
self.allPlexElementsId = {}
self.updateKodiVideoLib = False self.updateKodiVideoLib = False
self.updateKodiMusicLib = False
for view in self.views: for view in self.views:
self.updatelist = []
if self.threadStopped(): if self.threadStopped():
return True return True
# Get items per view # Get items per view
items = PlexFunctions.GetAllPlexLeaves( items = PlexFunctions.GetAllPlexLeaves(
view['id'], updatedAt=lastSync) view['id'], updatedAt=lastSync)
# Just skip item if something went wrong
if not items: if not items:
continue continue
# Get one itemtype, because they're the same in the PMS section # Get one itemtype, because they're the same in the PMS section
@ -299,21 +305,28 @@ class LibrarySync(Thread):
if self.updatelist: if self.updatelist:
if self.updatelist[0]['itemType'] in ['Movies', 'TVShows']: if self.updatelist[0]['itemType'] in ['Movies', 'TVShows']:
self.updateKodiVideoLib = True self.updateKodiVideoLib = True
elif self.updatelist[0]['itemType'] == 'Music':
self.updateKodiMusicLib = True
self.GetAndProcessXMLs( self.GetAndProcessXMLs(
PlexFunctions.GetItemClassFromType(plexType)) PlexFunctions.GetItemClassFromType(plexType))
self.updatelist = [] self.updatelist = []
# Update userdata # Update userdata
for view in self.views: for view in self.views:
self.PlexUpdateWatched( self.PlexUpdateWatched(
view['id'], view['id'],
PlexFunctions.GetItemClassFromType(view['itemtype']), PlexFunctions.GetItemClassFromType(view['itemtype']),
lastViewedAt=lastSync) lastViewedAt=lastSync)
# Let Kodi update the library now (artwork and userdata) # Let Kodi update the library now (artwork and userdata)
if self.updateKodiVideoLib: if self.updateKodiVideoLib:
self.logMsg("Doing Kodi Video Lib update", 2) self.logMsg("Doing Kodi Video Lib update", 2)
xbmc.executebuiltin('UpdateLibrary(video)') xbmc.executebuiltin('UpdateLibrary(video)')
if self.updateKodiMusicLib:
self.logMsg("Doing Kodi Music Lib update", 2)
xbmc.executebuiltin('UpdateLibrary(music)')
# Reset and return # Reset and return
self.allKodiElementsId = {}
self.allPlexElementsId = {} self.allPlexElementsId = {}
return True return True
@ -346,7 +359,6 @@ class LibrarySync(Thread):
def fullSync(self, manualrun=False, repair=False): def fullSync(self, manualrun=False, repair=False):
# Only run once when first setting up. Can be run manually. # Only run once when first setting up. Can be run manually.
self.compare = manualrun or repair self.compare = manualrun or repair
music_enabled = utils.settings('enableMusic') == "true"
# Add sources # Add sources
utils.sourcesXML() utils.sourcesXML()
@ -367,18 +379,12 @@ class LibrarySync(Thread):
# Set views # Set views
self.maintainViews() self.maintainViews()
# Sync video library
# process = {
# 'movies': self.movies,
# 'musicvideos': self.musicvideos,
# 'tvshows': self.tvshows
# }
process = { process = {
'movies': self.PlexMovies, 'movies': self.PlexMovies,
'tvshows': self.PlexTVShows 'tvshows': self.PlexTVShows,
} }
if self.enableMusic:
process['music'] = self.PlexMusic
for itemtype in process: for itemtype in process:
startTime = datetime.now() startTime = datetime.now()
@ -391,33 +397,12 @@ class LibrarySync(Thread):
"SyncDatabase (finished %s in: %s)" "SyncDatabase (finished %s in: %s)"
% (itemtype, str(elapsedTime).split('.')[0]), 1) % (itemtype, str(elapsedTime).split('.')[0]), 1)
# # sync music # Let kodi update the views in any case
# if music_enabled:
# musicconn = utils.kodiSQL('music')
# musiccursor = musicconn.cursor()
# startTime = datetime.now()
# completed = self.music(embycursor, musiccursor, pDialog)
# if not completed:
# utils.window('emby_dbScan', clear=True)
# embycursor.close()
# musiccursor.close()
# return False
# else:
# musicconn.commit()
# embyconn.commit()
# elapsedTime = datetime.now() - startTime
# self.logMsg(
# "SyncDatabase (finished music in: %s)"
# % (str(elapsedTime).split('.')[0]), 1)
# musiccursor.close()
xbmc.executebuiltin('UpdateLibrary(video)') xbmc.executebuiltin('UpdateLibrary(video)')
elapsedtotal = datetime.now() - starttotal if self.enableMusic:
xbmc.executebuiltin('UpdateLibrary(music)')
elapsedtotal = datetime.now() - starttotal
utils.window('emby_initialScan', clear=True) utils.window('emby_initialScan', clear=True)
self.showKodiNote("%s completed in: %s" self.showKodiNote("%s completed in: %s"
% (message, str(elapsedtotal).split('.')[0])) % (message, str(elapsedtotal).split('.')[0]))
@ -429,7 +414,8 @@ class LibrarySync(Thread):
folder = folderItem.attrib folder = folderItem.attrib
mediatype = folder['type'] mediatype = folder['type']
# Only process supported formats # Only process supported formats
if mediatype not in ['movie', 'show']: supportedMedia = ['movie', 'show']
if mediatype not in supportedMedia:
return return
folderid = folder['key'] folderid = folder['key']
@ -565,11 +551,24 @@ class LibrarySync(Thread):
# update views for all: # update views for all:
self.views = emby_db.getAllViewInfo() self.views = emby_db.getAllViewInfo()
# Append music views only to self.views (no custom views otherwise)
if self.enableMusic:
for folderItem in result:
if folderItem.attrib['type'] == 'artist':
entry = {
'id': folderItem.attrib['key'],
'name': folderItem.attrib['title'],
'itemtype': 'artist'
}
self.views.append(entry)
self.logMsg("views saved: %s" % self.views, 1) self.logMsg("views saved: %s" % self.views, 1)
def GetUpdatelist(self, xml, itemType, method, viewName, viewId, def GetUpdatelist(self, xml, itemType, method, viewName, viewId,
dontCheck=False): dontCheck=False):
""" """
THIS METHOD NEEDS TO BE FAST! => e.g. no API calls
Adds items to self.updatelist as well as self.allPlexElementsId dict Adds items to self.updatelist as well as self.allPlexElementsId dict
Input: Input:
@ -599,13 +598,13 @@ class LibrarySync(Thread):
if self.compare or not dontCheck: if self.compare or not dontCheck:
# Manual sync # Manual sync
for item in xml: for item in xml:
itemId = item.attrib.get('ratingKey')
# Skipping items 'title=All episodes' without a 'ratingKey' # Skipping items 'title=All episodes' without a 'ratingKey'
if not item.attrib.get('ratingKey', False): if not itemId:
continue continue
API = PlexAPI.API(item) title = item.attrib.get('title', 'Missing Title Name')
itemId = API.getRatingKey() plex_checksum = ("K%s%s"
title, sorttitle = API.getTitle() % (itemId, item.attrib.get('updatedAt', '')))
plex_checksum = API.getChecksum()
self.allPlexElementsId[itemId] = plex_checksum self.allPlexElementsId[itemId] = plex_checksum
kodi_checksum = self.allKodiElementsId.get(itemId) kodi_checksum = self.allKodiElementsId.get(itemId)
if kodi_checksum != plex_checksum: if kodi_checksum != plex_checksum:
@ -620,13 +619,13 @@ class LibrarySync(Thread):
else: else:
# Initial or repair sync: get all Plex movies # Initial or repair sync: get all Plex movies
for item in xml: for item in xml:
# Only look at valid items = Plex library items itemId = item.attrib.get('ratingKey')
if not item.attrib.get('ratingKey', False): # Skipping items 'title=All episodes' without a 'ratingKey'
if not itemId:
continue continue
API = PlexAPI.API(item) title = item.attrib.get('title', 'Missing Title Name')
itemId = API.getRatingKey() plex_checksum = ("K%s%s"
title, sorttitle = API.getTitle() % (itemId, item.attrib.get('updatedAt', '')))
plex_checksum = API.getChecksum()
self.allPlexElementsId[itemId] = plex_checksum self.allPlexElementsId[itemId] = plex_checksum
self.updatelist.append({'itemId': itemId, self.updatelist.append({'itemId': itemId,
'itemType': itemType, 'itemType': itemType,
@ -856,23 +855,12 @@ class LibrarySync(Thread):
self.allKodiElementsId = {} self.allKodiElementsId = {}
if self.compare: if self.compare:
with embydb.GetEmbyDB() as emby_db: with embydb.GetEmbyDB() as emby_db:
# Get movies from Plex server
# Pull the list of TV shows already in Kodi # Pull the list of TV shows already in Kodi
for kind in ('Series', 'Season', 'Episode'):
try: try:
all_koditvshows = dict(emby_db.getChecksum('Series')) elements = dict(emby_db.getChecksum(kind))
self.allKodiElementsId.update(all_koditvshows) self.allKodiElementsId.update(elements)
except ValueError: # Yet empty/not yet synched
pass
# Same for seasons
try:
all_kodiseasons = dict(emby_db.getChecksum('Season'))
self.allKodiElementsId.update(all_kodiseasons)
except ValueError:
pass
# Same for the episodes (sub-element of shows/series)
try:
all_kodiepisodes = dict(emby_db.getChecksum('Episode'))
self.allKodiElementsId.update(all_kodiepisodes)
except ValueError: except ValueError:
pass pass
@ -965,50 +953,83 @@ class LibrarySync(Thread):
self.logMsg("%s sync is finished." % itemType, 1) self.logMsg("%s sync is finished." % itemType, 1)
return True return True
def music(self, embycursor, kodicursor, pdialog): def PlexMusic(self):
# Get music from emby itemType = 'Music'
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
music = itemtypes.Music(embycursor, kodicursor)
process = { views = [x for x in self.views if x['itemtype'] == 'artist']
self.logMsg("Media folders for %s: %s" % (itemType, views), 1)
'artists': [emby.getArtists, music.add_updateArtist], methods = {
'albums': [emby.getAlbums, music.add_updateAlbum], 'MusicArtist': 'add_updateArtist',
'songs': [emby.getSongs, music.add_updateSong] 'MusicAlbum': 'add_updateAlbum',
'Audio': 'add_updateSong'
}
urlArgs = {
'MusicArtist': {'type': 8},
'MusicAlbum': {'type': 9},
'Audio': {'type': 10}
} }
types = ['artists', 'albums', 'songs']
for type in types:
if pdialog: # Process artist, then album and tracks last
pdialog.update( for kind in ('MusicArtist', 'MusicAlbum', 'Audio'):
heading="Emby for Kodi",
message="Gathering %s..." % type)
all_embyitems = process[type][0](dialog=pdialog)
total = all_embyitems['TotalRecordCount']
embyitems = all_embyitems['Items']
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (type, total))
count = 0
for embyitem in embyitems:
# Process individual item
if self.threadStopped(): if self.threadStopped():
return False
title = embyitem['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
process[type][1](embyitem)
else:
self.logMsg("%s finished." % type, 2)
return True return True
self.logMsg("Start processing music %s" % kind, 1)
self.ProcessMusic(
views, kind, urlArgs[kind], methods[kind])
self.logMsg("Processing of music %s done" % kind, 1)
self.GetAndProcessXMLs(itemType)
self.logMsg("GetAndProcessXMLs for music %s completed" % kind, 1)
# reset stuff
self.allKodiElementsId = {}
self.allPlexElementsId = {}
self.updatelist = []
self.logMsg("%s sync is finished." % itemType, 1)
return True
def ProcessMusic(self, views, kind, urlArgs, method):
self.allKodiElementsId = {}
self.allPlexElementsId = {}
self.updatelist = []
# Get a list of items already existing in Kodi db
if self.compare:
with embydb.GetEmbyDB() as emby_db:
# Pull the list of items already in Kodi
try:
elements = dict(emby_db.getChecksum(kind))
self.allKodiElementsId.update(elements)
# Yet empty/nothing yet synched
except ValueError:
pass
for view in views:
if self.threadStopped():
return True
# Get items per view
viewId = view['id']
viewName = view['name']
itemsXML = PlexFunctions.GetPlexSectionResults(
viewId, args=urlArgs)
if not itemsXML:
self.logMsg("Error downloading xml for view %s"
% viewId, -1)
continue
# Populate self.updatelist and self.allPlexElementsId
self.GetUpdatelist(itemsXML,
'Music',
method,
viewName,
viewId)
# Remove items from Kodi db?
if self.compare:
# Manual sync, process deletes
with itemtypes.Music() as Music:
for item in self.allKodiElementsId:
if item not in self.allPlexElementsId:
Music.remove(item)
def compareDBVersion(self, current, minimum): def compareDBVersion(self, current, minimum):
# It returns True is database is up to date. False otherwise. # It returns True is database is up to date. False otherwise.