PlexKodiConnect/resources/lib/itemtypes.py

2256 lines
83 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
##################################################################################################
import urllib
from ntpath import dirname
from datetime import datetime
import xbmc
import xbmcgui
import xbmcvfs
import api
import artwork
import clientinfo
import downloadutils
import utils
import embydb_functions as embydb
import kodidb_functions as kodidb
import read_embyserver as embyserver
import musicutils as musicutils
2015-12-29 04:47:16 +11:00
import PlexAPI
2016-01-12 03:37:01 +11:00
import sys
2015-12-29 04:47:16 +11:00
##################################################################################################
class Items(object):
2016-01-11 01:16:59 +11:00
"""
Items to be called with "with Items as xxx:" to ensure that __enter__
2016-01-11 18:10:36 +11:00
method is called (opens db connections)
2016-01-11 01:16:59 +11:00
"""
def __init__(self):
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
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"
self.contentmsg = utils.settings('newContent') == "true"
self.artwork = artwork.Artwork()
self.emby = embyserver.Read_EmbyServer()
def __enter__(self):
2016-01-11 01:16:59 +11:00
"""
Open DB connections and cursors
"""
self.embyconn = utils.kodiSQL('emby')
self.embycursor = self.embyconn.cursor()
self.kodiconn = utils.kodiSQL('video')
self.kodicursor = self.kodiconn.cursor()
self.emby_db = embydb.Embydb_Functions(self.embycursor)
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Make sure DB changes are committed and connection to DB is closed.
"""
self.embyconn.commit()
self.kodiconn.commit()
self.embyconn.close()
self.kodiconn.close()
return self
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
def itemsbyId(self, items, process, pdialog=None):
# Process items by itemid. Process can be added, update, userdata, remove
emby = self.emby
embycursor = self.embycursor
kodicursor = self.kodicursor
music_enabled = self.music_enabled
itemtypes = {
'Movie': Movies,
'BoxSet': Movies,
'Series': TVShows,
'Season': TVShows,
'Episode': TVShows,
'MusicAlbum': Music,
'MusicArtist': Music,
'AlbumArtist': Music,
'Audio': Music
}
total = 0
for item in items:
total += len(items[item])
if total == 0:
return False
self.logMsg("Processing %s: %s" % (process, items), 1)
if pdialog:
pdialog.update(heading="Processing %s: %s items" % (process, total))
count = 0
for itemtype in items:
# Safety check
if not itemtypes.get(itemtype):
# We don't process this type of item
continue
itemlist = items[itemtype]
if not itemlist:
# The list to process is empty
continue
musicconn = None
2016-01-09 05:56:21 +11:00
if itemtype in ('MusicAlbum', 'MusicArtist', 'AlbumArtist', 'Audio'):
if music_enabled:
musicconn = utils.kodiSQL('music')
musiccursor = musicconn.cursor()
items_process = itemtypes[itemtype](embycursor, musiccursor)
else:
# Music is not enabled, do not proceed with itemtype
continue
else:
items_process = itemtypes[itemtype](embycursor, kodicursor)
if itemtype == "Movie":
actions = {
'added': items_process.added,
'update': items_process.add_update,
'userdata': items_process.updateUserdata,
'remove': items_process.remove
}
elif itemtype == "BoxSet":
actions = {
'added': items_process.added_boxset,
'update': items_process.add_updateBoxset,
'remove': items_process.remove
}
elif itemtype == "MusicVideo":
actions = {
'added': items_process.added,
'update': items_process.add_update,
'userdata': items_process.updateUserdata,
'remove': items_process.remove
}
elif itemtype == "Series":
actions = {
'added': items_process.added,
'update': items_process.add_update,
'userdata': items_process.updateUserdata,
'remove': items_process.remove
}
elif itemtype == "Season":
actions = {
'added': items_process.added_season,
'update': items_process.add_updateSeason,
'remove': items_process.remove
}
elif itemtype == "Episode":
actions = {
'added': items_process.added_episode,
'update': items_process.add_updateEpisode,
'userdata': items_process.updateUserdata,
'remove': items_process.remove
}
elif itemtype == "MusicAlbum":
actions = {
'added': items_process.added_album,
'update': items_process.add_updateAlbum,
'userdata': items_process.updateUserdata,
'remove': items_process.remove
}
elif itemtype in ("MusicArtist", "AlbumArtist"):
actions = {
'added': items_process.added,
'update': items_process.add_updateArtist,
'remove': items_process.remove
}
elif itemtype == "Audio":
actions = {
'added': items_process.added_song,
'update': items_process.add_updateSong,
'userdata': items_process.updateUserdata,
'remove': items_process.remove
}
else:
self.logMsg("Unsupported itemtype: %s." % itemtype, 1)
actions = {}
if actions.get(process):
if process == "remove":
for item in itemlist:
actions[process](item)
elif process == "added":
actions[process](itemlist, pdialog)
else:
processItems = emby.getFullItems(itemlist)
for item in processItems:
title = item['Name']
if itemtype == "Episode":
title = "%s - %s" % (item['SeriesName'], title)
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
actions[process](item)
if musicconn is not None:
# close connection for special types
musicconn.commit()
musiccursor.close()
return True
def contentPop(self, name):
xbmcgui.Dialog().notification(
heading="Emby for Kodi",
message="Added: %s" % name,
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
sound=False)
class Movies(Items):
def added(self, items, pdialog):
total = len(items)
count = 0
for movie in items:
title = movie['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_update(movie)
if not pdialog and self.contentmsg:
self.contentPop(title)
def added_boxset(self, items, pdialog):
total = len(items)
count = 0
for boxset in items:
title = boxset['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_updateBoxset(boxset)
def updateUserdata(self, itemList):
"""
Updates the Kodi watched state of the item from PMS. Also retrieves
Plex resume points for movies in progress.
"""
API = PlexAPI.API(itemList)
for itemNumber in range(0, len(itemList)):
API.setChildNumber(itemNumber)
itemid = API.getKey()
# Get key and db entry on the Kodi db side
fileid = self.emby_db.getItem_byId(itemid)[1]
# Grab the user's viewcount, resume points etc. from PMS' answer
userdata = API.getUserData()
# Write to Kodi DB
self.kodi_db.addPlaystate(fileid,
userdata['Resume'],
userdata['Runtime'],
userdata['PlayCount'],
userdata['LastPlayedDate'])
def add_update(self, item, viewtag=None, viewid=None):
# Process single movie
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
2015-12-29 04:47:16 +11:00
API = PlexAPI.API(item)
# If the item already exist in the local Kodi DB we'll perform a full item update
# If the item doesn't exist, we'll add it to the database
update_item = True
2015-12-30 00:13:32 +11:00
itemid = API.getKey()
# Cannot parse XML, abort
if not itemid:
self.logMsg("Cannot parse XML data for movie", -1)
return
emby_dbitem = emby_db.getItem_byId(itemid)
try:
movieid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
self.logMsg("movieid: %s fileid: %s pathid: %s" % (movieid, fileid, pathid), 1)
2015-12-30 00:13:32 +11:00
except TypeError:
update_item = False
self.logMsg("movieid: %s not found." % itemid, 2)
# movieid
kodicursor.execute("select coalesce(max(idMovie),0) from movie")
movieid = kodicursor.fetchone()[0] + 1
2016-01-12 01:56:56 +11:00
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)
2015-12-30 00:13:32 +11:00
# fileId information
checksum = API.getChecksum()
dateadded = API.getDateCreated()
userdata = API.getUserData()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
people = API.getPeople()
2015-12-29 04:47:16 +11:00
writer = API.joinList(people['Writer'])
director = API.joinList(people['Director'])
genres = API.getGenres()
title, sorttitle = API.getTitle()
plot = API.getPlot()
2015-12-29 04:47:16 +11:00
shortplot = None
tagline = API.getTagline()
2015-12-30 23:25:37 +11:00
votecount = None
2016-01-11 19:57:45 +11:00
rating = API.getAudienceRating()
year = API.getYear()
imdb = API.getProvider('Imdb')
2015-12-29 04:47:16 +11:00
resume, runtime = API.getRuntime()
mpaa = API.getMpaa()
2015-12-29 04:47:16 +11:00
genre = API.joinList(genres)
countries = API.getCountry()
country = API.joinList(countries)
studios = API.getStudios()
try:
studio = studios[0]
except IndexError:
studio = None
2016-01-11 19:57:45 +11:00
self.logMsg("Retrieved metadata for %s" % itemid, 2)
2016-01-12 21:47:48 +11:00
# Find one trailer
2015-12-29 04:47:16 +11:00
trailer = None
2016-01-12 21:47:48 +11:00
extras = API.getExtras()
2016-01-12 23:57:26 +11:00
for item in extras:
# Only get 1st trailer element
if item['extraType'] == '1':
trailer = item['key']
trailer = "plugin://plugin.video.plexkodiconnect/trailer/?id=%s&mode=play" % trailer
self.logMsg("Trailer for %s: %s" % (itemid, trailer), 2)
break
##### GET THE FILE AND PATH #####
playurl = API.getFilePath()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
if self.directpath:
# 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
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")
else:
# Set plugin path and media flags using real filename
path = "plugin://plugin.video.plexkodiconnect.movies/"
params = {
'filename': filename.encode('utf-8'),
'id': itemid,
'dbid': movieid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE MOVIE #####
if update_item:
self.logMsg("UPDATE movie itemid: %s - Title: %s" % (itemid, title), 1)
# Update the movie entry
query = ' '.join((
"UPDATE movie",
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
"c16 = ?, c18 = ?, c19 = ?, c21 = ?",
"WHERE idMovie = ?"
))
kodicursor.execute(query, (title, plot, shortplot, tagline, votecount, rating, writer,
year, imdb, sorttitle, runtime, mpaa, genre, director, title, studio, trailer,
country, movieid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE MOVIE #####
else:
self.logMsg("ADD movie itemid: %s - Title: %s" % (itemid, title), 1)
# Add path
pathid = kodi_db.addPath(path)
# Add the file
fileid = kodi_db.addFile(filename, pathid)
# Create the movie entry
query = (
'''
INSERT INTO movie(
idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
c09, c10, c11, c12, c14, c15, c16, c18, c19, c21)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (movieid, fileid, title, plot, shortplot, tagline, votecount,
rating, writer, year, imdb, sorttitle, runtime, mpaa, genre, director, title,
studio, trailer, country))
# Create the reference in emby table
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None, checksum, viewid)
# Update the path
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (path, "movies", "metadata.local", 1, pathid))
# Update the file
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
# Process countries
2015-12-29 04:47:16 +11:00
kodi_db.addCountries(movieid, countries, "movie")
# Process cast
2015-12-29 04:47:16 +11:00
people = API.getPeopleList()
kodi_db.addPeople(movieid, people, "movie")
# Process genres
kodi_db.addGenres(movieid, genres, "movie")
# Process artwork
2015-12-31 01:57:55 +11:00
allartworks = API.getAllArtwork()
artwork.addArtwork(allartworks, movieid, "movie", kodicursor)
# Process stream details
streams = API.getMediaStreams()
kodi_db.addStreams(fileid, streams, runtime)
# Process studios
kodi_db.addStudios(movieid, studios, "movie")
# Process tags: view, emby tags
2016-01-12 01:56:56 +11:00
tags = [viewtag]
2015-12-29 04:47:16 +11:00
# tags.extend(item['Tags'])
# if userdata['Favorite']:
2016-01-12 01:56:56 +11:00
# tags.append("Favorite movies")
kodi_db.addTags(movieid, tags, "movie")
# Process playstates
2016-01-02 06:36:02 +11:00
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
def remove(self, itemid):
# Remove movieid, fileid, emby reference
emby_db = self.emby_db
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
fileid = emby_dbitem[1]
mediatype = emby_dbitem[4]
self.logMsg("Removing %sid: %s fileid: %s" % (mediatype, kodiid, fileid), 1)
except TypeError:
return
# Remove the emby reference
emby_db.removeItem(itemid)
# Remove artwork
artwork.deleteArtwork(kodiid, mediatype, kodicursor)
if mediatype == "movie":
# Delete kodi movie and file
kodicursor.execute("DELETE FROM movie WHERE idMovie = ?", (kodiid,))
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
elif mediatype == "set":
# Delete kodi boxset
boxset_movies = emby_db.getItem_byParentId(kodiid, "movie")
for movie in boxset_movies:
embyid = movie[0]
movieid = movie[1]
self.kodi_db.removefromBoxset(movieid)
# Update emby reference
emby_db.updateParentId(embyid, None)
kodicursor.execute("DELETE FROM sets WHERE idSet = ?", (kodiid,))
self.logMsg("Deleted %s %s from kodi database" % (mediatype, itemid), 1)
2016-01-11 01:16:59 +11:00
class MusicVideos(Items):
def __init__(self, embycursor, kodicursor):
Items.__init__(self, embycursor, kodicursor)
def added(self, items, pdialog):
total = len(items)
count = 0
for mvideo in items:
title = mvideo['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_update(mvideo)
if not pdialog and self.contentmsg:
self.contentPop(title)
def add_update(self, item, viewtag=None, viewid=None):
# Process single music video
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
API = api.API(item)
# If the item already exist in the local Kodi DB we'll perform a full item update
# If the item doesn't exist, we'll add it to the database
update_item = True
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
mvideoid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
self.logMsg("mvideoid: %s fileid: %s pathid: %s" % (mvideoid, fileid, pathid), 1)
except TypeError:
update_item = False
self.logMsg("mvideoid: %s not found." % itemid, 2)
# mvideoid
kodicursor.execute("select coalesce(max(idMVideo),0) from musicvideo")
mvideoid = 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()
dateadded = API.getDateCreated()
userdata = API.getUserData()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
runtime = API.getRuntime()
plot = API.getOverview()
title = item['Name']
year = item.get('ProductionYear')
genres = item['Genres']
genre = " / ".join(genres)
studios = API.getStudios()
studio = " / ".join(studios)
artist = " / ".join(item.get('Artists'))
album = item.get('Album')
track = item.get('Track')
people = API.getPeople()
director = " / ".join(people['Director'])
##### GET THE FILE AND PATH #####
playurl = API.getFilePath()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
if self.directpath:
# 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
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")
else:
# Set plugin path and media flags using real filename
path = "plugin://plugin.video.plexkodiconnect.musicvideos/"
params = {
'filename': filename.encode('utf-8'),
'id': itemid,
'dbid': mvideoid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE MUSIC VIDEO #####
if update_item:
self.logMsg("UPDATE mvideo itemid: %s - Title: %s" % (itemid, title), 1)
# Update path
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
kodicursor.execute(query, (path, pathid))
# Update the filename
query = "UPDATE files SET strFilename = ?, dateAdded = ? WHERE idFile = ?"
kodicursor.execute(query, (filename, dateadded, fileid))
# Update the music video entry
query = ' '.join((
"UPDATE musicvideo",
"SET c00 = ?, c04 = ?, c05 = ?, c06 = ?, c07 = ?, c08 = ?, c09 = ?, c10 = ?,",
"c11 = ?, c12 = ?"
"WHERE idMVideo = ?"
))
kodicursor.execute(query, (title, runtime, director, studio, year, plot, album,
artist, genre, track, mvideoid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE MUSIC VIDEO #####
else:
self.logMsg("ADD mvideo itemid: %s - Title: %s" % (itemid, title), 1)
# Add path
query = ' '.join((
"SELECT idPath",
"FROM path",
"WHERE strPath = ?"
))
kodicursor.execute(query, (path,))
try:
pathid = kodicursor.fetchone()[0]
except TypeError:
kodicursor.execute("select coalesce(max(idPath),0) from path")
pathid = kodicursor.fetchone()[0] + 1
query = (
'''
INSERT OR REPLACE INTO path(
idPath, strPath, strContent, strScraper, noUpdate)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (pathid, path, "musicvideos", "metadata.local", 1))
# Add the file
kodicursor.execute("select coalesce(max(idFile),0) from files")
fileid = kodicursor.fetchone()[0] + 1
query = (
'''
INSERT INTO files(
idFile, idPath, strFilename, dateAdded)
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (fileid, pathid, filename, dateadded))
# Create the musicvideo entry
query = (
'''
INSERT INTO musicvideo(
idMVideo, idFile, c00, c04, c05, c06, c07, c08, c09, c10, c11, c12)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (mvideoid, fileid, title, runtime, director, studio,
year, plot, album, artist, genre, track))
# Create the reference in emby table
emby_db.addReference(itemid, mvideoid, "MusicVideo", "musicvideo", fileid, pathid,
checksum=checksum, mediafolderid=viewid)
# Process cast
people = item['People']
artists = item['ArtistItems']
for artist in artists:
artist['Type'] = "Artist"
people.extend(artists)
people = artwork.getPeopleArtwork(people)
kodi_db.addPeople(mvideoid, people, "musicvideo")
# Process genres
kodi_db.addGenres(mvideoid, genres, "musicvideo")
# Process artwork
artwork.addArtwork(artwork.getAllArtwork(item), mvideoid, "musicvideo", kodicursor)
# Process stream details
streams = API.getMediaStreams()
kodi_db.addStreams(fileid, streams, runtime)
# Process studios
kodi_db.addStudios(mvideoid, studios, "musicvideo")
# Process tags: view, emby tags
tags = [viewtag]
tags.extend(item['Tags'])
if userdata['Favorite']:
tags.append("Favorite musicvideos")
kodi_db.addTags(mvideoid, tags, "musicvideo")
# Process playstates
resume = API.adjustResume(userdata['Resume'])
total = round(float(runtime), 6)
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
def updateUserdata(self, item):
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
# Poster with progress bar
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()
# Get Kodi information
emby_dbitem = emby_db.getItem_byId(itemid)
try:
mvideoid = emby_dbitem[0]
fileid = emby_dbitem[1]
self.logMsg(
"Update playstate for musicvideo: %s fileid: %s"
% (item['Name'], fileid), 1)
except TypeError:
return
# Process favorite tags
if userdata['Favorite']:
kodi_db.addTag(mvideoid, "Favorite musicvideos", "musicvideo")
else:
kodi_db.removeTag(mvideoid, "Favorite musicvideos", "musicvideo")
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
resume = API.adjustResume(userdata['Resume'])
total = round(float(runtime), 6)
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove mvideoid, fileid, pathid, emby reference
emby_db = self.emby_db
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
mvideoid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
self.logMsg("Removing mvideoid: %s fileid: %s" % (mvideoid, fileid, pathid), 1)
except TypeError:
return
# Remove artwork
query = ' '.join((
"SELECT url, type",
"FROM art",
"WHERE media_id = ?",
"AND media_type = 'musicvideo'"
))
kodicursor.execute(query, (mvideoid,))
rows = kodicursor.fetchall()
for row in rows:
url = row[0]
imagetype = row[1]
if imagetype in ("poster", "fanart"):
artwork.deleteCachedArtwork(url)
kodicursor.execute("DELETE FROM musicvideo WHERE idMVideo = ?", (mvideoid,))
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
if self.directpath:
kodicursor.execute("DELETE FROM path WHERE idPath = ?", (pathid,))
self.embycursor.execute("DELETE FROM emby WHERE emby_id = ?", (itemid,))
self.logMsg("Deleted musicvideo %s from kodi database" % itemid, 1)
2016-01-10 02:14:02 +11:00
class TVShows(Items):
def added(self, items, pdialog):
total = len(items)
count = 0
for tvshow in items:
title = tvshow['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_update(tvshow)
# Add episodes
all_episodes = self.emby.getEpisodesbyShow(tvshow['Id'])
self.added_episode(all_episodes['Items'], pdialog)
def added_season(self, items, pdialog):
total = len(items)
count = 0
for season in items:
title = "%s - %s" % (season.get('SeriesName', "Unknown"), season['Name'])
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_updateSeason(season)
# Add episodes
all_episodes = self.emby.getEpisodesbySeason(season['Id'])
self.added_episode(all_episodes['Items'], pdialog)
def added_episode(self, items, pdialog):
total = len(items)
count = 0
for episode in items:
title = "%s - %s" % (episode.get('SeriesName', "Unknown"), episode['Name'])
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_updateEpisode(episode)
if not pdialog and self.contentmsg:
self.contentPop(title)
def updateUserdata(self, itemList):
"""
Updates the Kodi watched state of the item from PMS. Also retrieves
Plex resume points for movies in progress.
"""
API = PlexAPI.API(itemList)
for itemNumber in range(0, len(itemList)):
API.setChildNumber(itemNumber)
itemid = API.getKey()
# Get key and db entry on the Kodi db side
fileid = self.emby_db.getItem_byId(itemid)[1]
# Grab the user's viewcount, resume points etc. from PMS' answer
userdata = API.getUserData()
# Write to Kodi DB
self.kodi_db.addPlaystate(fileid,
userdata['Resume'],
userdata['Runtime'],
userdata['PlayCount'],
userdata['LastPlayedDate'])
def add_update(self, item, viewtag=None, viewid=None):
# Process single tvshow
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
2016-01-10 02:14:02 +11:00
API = PlexAPI.API(item)
2016-01-10 02:14:02 +11:00
# 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
2016-01-10 02:14:02 +11:00
itemid = API.getKey()
if not itemid:
self.logMsg("Cannot parse XML data for TV show", -1)
return
emby_dbitem = emby_db.getItem_byId(itemid)
try:
showid = emby_dbitem[0]
pathid = emby_dbitem[2]
self.logMsg("showid: %s pathid: %s" % (showid, pathid), 1)
except TypeError:
update_item = False
self.logMsg("showid: %s not found." % itemid, 2)
2016-01-12 01:56:56 +11:00
self.logMsg("View tag found: %s" % viewtag, 2)
# fileId information
checksum = API.getChecksum()
# item details
2016-01-10 02:14:02 +11:00
genres = API.getGenres()
title, sorttitle = API.getTitle()
plot = API.getPlot()
rating = API.getAudienceRating()
premieredate = API.getPremiereDate()
tvdb = API.getProvider('Tvdb')
mpaa = API.getMpaa()
2016-01-10 02:14:02 +11:00
genre = API.joinList(genres)
studios = API.getStudios()
2016-01-10 02:14:02 +11:00
try:
studio = studios[0]
except IndexError:
studio = None
##### GET THE FILE AND PATH #####
playurl = API.getFilePath()
if self.directpath:
# Direct paths is set the Kodi way
if "\\" in playurl:
# Local path
path = "%s\\" % playurl
toplevelpath = "%s\\" % dirname(dirname(path))
else:
# Network path
path = "%s/" % playurl
toplevelpath = "%s/" % dirname(dirname(path))
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(path):
# Validate the path is correct with user intervention
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
utils.window('emby_pathverified', value="true")
else:
# Set plugin path
toplevelpath = "plugin://plugin.video.plexkodiconnect.tvshows/"
path = "%s%s/" % (toplevelpath, itemid)
##### UPDATE THE TVSHOW #####
if update_item:
self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
# Update the tvshow entry
query = ' '.join((
"UPDATE tvshow",
"SET c00 = ?, c01 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?",
"WHERE idShow = ?"
))
kodicursor.execute(query, (title, plot, rating, premieredate, genre, title,
tvdb, mpaa, studio, sorttitle, showid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE TVSHOW #####
else:
self.logMsg("ADD tvshow itemid: %s - Title: %s" % (itemid, title), 1)
# Add top path
toppathid = kodi_db.addPath(toplevelpath)
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (toplevelpath, "tvshows", "metadata.local", 1, toppathid))
# Add path
pathid = kodi_db.addPath(path)
# Create the tvshow entry
kodicursor.execute("select coalesce(max(idShow),0) from tvshow")
showid = kodicursor.fetchone()[0] + 1
query = (
'''
INSERT INTO tvshow(
idShow, c00, c01, c04, c05, c08, c09, c12, c13, c14, c15)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (showid, title, plot, rating, premieredate, genre,
title, tvdb, mpaa, studio, sorttitle))
# Link the path
query = "INSERT INTO tvshowlinkpath(idShow, idPath) values(?, ?)"
kodicursor.execute(query, (showid, pathid))
# Create the reference in emby table
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
checksum=checksum, mediafolderid=viewid)
# Update the path
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (path, None, None, 1, pathid))
# Process cast
2016-01-10 02:14:02 +11:00
people = API.getPeopleList()
kodi_db.addPeople(showid, people, "tvshow")
# Process genres
kodi_db.addGenres(showid, genres, "tvshow")
# Process artwork
2016-01-10 02:14:02 +11:00
allartworks = API.getAllArtwork()
artwork.addArtwork(allartworks, showid, "tvshow", kodicursor)
# Process studios
kodi_db.addStudios(showid, studios, "tvshow")
# Process tags: view, emby tags
2016-01-12 01:56:56 +11:00
tags = [viewtag]
kodi_db.addTags(showid, tags, "tvshow")
2016-01-10 02:14:02 +11:00
def refreshSeasonEntry(self, item, showid):
API = PlexAPI.API(item)
kodicursor = self.kodicursor
kodi_db = self.kodi_db
# Finally, refresh the all season entry
seasonid = kodi_db.addSeason(showid, -1)
# Process artwork for season
allartworks = API.getAllArtwork()
artwork.addArtwork(allartworks, seasonid, "season", kodicursor)
def add_updateSeason(self, item, viewid=None, viewtag=None):
2016-01-11 01:16:59 +11:00
API = PlexAPI.API(item)
2016-01-10 02:14:02 +11:00
showid = viewid
2016-01-11 01:16:59 +11:00
itemid = API.getKey()
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
2016-01-10 02:14:02 +11:00
seasonnum = API.getIndex()
seasonid = kodi_db.addSeason(showid, seasonnum)
2016-01-12 03:37:01 +11:00
checksum = API.getChecksum()
# Check whether Season already exists
update_item = True
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
2016-01-10 02:14:02 +11:00
allartworks = API.getAllArtwork()
artwork.addArtwork(allartworks, seasonid, "season", kodicursor)
2016-01-11 01:16:59 +11:00
self.logMsg("Updated season %s, Plex Id: %s of Plex show Id: %s" % (
seasonnum, itemid, showid), 2)
2016-01-12 03:37:01 +11:00
if update_item:
# Update a reference: checksum in emby table
emby_db.updateReference(itemid, checksum)
else:
# Create the reference in emby table
emby_db.addReference(itemid, seasonid, "Season", "season", parentid=showid, checksum=checksum)
2016-01-10 02:14:02 +11:00
def add_updateEpisode(self, item, viewtag=None, viewid=None):
"""
viewtag and viewid are irrelevant!
"""
# Process single episode
kodiversion = self.kodiversion
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
2016-01-10 02:14:02 +11:00
API = PlexAPI.API(item)
# If the item already exist in the local Kodi DB we'll perform a full item update
# If the item doesn't exist, we'll add it to the database
update_item = True
2016-01-10 02:14:02 +11:00
itemid = API.getKey()
emby_dbitem = emby_db.getItem_byId(itemid)
2016-01-11 01:16:59 +11:00
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
# fileId information
checksum = API.getChecksum()
dateadded = API.getDateCreated()
userdata = API.getUserData()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
2016-01-11 01:16:59 +11:00
peoples = API.getPeople()
director = API.joinList(peoples['Director'])
writer = API.joinList(peoples['Writer'])
cast = API.joinList(peoples['Cast'])
producer = API.joinList(peoples['Producer'])
2016-01-10 02:14:02 +11:00
title, sorttitle = API.getTitle()
plot = API.getPlot()
rating = API.getAudienceRating()
2016-01-11 01:16:59 +11:00
resume, runtime = API.getRuntime()
premieredate = API.getPremiereDate()
# episode details
2016-01-10 02:14:02 +11:00
seriesId, seriesName, season, episode = API.getEpisodeDetails()
if season is None:
if item.get('AbsoluteEpisodeNumber'):
# Anime scenario
season = 1
episode = item['AbsoluteEpisodeNumber']
else:
season = -1
# Specials ordering within season
2016-01-10 02:14:02 +11:00
# if item.get('AirsAfterSeasonNumber'):
# airsBeforeSeason = item['AirsAfterSeasonNumber']
# airsBeforeEpisode = 4096 # Kodi default number for afterseason ordering
# else:
# airsBeforeSeason = item.get('AirsBeforeSeasonNumber', "-1")
# airsBeforeEpisode = item.get('AirsBeforeEpisodeNumber', "-1")
airsBeforeSeason = "-1"
airsBeforeEpisode = "-1"
# Append multi episodes to title
2016-01-10 02:14:02 +11:00
# if item.get('IndexNumberEnd'):
# title = "| %02d | %s" % (item['IndexNumberEnd'], title)
# Get season id
show = emby_db.getItem_byId(seriesId)
try:
showid = show[0]
except TypeError:
# Show is missing from database
show = self.emby.getItem(seriesId)
self.add_update(show)
show = emby_db.getItem_byId(seriesId)
try:
showid = show[0]
except TypeError:
2016-01-10 02:14:02 +11:00
self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId), -1)
return False
seasonid = kodi_db.addSeason(showid, season)
##### GET THE FILE AND PATH #####
playurl = API.getFilePath()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
if self.directpath:
# 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
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")
else:
# Set plugin path and media flags using real filename
path = "plugin://plugin.video.plexkodiconnect.tvshows/%s/" % seriesId
params = {
'filename': filename.encode('utf-8'),
'id': itemid,
'dbid': episodeid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE EPISODE #####
if update_item:
self.logMsg("UPDATE episode itemid: %s - Title: %s" % (itemid, title), 1)
# Update the movie entry
if kodiversion == 16:
# Kodi Jarvis
query = ' '.join((
"UPDATE episode",
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idSeason = ?",
"WHERE idEpisode = ?"
))
kodicursor.execute(query, (title, plot, rating, writer, premieredate,
runtime, director, season, episode, title, airsBeforeSeason,
airsBeforeEpisode, seasonid, episodeid))
else:
query = ' '.join((
"UPDATE episode",
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?",
"WHERE idEpisode = ?"
))
kodicursor.execute(query, (title, plot, rating, writer, premieredate,
runtime, director, season, episode, title, airsBeforeSeason,
airsBeforeEpisode, episodeid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
# Update parentid reference
emby_db.updateParentId(itemid, seasonid)
##### OR ADD THE EPISODE #####
else:
self.logMsg("ADD episode itemid: %s - Title: %s" % (itemid, title), 1)
# Add path
pathid = kodi_db.addPath(path)
# Add the file
fileid = kodi_db.addFile(filename, pathid)
# Create the episode entry
if kodiversion == 16:
# Kodi Jarvis
query = (
'''
INSERT INTO episode(
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
idShow, c15, c16, idSeason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (episodeid, fileid, title, plot, rating, writer,
premieredate, runtime, director, season, episode, title, showid,
airsBeforeSeason, airsBeforeEpisode, seasonid))
else:
query = (
'''
INSERT INTO episode(
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
idShow, c15, c16)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (episodeid, fileid, title, plot, rating, writer,
premieredate, runtime, director, season, episode, title, showid,
airsBeforeSeason, airsBeforeEpisode))
# Create the reference in emby table
emby_db.addReference(itemid, episodeid, "Episode", "episode", fileid, pathid,
seasonid, checksum)
# Update the path
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (path, None, None, 1, pathid))
# Update the file
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
# Process cast
2016-01-10 02:14:02 +11:00
people = API.getPeopleList()
kodi_db.addPeople(episodeid, people, "episode")
# Process artwork
2016-01-10 02:14:02 +11:00
artworks = API.getAllArtwork()
artwork.addOrUpdateArt(artworks['Primary'], episodeid, "episode", "thumb", kodicursor)
# Process stream details
streams = API.getMediaStreams()
kodi_db.addStreams(fileid, streams, runtime)
# Process playstates
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
if not self.directpath and resume:
# Create additional entry for widgets. This is only required for plugin/episode.
temppathid = kodi_db.getPath("plugin://plugin.video.plexkodiconnect.tvshows/")
tempfileid = kodi_db.addFile(filename, temppathid)
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid))
kodi_db.addPlaystate(tempfileid, resume, runtime, playcount, dateplayed)
def remove(self, itemid):
# Remove showid, fileid, pathid, emby reference
emby_db = self.emby_db
embycursor = self.embycursor
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
parentid = emby_dbitem[3]
mediatype = emby_dbitem[4]
self.logMsg("Removing %s kodiid: %s fileid: %s" % (mediatype, kodiid, fileid), 1)
except TypeError:
return
##### PROCESS ITEM #####
# Remove the emby reference
emby_db.removeItem(itemid)
##### IF EPISODE #####
if mediatype == "episode":
# Delete kodi episode and file, verify season and tvshow
self.removeEpisode(kodiid, fileid)
# Season verification
season = emby_db.getItem_byKodiId(parentid, "season")
try:
showid = season[1]
except TypeError:
return
season_episodes = emby_db.getItem_byParentId(parentid, "episode")
if not season_episodes:
self.removeSeason(parentid)
emby_db.removeItem(season[0])
# Show verification
show = emby_db.getItem_byKodiId(showid, "tvshow")
query = ' '.join((
"SELECT totalCount",
"FROM tvshowcounts",
"WHERE idShow = ?"
))
kodicursor.execute(query, (showid,))
result = kodicursor.fetchone()
if result and result[0] is None:
# There's no episodes left, delete show and any possible remaining seasons
seasons = emby_db.getItem_byParentId(showid, "season")
for season in seasons:
self.removeSeason(season[1])
else:
# Delete emby season entries
emby_db.removeItems_byParentId(showid, "season")
self.removeShow(showid)
emby_db.removeItem(show[0])
##### IF TVSHOW #####
elif mediatype == "tvshow":
# Remove episodes, seasons, tvshow
seasons = emby_db.getItem_byParentId(kodiid, "season")
for season in seasons:
seasonid = season[1]
season_episodes = emby_db.getItem_byParentId(seasonid, "episode")
for episode in season_episodes:
self.removeEpisode(episode[1], episode[2])
else:
# Remove emby episodes
emby_db.removeItems_byParentId(seasonid, "episode")
else:
# Remove emby seasons
emby_db.removeItems_byParentId(kodiid, "season")
# Remove tvshow
self.removeShow(kodiid)
##### IF SEASON #####
elif mediatype == "season":
# Remove episodes, season, verify tvshow
season_episodes = emby_db.getItem_byParentId(kodiid, "episode")
for episode in season_episodes:
self.removeEpisode(episode[1], episode[2])
else:
# Remove emby episodes
emby_db.removeItems_byParentId(kodiid, "episode")
# Remove season
self.removeSeason(kodiid)
# Show verification
seasons = emby_db.getItem_byParentId(parentid, "season")
if not seasons:
# There's no seasons, delete the show
self.removeShow(parentid)
emby_db.removeItem_byKodiId(parentid, "tvshow")
self.logMsg("Deleted %s: %s from kodi database" % (mediatype, itemid), 1)
def removeShow(self, kodiid):
kodicursor = self.kodicursor
artwork = self.artwork
artwork.deleteArtwork(kodiid, "tvshow", kodicursor)
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodiid,))
self.logMsg("Removed tvshow: %s." % kodiid, 2)
def removeSeason(self, kodiid):
kodicursor = self.kodicursor
artwork = self.artwork
artwork.deleteArtwork(kodiid, "season", kodicursor)
kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?", (kodiid,))
self.logMsg("Removed season: %s." % kodiid, 2)
def removeEpisode(self, kodiid, fileid):
kodicursor = self.kodicursor
artwork = self.artwork
artwork.deleteArtwork(kodiid, "episode", kodicursor)
kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?", (kodiid,))
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)
self.directstream = utils.settings('streamMusic') == "true"
self.userid = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userid)
def added(self, items, pdialog):
2015-12-30 23:42:37 +11:00
total = len(items)
count = 0
for artist in items:
title = artist['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_updateArtist(artist)
# Add albums
all_albums = self.emby.getAlbumsbyArtist(artist['Id'])
self.added_album(all_albums['Items'], pdialog)
def added_album(self, items, pdialog):
2015-12-30 23:42:37 +11:00
total = len(items)
count = 0
for album in items:
title = album['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_updateAlbum(album)
# Add songs
all_songs = self.emby.getSongsbyAlbum(album['Id'])
self.added_song(all_songs['Items'], pdialog)
def added_song(self, items, pdialog):
2015-12-30 23:42:37 +11:00
total = len(items)
count = 0
for song in items:
title = song['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
self.add_updateSong(song)
if not pdialog and self.contentmsg:
self.contentPop(title)
def add_updateArtist(self, item, artisttype="MusicArtist"):
# Process a single artist
kodiversion = self.kodiversion
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
API = api.API(item)
update_item = True
itemid = item['Id']
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 #####
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()
# Associate artwork
artworks = artwork.getAllArtwork(item, parentInfo=True)
thumb = artworks['Primary']
backdrops = artworks['Backdrop'] # List
if thumb:
thumb = "<thumb>%s</thumb>" % thumb
if backdrops:
fanart = "<fanart>%s</fanart>" % backdrops[0]
else:
fanart = ""
##### UPDATE THE ARTIST #####
if update_item:
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 #####
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.
artistid = kodi_db.addArtist(name, musicBrainzId)
# Create the reference in emby table
emby_db.addReference(itemid, artistid, artisttype, "artist", checksum=checksum)
# Process the artist
if kodiversion == 16:
query = ' '.join((
"UPDATE artist",
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
"lastScraped = ?",
"WHERE idArtist = ?"
))
kodicursor.execute(query, (genres, bio, thumb, fanart, lastScraped, artistid))
else:
query = ' '.join((
"UPDATE artist",
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
"lastScraped = ?, dateAdded = ?",
"WHERE idArtist = ?"
))
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
kodiversion = self.kodiversion
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
artwork = self.artwork
API = api.API(item)
update_item = True
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
albumid = emby_dbitem[0]
except TypeError:
update_item = False
self.logMsg("albumid: %s not found." % itemid, 2)
##### 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()
rating = userdata['UserRating']
artists = item['AlbumArtists']
if not artists:
artists = item['ArtistItems']
artistname = []
for artist in artists:
artistname.append(artist['Name'])
artistname = " / ".join(artistname)
# Associate artwork
artworks = artwork.getAllArtwork(item, parentInfo=True)
thumb = artworks['Primary']
if thumb:
thumb = "<thumb>%s</thumb>" % thumb
##### UPDATE THE ALBUM #####
if update_item:
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 #####
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.
albumid = kodi_db.addAlbum(name, musicBrainzId)
# Create the reference in emby table
emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum)
# Process the album info
if kodiversion == 16:
# Kodi Jarvis
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
"album", albumid))
elif kodiversion == 15:
# Kodi Isengard
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
dateadded, "album", albumid))
else:
# Kodi Helix
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, dateAdded = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
dateadded, albumid))
# Associate the parentid for emby reference
parentId = item.get('ParentId')
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)
# Item may not be an artist, verification necessary.
if artist['Type'] == "MusicArtist":
# Update with the parentId, for remove reference
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']
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")
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,))
# Add artist to album
query = (
'''
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, albumid, artistname))
# Update discography
query = (
'''
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)
# Add genres
kodi_db.addMusicGenres(albumid, genres, "album")
# Update artwork
artwork.addArtwork(artworks, albumid, "album", kodicursor)
def add_updateSong(self, item):
# 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)
update_item = True
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
songid = emby_dbitem[0]
pathid = emby_dbitem[2]
albumid = emby_dbitem[3]
except TypeError:
update_item = False
self.logMsg("songid: %s not found." % itemid, 2)
##### The song details #####
checksum = API.getChecksum()
dateadded = API.getDateCreated()
userdata = API.getUserData()
playcount = userdata['PlayCount']
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)
if disc == 1:
track = tracknumber
else:
track = disc*2**16 + tracknumber
year = item.get('ProductionYear')
duration = API.getRuntime()
#the server only returns the rating based on like/love and not the actual rating from the song
rating = userdata['UserRating']
#the server doesn't support comment on songs so this will always be empty
comment = API.getOverview()
#if enabled, try to get the rating and comment value from the file itself
if not self.directstream:
rating, comment = self.getSongRatingAndComment(itemid, rating, API)
##### GET THE FILE AND PATH #####
if self.directstream:
path = "%s/emby/Audio/%s/" % (self.server, itemid)
filename = "stream.mp3"
else:
playurl = API.getFilePath()
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 #####
if update_item:
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))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE SONG #####
else:
self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1)
# Add path
pathid = kodi_db.addPath(path)
try:
2016-01-01 15:19:36 +11:00
# Get the album
emby_dbalbum = emby_db.getItem_byId(item['AlbumId'])
albumid = emby_dbalbum[0]
2016-01-01 15:19:36 +11:00
except KeyError:
# No album Id associated to the song.
self.logMsg("Song itemid: %s has no albumId." % itemid, 1)
return
except TypeError:
# No album found, create a single's album
kodicursor.execute("select coalesce(max(idAlbum),0) from album")
albumid = kodicursor.fetchone()[0] + 1
if kodiversion == 16:
# Kodi Jarvis
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (albumid, genre, year, "single"))
elif kodiversion == 15:
# Kodi Isengard
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded, strReleaseType)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (albumid, genre, year, dateadded, "single"))
else:
# Kodi Helix
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded)
VALUES (?, ?, ?, ?)
'''
)
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(
idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
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,
checksum=checksum)
# Link song to album
query = (
'''
INSERT OR REPLACE INTO albuminfosong(
idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (songid, albumid, track, title, duration))
# Verify if album has artists
addArtist = False
query = ' '.join((
"SELECT strArtists",
"FROM album",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (albumid,))
result = kodicursor.fetchone()
if result and result[0] == "":
addArtist = True
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'])
try:
artistid = emby_dbartist[0]
except: pass
else:
query = (
'''
INSERT OR REPLACE INTO song_artist(idArtist, idSong, strArtist)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, songid, artist_name))
if addArtist:
query = (
'''
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, albumid, artist_name))
else:
if addArtist:
artists_onalbum = " / ".join(artists_name)
if kodiversion == 16:
# Kodi Jarvis
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
kodi_db.addMusicGenres(songid, genres, "song")
# Update artwork
artwork.addArtwork(artwork.getAllArtwork(item, parentInfo=True), 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":
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
rating, comment = self.getSongRatingAndComment(itemid, rating, API)
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 getSongRatingAndComment(self, embyid, emby_rating, API):
kodicursor = self.kodicursor
previous_values = None
filename = API.getFilePath()
rating = 0
emby_rating = int(round(emby_rating, 0))
file_rating, comment = musicutils.getSongTags(filename)
emby_dbitem = self.emby_db.getItem_byId(embyid)
try:
kodiid = emby_dbitem[0]
except TypeError:
# Item is not in database.
currentvalue = None
else:
query = "SELECT rating FROM song WHERE idSong = ?"
kodicursor.execute(query, (kodiid,))
currentvalue = int(round(float(kodicursor.fetchone()[0]),0))
# Only proceed if we actually have a rating from the file
if file_rating is None and currentvalue:
return (currentvalue, comment)
elif file_rating is None and currentvalue is None:
return (emby_rating, comment)
file_rating = int(round(file_rating,0))
self.logMsg("getSongRatingAndComment --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
updateFileRating = False
updateEmbyRating = False
if currentvalue != None:
# we need to translate the emby values...
if emby_rating == 1 and currentvalue == 2:
emby_rating = 2
if emby_rating == 3 and currentvalue == 4:
emby_rating = 4
if (emby_rating == file_rating) and (file_rating != currentvalue):
#the rating has been updated from kodi itself, update change to both emby ands file
rating = currentvalue
updateFileRating = True
updateEmbyRating = True
elif (emby_rating != currentvalue) and (file_rating == currentvalue):
#emby rating changed - update the file
rating = emby_rating
updateFileRating = True
elif (file_rating != currentvalue) and (emby_rating == currentvalue):
#file rating was updated, sync change to emby
rating = file_rating
updateEmbyRating = True
elif (emby_rating != currentvalue) and (file_rating != currentvalue):
#both ratings have changed (corner case) - the highest rating wins...
if emby_rating > file_rating:
rating = emby_rating
updateFileRating = True
else:
rating = file_rating
updateEmbyRating = True
else:
#nothing has changed, just return the current value
rating = currentvalue
else:
# no rating yet in DB, always prefer file details...
rating = file_rating
updateEmbyRating = True
if updateFileRating:
musicutils.updateRatingToFile(rating, filename)
if updateEmbyRating:
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(rating)
API.updateUserRating(embyid, like, favourite, deletelike)
return (rating, comment)
def remove(self, itemid):
# Remove kodiid, fileid, pathid, emby reference
emby_db = self.emby_db
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
mediatype = emby_dbitem[4]
self.logMsg("Removing %s kodiid: %s" % (mediatype, kodiid), 1)
except TypeError:
return
##### PROCESS ITEM #####
# Remove the emby reference
emby_db.removeItem(itemid)
##### IF SONG #####
if mediatype == "song":
# Delete song
self.removeSong(kodiid)
##### IF ALBUM #####
elif mediatype == "album":
# Delete songs, album
album_songs = emby_db.getItem_byParentId(kodiid, "song")
for song in album_songs:
self.removeSong(song[1])
else:
# Remove emby songs
emby_db.removeItems_byParentId(kodiid, "song")
# Remove the album
self.removeAlbum(kodiid)
##### IF ARTIST #####
elif mediatype == "artist":
# Delete songs, album, artist
albums = emby_db.getItem_byParentId(kodiid, "album")
for album in albums:
albumid = album[1]
album_songs = emby_db.getItem_byParentId(albumid, "song")
for song in album_songs:
self.removeSong(song[1])
else:
# Remove emby song
emby_db.removeItems_byParentId(albumid, "song")
# Remove emby artist
emby_db.removeItems_byParentId(albumid, "artist")
# Remove kodi album
self.removeAlbum(albumid)
else:
# Remove emby albums
emby_db.removeItems_byParentId(kodiid, "album")
# Remove artist
self.removeArtist(kodiid)
self.logMsg("Deleted %s: %s from kodi database" % (mediatype, itemid), 1)
def removeSong(self, kodiid):
kodicursor = self.kodicursor
artwork = self.artwork
artwork.deleteArtwork(kodiid, "song", kodicursor)
kodicursor.execute("DELETE FROM song WHERE idSong = ?", (kodiid,))
def removeAlbum(self, kodiid):
kodicursor = self.kodicursor
artwork = self.artwork
artwork.deleteArtwork(kodiid, "album", kodicursor)
kodicursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodiid,))
def removeArtist(self, kodiid):
kodicursor = self.kodicursor
artwork = self.artwork
artwork.deleteArtwork(kodiid, "artist", kodicursor)
kodicursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodiid,))