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)
# return localtime + ' ' + localdate
try:
DATEFORMAT = xbmc.getRegion('dateshort')
TIMEFORMAT = xbmc.getRegion('meridiem')
# DATEFORMAT = xbmc.getRegion('dateshort')
# TIMEFORMAT = xbmc.getRegion('meridiem')
date_time = time.localtime(float(stamp))
localdate = time.strftime('%Y-%m-%d %H:%M:%S', date_time)
except:
@ -1447,7 +1447,7 @@ class API():
"""
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):
"""
@ -1491,7 +1491,7 @@ class API():
try:
playcount = int(item['viewCount'])
except KeyError:
except:
playcount = None
if playcount:
@ -1499,9 +1499,14 @@ class API():
try:
lastPlayedDate = self.DateToKodi(int(item['lastViewedAt']))
except KeyError:
except:
lastPlayedDate = None
try:
userrating = int(item['userRating'])
except:
userrating = None
resume, runtime = self.getRuntime()
return {
'Favorite': favorite,
@ -1510,7 +1515,8 @@ class API():
'LastPlayedDate': lastPlayedDate,
'Resume': resume,
'Runtime': runtime,
'Rating': rating
'Rating': rating,
'UserRating': userrating
}
def getPeople(self):

View file

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

View file

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

View file

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

View file

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

View file

