Plex Music!
Playing does not work yet, but Lib sync is up
This commit is contained in:
parent
f162b6ea8a
commit
bcbbb1b42f
6 changed files with 421 additions and 455 deletions
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -409,10 +405,6 @@ class Artwork():
|
||||||
imageType in ("fanart", "poster")):
|
imageType in ("fanart", "poster")):
|
||||||
# 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((
|
||||||
|
|
||||||
|
|
|
@ -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'):
|
||||||
|
@ -1171,7 +1150,7 @@ class TVShows(Items):
|
||||||
airsBeforeSeason = "-1"
|
airsBeforeSeason = "-1"
|
||||||
airsBeforeEpisode = "-1"
|
airsBeforeEpisode = "-1"
|
||||||
# Append multi episodes to title
|
# Append multi episodes to title
|
||||||
# if item.get('IndexNumberEnd'):
|
# if item.get('IndexNumberEnd'):
|
||||||
# title = "| %02d | %s" % (item['IndexNumberEnd'], title)
|
# title = "| %02d | %s" % (item['IndexNumberEnd'], title)
|
||||||
|
|
||||||
# Get season id
|
# Get season id
|
||||||
|
@ -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,38 +1535,37 @@ 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
|
||||||
|
|
||||||
if thumb:
|
if thumb:
|
||||||
thumb = "<thumb>%s</thumb>" % thumb
|
thumb = "<thumb>%s</thumb>" % thumb
|
||||||
|
@ -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((
|
||||||
|
|
||||||
|
@ -1621,73 +1613,79 @@ class Music(Items):
|
||||||
"WHERE idArtist = ?"
|
"WHERE idArtist = ?"
|
||||||
))
|
))
|
||||||
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,92 +1731,104 @@ 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:
|
||||||
# Update with the parentId, for remove reference
|
if artist[0].attrib.get('type') == "artist":
|
||||||
emby_db.addReference(parentId, parentId, "MusicArtist", "artist")
|
# Update with the parentId, for remove reference
|
||||||
emby_db.updateParentId(itemid, parentId)
|
emby_db.addReference(
|
||||||
|
parentId, parentId, "MusicArtist", "artist")
|
||||||
|
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
|
self.logMsg('Artist %s does not exist in emby database'
|
||||||
artist = emby.getItem(artistId)
|
% artistId, 1)
|
||||||
self.add_updateArtist(artist, artisttype="AlbumArtist")
|
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 = (
|
||||||
'''
|
'''
|
||||||
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||||
|
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
kodicursor.execute(query, (artistid, albumid, artistname))
|
kodicursor.execute(query, (artistid, albumid, artistname))
|
||||||
# Update discography
|
# Update discography
|
||||||
query = (
|
query = (
|
||||||
'''
|
'''
|
||||||
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
||||||
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
kodicursor.execute(query, (artistid, name, year))
|
|
||||||
# Update emby reference with parentid
|
|
||||||
emby_db.updateParentId(artistId, albumid)
|
|
||||||
|
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
kodicursor.execute(query, (artistid, name, year))
|
||||||
|
# Update emby reference with parentid
|
||||||
|
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,94 +1836,75 @@ 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)
|
# Plex works a bit differently
|
||||||
else:
|
|
||||||
hasEmbeddedCover = False
|
|
||||||
comment = API.getOverview()
|
|
||||||
|
|
||||||
|
|
||||||
##### 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)
|
||||||
|
|
||||||
# Add path
|
# Add path
|
||||||
pathid = kodi_db.addPath(path)
|
pathid = kodi_db.addPath(path)
|
||||||
|
|
||||||
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:
|
||||||
|
@ -1963,10 +1955,8 @@ 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,25 +1967,27 @@ 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 = (
|
||||||
'''
|
'''
|
||||||
INSERT OR REPLACE INTO albuminfosong(
|
INSERT OR REPLACE INTO albuminfosong(
|
||||||
idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration)
|
idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration)
|
||||||
|
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
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,112 +2001,59 @@ 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)
|
try:
|
||||||
emby_dbartist = emby_db.getItem_byId(artist['Id'])
|
artistid = emby_dbartist[0]
|
||||||
try:
|
except:
|
||||||
artistid = emby_dbartist[0]
|
pass
|
||||||
except: pass
|
else:
|
||||||
else:
|
query = (
|
||||||
|
'''
|
||||||
|
INSERT OR REPLACE INTO song_artist(idArtist, idSong, strArtist)
|
||||||
|
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
kodicursor.execute(query, (artistid, songid, artist_name))
|
||||||
|
|
||||||
|
if addArtist:
|
||||||
query = (
|
query = (
|
||||||
'''
|
'''
|
||||||
INSERT OR REPLACE INTO song_artist(idArtist, idSong, strArtist)
|
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||||
|
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
kodicursor.execute(query, (artistid, songid, artist_name))
|
kodicursor.execute(query, (artistid, albumid, artist_name))
|
||||||
|
|
||||||
if addArtist:
|
if addArtist:
|
||||||
query = (
|
if kodiversion in (16, 17):
|
||||||
'''
|
# Kodi Jarvis, Krypton
|
||||||
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||||
|
kodicursor.execute(query, (artist_name, albumid))
|
||||||
VALUES (?, ?, ?)
|
elif kodiversion == 15:
|
||||||
'''
|
# Kodi Isengard
|
||||||
)
|
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||||
kodicursor.execute(query, (artistid, albumid, artist_name))
|
kodicursor.execute(query, (artist_name, albumid))
|
||||||
else:
|
else:
|
||||||
if addArtist:
|
# Kodi Helix
|
||||||
artists_onalbum = " / ".join(artists_name)
|
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||||
if kodiversion in (16, 17):
|
kodicursor.execute(query, (artist_name, albumid))
|
||||||
# Kodi Jarvis, Krypton
|
|
||||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
|
||||||
kodicursor.execute(query, (artists_onalbum, albumid))
|
|
||||||
elif kodiversion == 15:
|
|
||||||
# Kodi Isengard
|
|
||||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
|
||||||
kodicursor.execute(query, (artists_onalbum, albumid))
|
|
||||||
else:
|
|
||||||
# Kodi Helix
|
|
||||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
|
||||||
kodicursor.execute(query, (artists_onalbum, 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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import read_embyserver as embyserver
|
||||||
import userclient
|
import userclient
|
||||||
import videonodes
|
import videonodes
|
||||||
|
|
||||||
import PlexAPI
|
|
||||||
import PlexFunctions
|
import PlexFunctions
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -125,9 +124,14 @@ 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:
|
||||||
itemSubFkt(child,
|
if method == 'add_updateAlbum':
|
||||||
viewtag=viewName,
|
item.add_updateAlbum(child,
|
||||||
viewid=viewId)
|
viewtag=viewName,
|
||||||
|
viewid=viewId)
|
||||||
|
else:
|
||||||
|
itemSubFkt(child,
|
||||||
|
viewtag=viewName,
|
||||||
|
viewid=viewId)
|
||||||
# Keep track of where we are at
|
# Keep track of where we are at
|
||||||
processMetadataCount += 1
|
processMetadataCount += 1
|
||||||
processingViewName = title
|
processingViewName = title
|
||||||
|
@ -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,25 +855,14 @@ 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
|
||||||
try:
|
for kind in ('Series', 'Season', 'Episode'):
|
||||||
all_koditvshows = dict(emby_db.getChecksum('Series'))
|
try:
|
||||||
self.allKodiElementsId.update(all_koditvshows)
|
elements = dict(emby_db.getChecksum(kind))
|
||||||
except ValueError:
|
self.allKodiElementsId.update(elements)
|
||||||
pass
|
# Yet empty/not yet synched
|
||||||
# Same for seasons
|
except ValueError:
|
||||||
try:
|
pass
|
||||||
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:
|
|
||||||
pass
|
|
||||||
|
|
||||||
##### PROCESS TV Shows #####
|
##### PROCESS TV Shows #####
|
||||||
self.updatelist = []
|
self.updatelist = []
|
||||||
|
@ -965,51 +953,84 @@ 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",
|
if self.threadStopped():
|
||||||
message="Gathering %s..." % type)
|
return True
|
||||||
|
self.logMsg("Start processing music %s" % kind, 1)
|
||||||
all_embyitems = process[type][0](dialog=pdialog)
|
self.ProcessMusic(
|
||||||
total = all_embyitems['TotalRecordCount']
|
views, kind, urlArgs[kind], methods[kind])
|
||||||
embyitems = all_embyitems['Items']
|
self.logMsg("Processing of music %s done" % kind, 1)
|
||||||
|
self.GetAndProcessXMLs(itemType)
|
||||||
if pdialog:
|
self.logMsg("GetAndProcessXMLs for music %s completed" % kind, 1)
|
||||||
pdialog.update(heading="Processing %s / %s items" % (type, total))
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
for embyitem in embyitems:
|
|
||||||
# Process individual item
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
# reset stuff
|
||||||
|
self.allKodiElementsId = {}
|
||||||
|
self.allPlexElementsId = {}
|
||||||
|
self.updatelist = []
|
||||||
|
self.logMsg("%s sync is finished." % itemType, 1)
|
||||||
return True
|
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.
|
||||||
self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
|
self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
|
||||||
|
|
Loading…
Reference in a new issue