@ -20,7 +20,6 @@ import read_embyserver as embyserver
import userclient
import videonodes
import PlexAPI
import PlexFunctions
###############################################################################
@ -125,6 +124,11 @@ class ThreadedProcessMetadata(Thread):
with lock:
# Get the one child entry in the xml and process
for child in plexitem:
if method == 'add_updateAlbum':
item.add_updateAlbum(child,
viewtag=viewName,
viewid=viewId)
else:
itemSubFkt(child,
viewtag=viewName,
viewid=viewId)
@ -219,6 +223,8 @@ class LibrarySync(Thread):
utils.settings('SyncInstallRunDone') == 'true' else False
self.showDbSync = True if \
utils.settings('dbSyncIndicator') == 'true' else False
self.enableMusic = True if utils.settings('enableMusic') == "true" \
else False
Thread.__init__(self)
@ -250,6 +256,13 @@ class LibrarySync(Thread):
Using /library/recentlyAdded is NOT working as changes to lib items are
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
# Get last sync time
@ -262,29 +275,22 @@ class LibrarySync(Thread):
# Set new timestamp NOW because sync might take a while
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
allKodiElementsId = {}
with embydb.GetEmbyDB() as emby_db:
for itemtype in PlexFunctions.EmbyItemtypes():
try:
allKodiElementsId.update(
dict(emby_db.getChecksum(itemtype)))
except ValueError:
pass
self.allKodiElementsId = allKodiElementsId
# NEW idea: process every item returned by the PMS
self.allKodiElementsId = {}
# Run through views and get latest changed elements using time diff
self.updatelist = []
self.allPlexElementsId = {}
self.updateKodiVideoLib = False
self.updateKodiMusicLib = False
for view in self.views:
self.updatelist = []
if self.threadStopped():
return True
# Get items per view
items = PlexFunctions.GetAllPlexLeaves(
view['id'], updatedAt=lastSync)
# Just skip item if something went wrong
if not items:
continue
# 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[0]['itemType'] in ['Movies', 'TVShows']:
self.updateKodiVideoLib = True
elif self.updatelist[0]['itemType'] == 'Music':
self.updateKodiMusicLib = True
self.GetAndProcessXMLs(
PlexFunctions.GetItemClassFromType(plexType))
self.updatelist = []
# Update userdata
for view in self.views:
self.PlexUpdateWatched(
view['id'],
PlexFunctions.GetItemClassFromType(view['itemtype']),
lastViewedAt=lastSync)
# Let Kodi update the library now (artwork and userdata)
if self.updateKodiVideoLib:
self.logMsg("Doing Kodi Video Lib update", 2)
xbmc.executebuiltin('UpdateLibrary(video)')
if self.updateKodiMusicLib:
self.logMsg("Doing Kodi Music Lib update", 2)
xbmc.executebuiltin('UpdateLibrary(music)')
# Reset and return
self.allKodiElementsId = {}
self.allPlexElementsId = {}
return True
@ -346,7 +359,6 @@ class LibrarySync(Thread):
def fullSync(self, manualrun=False, repair=False):
# Only run once when first setting up. Can be run manually.
self.compare = manualrun or repair
music_enabled = utils.settings('enableMusic') == "true"
# Add sources
utils.sourcesXML()
@ -367,18 +379,12 @@ class LibrarySync(Thread):
# Set views
self.maintainViews()
# Sync video library
# process = {
# 'movies': self.movies,
# 'musicvideos': self.musicvideos,
# 'tvshows': self.tvshows
# }
process = {
'movies': self.PlexMovies,
'tvshows': self.PlexTVShows
'tvshows': self.PlexTVShows,
}
if self.enableMusic:
process['music'] = self.PlexMusic
for itemtype in process:
startTime = datetime.now()
@ -391,33 +397,12 @@ class LibrarySync(Thread):
"SyncDatabase (finished %s in: %s)"
% (itemtype, str(elapsedTime).split('.')[0]), 1)
# # sync music
# 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()
# Let kodi update the views in any case
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)
self.showKodiNote("%s completed in: %s"
% (message, str(elapsedtotal).split('.')[0]))
@ -429,7 +414,8 @@ class LibrarySync(Thread):
folder = folderItem.attrib
mediatype = folder['type']
# Only process supported formats
if mediatype not in ['movie', 'show']:
supportedMedia = ['movie', 'show']
if mediatype not in supportedMedia:
return
folderid = folder['key']
@ -565,11 +551,24 @@ class LibrarySync(Thread):
# update views for all:
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)
def GetUpdatelist(self, xml, itemType, method, viewName, viewId,
dontCheck=False):
"""
THIS METHOD NEEDS TO BE FAST! => e.g. no API calls
Adds items to self.updatelist as well as self.allPlexElementsId dict
Input:
@ -599,13 +598,13 @@ class LibrarySync(Thread):
if self.compare or not dontCheck:
# Manual sync
for item in xml:
itemId = item.attrib.get('ratingKey')
# Skipping items 'title=All episodes' without a 'ratingKey'
if not item.attrib.get('ratingKey', False):
if not itemId:
continue
API = PlexAPI.API(item)
itemId = API.getRatingKey()
title, sorttitle = API.getTitle()
plex_checksum = API.getChecksum()
title = item.attrib.get('title', 'Missing Title Name')
plex_checksum = ("K%s%s"
% (itemId, item.attrib.get('updatedAt', '')))
self.allPlexElementsId[itemId] = plex_checksum
kodi_checksum = self.allKodiElementsId.get(itemId)
if kodi_checksum != plex_checksum:
@ -620,13 +619,13 @@ class LibrarySync(Thread):
else:
# Initial or repair sync: get all Plex movies
for item in xml:
# Only look at valid items = Plex library items
if not item.attrib.get('ratingKey', False):
itemId = item.attrib.get('ratingKey')
# Skipping items 'title=All episodes' without a 'ratingKey'
if not itemId:
continue
API = PlexAPI.API(item)
itemId = API.getRatingKey()
title, sorttitle = API.getTitle()
plex_checksum = API.getChecksum()
title = item.attrib.get('title', 'Missing Title Name')
plex_checksum = ("K%s%s"
% (itemId, item.attrib.get('updatedAt', '')))
self.allPlexElementsId[itemId] = plex_checksum
self.updatelist.append({'itemId': itemId,
'itemType': itemType,
@ -856,23 +855,12 @@ class LibrarySync(Thread):
self.allKodiElementsId = {}
if self.compare:
with embydb.GetEmbyDB() as emby_db:
# Get movies from Plex server
# Pull the list of TV shows already in Kodi
for kind in ('Series', 'Season', 'Episode'):
try:
all_koditvshows = dict(emby_db.getChecksum('Series'))
self.allKodiElementsId.update(all_koditvshows)
except ValueError:
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)
elements = dict(emby_db.getChecksum(kind))
self.allKodiElementsId.update(elements)
# Yet empty/not yet synched
except ValueError:
pass
@ -965,50 +953,83 @@ class LibrarySync(Thread):
self.logMsg("%s sync is finished." % itemType, 1)
return True
def music(self, embycursor, kodicursor, pdialog):
# Get music from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
music = itemtypes.Music(embycursor, kodicursor)
def PlexMusic(self):
itemType = 'Music'
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],
'albums': [emby.getAlbums, music.add_updateAlbum],
'songs': [emby.getSongs, music.add_updateSong]
methods = {
'MusicArtist': 'add_updateArtist',
'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:
pdialog.update(
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
# Process artist, then album and tracks last
for kind in ('MusicArtist', 'MusicAlbum', 'Audio'):
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
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):
# It returns True is database is up to date. False otherwise.