Merge pull request #3 from MediaBrowser/database_changes

Database changes
This commit is contained in:
Ian Mclaughlin 2015-05-04 17:55:16 +01:00
commit 5f486468ba
16 changed files with 1792 additions and 2694 deletions

View file

@ -2,6 +2,7 @@ import xbmcaddon
import xbmcplugin
import xbmc
import xbmcgui
import xbmcvfs
import os
import threading
import json
@ -12,24 +13,80 @@ cwd = addonSettings.getAddonInfo('path')
BASE_RESOURCE_PATH = xbmc.translatePath( os.path.join( cwd, 'resources', 'lib' ) )
sys.path.append(BASE_RESOURCE_PATH)
WINDOW = xbmcgui.Window( 10000 )
WINDOW = xbmcgui.Window(10000)
import Utils as utils
from PlaybackUtils import PlaybackUtils
from DownloadUtils import DownloadUtils
from ReadEmbyDB import ReadEmbyDB
from API import API
try:
params=utils.get_params(sys.argv[2])
mode = params.get('mode',"")
id = params.get('id',"")
params = utils.get_params(sys.argv[2])
mode = params['mode']
id = params['id']
except:
params={}
mode=None
id=None
params = {}
mode = None
if mode == "play":
# Play items via plugin://plugin.video.emby/
url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % id
result = DownloadUtils().downloadUrl(url)
item = PlaybackUtils().PLAY(result, setup="default")
#get extrafanart for listitem - this will only be used for skins that actually call the listitem's path + fanart dir...
elif "extrafanart" in sys.argv[0]:
itemPath = ""
embyId = ""
try:
#only do this if the listitem has actually changed
itemPath = xbmc.getInfoLabel("ListItem.FileNameAndPath")
if not itemPath:
itemPath = xbmc.getInfoLabel("ListItem.Path")
if ("/tvshows/" in itemPath or "/musicvideos/" in itemPath or "/movies/" in itemPath):
embyId = itemPath.split("/")[-2]
#we need to store the images locally for this to work because of the caching system in xbmc
fanartDir = xbmc.translatePath("special://thumbnails/emby/" + embyId + "/")
if not xbmcvfs.exists(fanartDir):
#download the images to the cache directory
xbmcvfs.mkdir(fanartDir)
item = ReadEmbyDB().getFullItem(embyId)
if item != None:
if item.has_key("BackdropImageTags"):
if(len(item["BackdropImageTags"]) > 1):
totalbackdrops = len(item["BackdropImageTags"])
for index in range(1,totalbackdrops):
backgroundUrl = API().getArtwork(item, "Backdrop",str(index))
fanartFile = os.path.join(fanartDir,"fanart" + str(index) + ".jpg")
li = xbmcgui.ListItem(str(index), path=fanartFile)
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=fanartFile, listitem=li)
xbmcvfs.copy(backgroundUrl,fanartFile)
else:
#use existing cached images
dirs, files = xbmcvfs.listdir(fanartDir)
count = 1
for file in files:
count +=1
li = xbmcgui.ListItem(file, path=os.path.join(fanartDir,file))
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=os.path.join(fanartDir,file), listitem=li)
except:
pass
#always do endofdirectory to prevent errors in the logs
xbmcplugin.endOfDirectory(int(sys.argv[1]))
if mode != None and mode == "play":
PlaybackUtils().PLAY(id)
elif sys.argv[1] == "reset":
utils.reset()
else:
else:
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')

View file

@ -84,6 +84,7 @@ class API():
width = ''
aspectratio = '1:1'
aspectfloat = 1.85
Video3DFormat = ''
if mediaSources == True:
mediaSources = item.get("MediaSources")
@ -99,9 +100,10 @@ class API():
for mediaStream in MediaStreams:
if(mediaStream.get("Type") == "Video"):
videocodec = mediaStream.get("Codec")
height = str(mediaStream.get("Height"))
width = str(mediaStream.get("Width"))
height = int(mediaStream.get("Height"))
width = int(mediaStream.get("Width"))
aspectratio = mediaStream.get("AspectRatio")
Video3DFormat = item.get("Video3DFormat")
if aspectratio != None and len(aspectratio) >= 3:
try:
aspectwidth,aspectheight = aspectratio.split(':')
@ -116,9 +118,30 @@ class API():
'audiocodec' : audiocodec,
'height' : height,
'width' : width,
'aspectratio' : str(aspectfloat)
'aspectratio' : aspectfloat,
'3dformat' : Video3DFormat
}
def getChecksum(self, item):
# use the etags checksum for this if available
# AND the userdata
checksum = ""
if item.get("Etag") != None:
checksum = item.get("Etag")
userData = item.get("UserData")
if(userData != None):
checksum += str(userData.get("Played"))
checksum += str(userData.get("IsFavorite"))
if userData.get('UnplayedItemCount') != None:
checksum += str(userData.get("UnplayedItemCount"))
if userData.get('LastPlayedDate') != None:
checksum += str(userData.get("LastPlayedDate"))
if userData.get('PlaybackPositionTicks') != None:
checksum += str(userData.get("PlaybackPositionTicks"))
return checksum
def getUserData(self, item):
userData = item.get("UserData")
resumeTime = 0
@ -128,9 +151,9 @@ class API():
else:
watched="False"
if userData.get("IsFavorite") == True:
favorite="True"
favorite=True
else:
favorite="False"
favorite=False
if(userData.get("Played") == True):
playcount="1"
else:

View file

@ -116,12 +116,12 @@ class ConnectionManager():
return
# Option to play from http
setPlayback = xbmcgui.Dialog().yesno("Playback option", "Play your files using HTTP?")
if setPlayback == 1:
self.logMsg("Playback will be set using HTTP.", 1)
addon.setSetting("playFromStream", "true")
else:
self.logMsg("Playback will be set using SMB.", 1)
#setPlayback = xbmcgui.Dialog().yesno("Playback option", "Play your files using HTTP?")
#if setPlayback == 1:
#self.logMsg("Playback will be set using HTTP.", 1)
#addon.setSetting("playFromStream", "true")
#else:
#self.logMsg("Playback will be set using SMB.", 1)
def getServerDetails(self):

View file

@ -11,8 +11,8 @@ from ClientInformation import ClientInformation
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# Disable requests logging
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
logging.getLogger("requests").setLevel(logging.WARNING)
# requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# logging.getLogger("requests").setLevel(logging.WARNING)
class DownloadUtils():
@ -67,26 +67,22 @@ class DownloadUtils():
def postCapabilities(self, deviceId):
# Get sessionId
url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
result = self.downloadUrl(url)
# sessionId result
self.logMsg("Session result: %s" % result, 2)
self.sessionId = result[0][u'Id']
self.WINDOW.setProperty('sessionId%s' % self.username, self.sessionId)
# Post settings to session
url = "{server}/mediabrowser/Sessions/Capabilities/Full"
data = {
'PlayableMediaTypes': "Audio,Video",
'SupportedCommands': "Play,Playstate,SendString,DisplayMessage,PlayNext",
'SupportsMediaControl': True
}
# Settings for capabilities
playableMediaTypes = "Audio,Video"
supportedCommands = "Play,Playstate,SendString,DisplayMessage,PlayNext"
# Post settings to sessionId
url = "{server}/mediabrowser/Sessions/Capabilities?Id=%s&PlayableMediaTypes=%s&SupportedCommands=%s&SupportsMediaControl=True" % (self.sessionId, playableMediaTypes, supportedCommands)
data = {}
self.logMsg("Capabilities URL: %s" % url, 2)
self.logMsg("PostData: %s" % data, 2)
self.downloadUrl(url, postBody=data, type="POST")
self.logMsg("Posted capabilities to sessionId: %s" % self.sessionId, 1)
try:
self.downloadUrl(url, postBody=data, type="POST")
self.logMsg("Posted capabilities to %s" % self.server, 1)
except:
self.logMsg("Posted capabilities failed.")
def startSession(self):
@ -99,9 +95,11 @@ class DownloadUtils():
header = self.getHeader()
# If user enabled host certificate verification
if self.sslverify:
verify = True
try:
verify = self.sslverify
cert = self.sslclient
except:
self.logMsg("Could not load SSL settings.", 1)
# Start session
self.s = requests.Session()
@ -153,52 +151,86 @@ class DownloadUtils():
timeout = self.timeout
default_link = ""
# If user is authenticated
if (authenticate):
# Get requests session
s = self.s
# Replace for the real values and append api_key
url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
#url = "%s&api_key=%s" % (url, self.token)
self.logMsg("URL: %s" % url, 2)
# Prepare request
if type == "GET":
r = s.get(url, json=postBody, timeout=timeout)
elif type == "POST":
r = s.post(url, json=postBody, timeout=timeout)
elif type == "DELETE":
r = s.delete(url, json=postBody, timeout=timeout)
# If user is not authenticated
elif not authenticate:
self.logMsg("URL: %s" % url, 1)
header = self.getHeader(authenticate=False)
verifyssl = False
# If user enables ssl verification
try:
verifyssl = self.sslverify
except AttributeError:
pass
# Prepare request
if type == "GET":
r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
elif type == "POST":
r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
# Process the response
try:
r.raise_for_status()
# If user is authenticated
if (authenticate):
# Get requests session
try:
s = self.s
# Replace for the real values and append api_key
url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
#url = "%s&api_key=%s" % (url, self.token)
self.logMsg("URL: %s" % url, 2)
# Prepare request
if type == "GET":
r = s.get(url, json=postBody, timeout=timeout)
elif type == "POST":
r = s.post(url, json=postBody, timeout=timeout)
elif type == "DELETE":
r = s.delete(url, json=postBody, timeout=timeout)
except AttributeError:
# Get user information
self.username = WINDOW.getProperty('currUser')
self.userId = WINDOW.getProperty('userId%s' % self.username)
self.server = WINDOW.getProperty('server%s' % self.username)
self.token = WINDOW.getProperty('accessToken%s' % self.username)
header = self.getHeader()
verifyssl = False
cert = None
# IF user enables ssl verification
try:
if self.addon.getSetting('sslverify') == "true":
verifyssl = True
if self.addon.getSetting('sslcert') != "None":
cert = self.addon.getSetting('sslcert')
except:
self.logMsg("Could not load SSL settings.", 1)
pass
# Replace for the real values and append api_key
url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
self.logMsg("URL: %s" % url, 2)
# Prepare request
if type == "GET":
r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
elif type == "POST":
r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
elif type == "DELETE":
r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
# If user is not authenticated
elif not authenticate:
self.logMsg("URL: %s" % url, 2)
header = self.getHeader(authenticate=False)
verifyssl = False
# If user enables ssl verification
try:
verifyssl = self.sslverify
except AttributeError:
pass
# Prepare request
if type == "GET":
r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
elif type == "POST":
r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
# Process the response
if r.status_code == 204:
# No response in body
# No body in the response
self.logMsg("====== 204 Success ======", 2)
return default_link
# Response code 200
elif r.status_code == requests.codes.ok:
try:
# UTF-8 - JSON object
@ -207,13 +239,19 @@ class DownloadUtils():
return r
except:
self.logMsg("Unable to convert the response for: %s" % url, 1)
else:
r.raise_for_status()
return default_link
# TO REVIEW EXCEPTIONS
except requests.exceptions.ConnectionError as e:
self.logMsg("Server unreachable at: %s" % url, 0)
self.logMsg(e, 1)
# Make the addon aware of status
if WINDOW.getProperty("Server_online") != "false":
self.logMsg("Server unreachable at: %s" % url, 0)
self.logMsg(e, 2)
WINDOW.setProperty("Server_online", "false")
pass
except requests.exceptions.ConnectTimeout as e:
self.logMsg("Server timeout at: %s" % url, 0)
@ -230,6 +268,7 @@ class DownloadUtils():
# Tell UserClient token has been revoked.
WINDOW.setProperty("Server_status", "401")
self.logMsg("HTTP Error: %s" % e, 0)
xbmcgui.Dialog().notification("Error connecting", "Unauthorized.", xbmcgui.NOTIFICATION_ERROR)
elif (r.status_code == 301) or (r.status_code == 302):
# Redirects

View file

@ -11,8 +11,10 @@ import json
import Utils as utils
from WriteKodiDB import WriteKodiDB
from ReadKodiDB import ReadKodiDB
from LibrarySync import LibrarySync
from PlayUtils import PlayUtils
from DownloadUtils import DownloadUtils
from PlaybackUtils import PlaybackUtils
class Kodi_Monitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
@ -26,65 +28,7 @@ class Kodi_Monitor(xbmc.Monitor):
def onNotification (self,sender,method,data):
addon = xbmcaddon.Addon(id='plugin.video.emby')
downloadUtils = DownloadUtils()
print "onNotification:" + method + ":" + sender + ":" + str(data)
#player started playing an item -
if method == "Player.OnPlay":
print "playlist onadd is called"
jsondata = json.loads(data)
if jsondata != None:
if jsondata.has_key("item"):
if jsondata.get("item").has_key("id") and jsondata.get("item").has_key("type"):
id = jsondata.get("item").get("id")
type = jsondata.get("item").get("type")
embyid = ReadKodiDB().getEmbyIdByKodiId(id,type)
if embyid != None:
WINDOW = xbmcgui.Window( 10000 )
username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % embyid
result = downloadUtils.downloadUrl(url)
userData = result[u'UserData']
playurl = PlayUtils().getPlayUrl(server, embyid, result)
watchedurl = "%s/mediabrowser/Users/%s/PlayedItems/%s" % (server, userid, embyid)
positionurl = "%s/mediabrowser/Users/%s/PlayingItems/%s" % (server, userid, embyid)
deleteurl = "%s/mediabrowser/Items/%s" % (server, embyid)
# set the current playing info
WINDOW.setProperty(playurl+"watchedurl", watchedurl)
WINDOW.setProperty(playurl+"positionurl", positionurl)
WINDOW.setProperty(playurl+"deleteurl", "")
WINDOW.setProperty(playurl+"deleteurl", deleteurl)
if result[u'Type']=="Episode":
WINDOW.setProperty(playurl+"refresh_id", result[u'SeriesId'])
else:
WINDOW.setProperty(playurl+"refresh_id", embyid)
WINDOW.setProperty(playurl+"runtimeticks", str(result[u'RunTimeTicks']))
WINDOW.setProperty(playurl+"type", result[u'Type'])
WINDOW.setProperty(playurl+"item_id", embyid)
if PlayUtils().isDirectPlay(result) == True:
playMethod = "DirectPlay"
else:
playMethod = "Transcode"
WINDOW.setProperty(playurl+"playmethod", playMethod)
mediaSources = result[u'MediaSources']
if(mediaSources != None):
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
WINDOW.setProperty(playurl+"AudioStreamIndex", str(mediaSources[0][u'DefaultAudioStreamIndex']))
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
WINDOW.setProperty(playurl+"SubtitleStreamIndex", str(mediaSources[0][u'DefaultSubtitleStreamIndex']))
if method == "VideoLibrary.OnUpdate":
jsondata = json.loads(data)
if jsondata != None:
@ -97,6 +41,12 @@ class Kodi_Monitor(xbmc.Monitor):
utils.logMsg("MB# Sync","Kodi_Monitor--> VideoLibrary.OnUpdate : " + str(data),2)
WriteKodiDB().updatePlayCountFromKodi(item, type, playcount)
if method == "System.OnWake":
xbmc.sleep(10000) #Allow network to wake up
utils.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Started)",1)
libSync = LibrarySync().FullLibrarySync()
utils.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Finished) " + str(libSync),1)

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,7 @@ class PlaybackUtils():
def __init__(self, *args):
pass
def PLAY(self, id):
def PLAY(self, result, setup="service"):
xbmc.log("PLAY Called")
WINDOW = xbmcgui.Window(10000)
@ -43,43 +43,22 @@ class PlaybackUtils():
userid = WINDOW.getProperty('userId%s' % username)
server = WINDOW.getProperty('server%s' % username)
url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % id
result = self.downloadUtils.downloadUrl(url)
try:
id = result["Id"]
except:
return
userData = result[u'UserData']
userData = result['UserData']
resume_result = 0
seekTime = 0
#get the resume point from Kodi DB for a Movie
kodiItem = ReadKodiDB().getKodiMovie(id)
if kodiItem != None:
seekTime = int(round(kodiItem['resume'].get("position")))
else:
#get the resume point from Kodi DB for an episode
episodeItem = ReadEmbyDB().getItem(id)
if episodeItem != None and str(episodeItem["Type"]) == "Episode":
kodiItem = ReadKodiDB().getKodiEpisodeByMbItem(id,episodeItem["SeriesId"])
if kodiItem != None:
seekTime = int(round(kodiItem['resume'].get("position")))
if userData.get("PlaybackPositionTicks") != 0:
reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
seekTime = reasonableTicks / 10000
playurl = PlayUtils().getPlayUrl(server, id, result)
isStrmFile = False
thumbPath = API().getArtwork(result, "Primary")
#workaround for when the file to play is a strm file itself
if playurl.endswith(".strm"):
isStrmFile = True
tempPath = os.path.join(addondir,"library","temp.strm")
xbmcvfs.copy(playurl, tempPath)
sfile = open(tempPath, 'r')
playurl = sfile.readline()
sfile.close()
xbmcvfs.delete(tempPath)
WINDOW.setProperty("virtualstrm", id)
WINDOW.setProperty("virtualstrmtype", result.get("Type"))
listItem = xbmcgui.ListItem(path=playurl, iconImage=thumbPath, thumbnailImage=thumbPath)
self.setListItemProps(server, id, listItem, result)
@ -97,17 +76,19 @@ class PlaybackUtils():
WINDOW.setProperty(playurl+"deleteurl", "")
WINDOW.setProperty(playurl+"deleteurl", deleteurl)
if seekTime != 0:
displayTime = str(datetime.timedelta(seconds=seekTime))
display_list = [ self.language(30106) + ' ' + displayTime, self.language(30107)]
resumeScreen = xbmcgui.Dialog()
resume_result = resumeScreen.select(self.language(30105), display_list)
if resume_result == 0:
WINDOW.setProperty(playurl+"seektime", str(seekTime))
#show the additional resume dialog if launched from a widget
if xbmc.getCondVisibility("Window.IsActive(home)"):
if seekTime != 0:
displayTime = str(datetime.timedelta(seconds=seekTime))
display_list = [ self.language(30106) + ' ' + displayTime, self.language(30107)]
resumeScreen = xbmcgui.Dialog()
resume_result = resumeScreen.select(self.language(30105), display_list)
if resume_result == 0:
WINDOW.setProperty(playurl+"seektime", str(seekTime))
else:
WINDOW.clearProperty(playurl+"seektime")
else:
WINDOW.clearProperty(playurl+"seektime")
else:
WINDOW.clearProperty(playurl+"seektime")
if result.get("Type")=="Episode":
WINDOW.setProperty(playurl+"refresh_id", result.get("SeriesId"))
@ -132,15 +113,15 @@ class PlaybackUtils():
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
WINDOW.setProperty(playurl+"SubtitleStreamIndex", str(mediaSources[0].get('DefaultSubtitleStreamIndex')))
#this launches the playback
#artwork only works with both resolvedurl and player command
if isStrmFile:
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
else:
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
if(addon.getSetting("addExtraPlaybackArt") == "true"):
utils.logMsg("PLAY", "Doing second xbmc.Player().play to add extra art")
#launch the playback
if setup == "service":
xbmc.Player().play(playurl,listItem)
elif setup == "default":
#artwork only works from widgets (home screen) with player command as there is no listitem selected
if xbmc.getCondVisibility("Window.IsActive(home)"):
xbmc.Player().play(playurl,listItem)
else:
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
def setArt(self, list,name,path):
if name=='thumb' or name=='fanart_image' or name=='small_poster' or name=='tiny_poster' or name == "medium_landscape" or name=='medium_poster' or name=='small_fanartimage' or name=='medium_fanartimage' or name=='fanart_noindicators':

View file

@ -90,9 +90,9 @@ class Player( xbmc.Player ):
self.logMsg("emby Service -> Percent Complete:" + str(percentComplete) + " Mark Played At:" + str(markPlayedAt))
self.stopPlayback(data)
if(refresh_id != None):
#if(refresh_id != None):
#report updates playcount and resume status to Kodi and MB3
librarySync.updatePlayCount(item_id)
#librarySync.updatePlayCount(item_id)
self.played_information.clear()
@ -138,6 +138,10 @@ class Player( xbmc.Player ):
self.logMsg("reportPlayback Called", 2)
xbmcplayer = self.xbmcplayer
if not xbmcplayer.isPlaying():
self.logMsg("reportPlayback: Not playing anything so returning", 0)
return
currentFile = xbmcplayer.getPlayingFile()
data = self.played_information.get(currentFile)
@ -176,7 +180,7 @@ class Player( xbmc.Player ):
postdata['SubtitleStreamIndex'] = subtitleindex
postdata = json.dumps(postdata)
self.logMsg("Report: %s" % postdata)
self.logMsg("Report: %s" % postdata, 2)
self.ws.sendProgressUpdate(postdata)
def onPlayBackPaused( self ):
@ -204,7 +208,11 @@ class Player( xbmc.Player ):
self.stopAll()
if xbmcplayer.isPlaying():
currentFile = xbmcplayer.getPlayingFile()
currentFile = ""
try:
currentFile = xbmcplayer.getPlayingFile()
except: pass
self.logMsg("onPlayBackStarted: %s" % currentFile, 0)
# we may need to wait until the info is available
@ -321,14 +329,11 @@ class Player( xbmc.Player ):
def autoPlayPlayback(self):
currentFile = xbmc.Player().getPlayingFile()
data = self.played_information.get(currentFile)
# only report playback if emby has initiated the playback (item_id has value)
if(data != None and data.get("item_id") != None):
addonSettings = xbmcaddon.Addon(id='plugin.video.emby')
item_id = data.get("item_id")
type = data.get("Type")
# if its an episode see if autoplay is enabled
if addonSettings.getSetting("autoPlaySeason")=="true" and type=="Episode":
WINDOW = xbmcgui.Window( 10000 )
@ -343,8 +348,10 @@ class Player( xbmc.Player ):
seasonId = MB3Episode["SeasonId"]
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s&ImageTypeLimit=1&Limit=1&SortBy=SortName&SortOrder=Ascending&Filters=IsUnPlayed&IncludeItemTypes=Episode&IsVirtualUnaired=false&Recursive=true&IsMissing=False&format=json" % seasonId
jsonData = self.doUtils.downloadUrl(url)
if(jsonData != ""):
seasonData = json.loads(jsonData)
seasonData = jsonData
if seasonData.get("Items") != None:
item = seasonData.get("Items")[0]
pDialog.create("Auto Play next episode", str(item.get("ParentIndexNumber")) + "x" + str(item.get("IndexNumber")) + ". " + item["Name"] + " found","Cancel to stop automatic play")
@ -365,5 +372,5 @@ class Player( xbmc.Player ):
xbmc.sleep(500)
playTime = xbmc.Player().getTime()
totalTime = xbmc.Player().getTotalTime()
PlaybackUtils().PLAYAllEpisodes(seasonData.get("Items"))

View file

@ -6,30 +6,22 @@ import xbmc
import xbmcgui
import xbmcaddon
from DownloadUtils import DownloadUtils
addon = xbmcaddon.Addon(id='plugin.video.emby')
class ReadEmbyDB():
def getMovies(self, id, fullinfo = False, fullSync = True, itemList = []):
def getMovies(self, id, itemList = []):
result = None
doUtils = DownloadUtils()
if fullSync:
sortstring = "&SortBy=SortName"
else:
if(len(itemList) > 0): # if we want a certain list specify it
#sortstring = "&Ids=" + ",".join(itemList)
sortstring = "" # work around for now until ParetnId and Id work together
else: # just get the last 20 created items
sortstring = "&Limit=20&SortBy=DateCreated"
if fullinfo:
url = "{server}/mediabrowser/Users/{UserId}/items?ParentId=%s%s&Fields=Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&Recursive=true&SortOrder=Descending&IncludeItemTypes=Movie&CollapseBoxSetItems=false&format=json&ImageTypeLimit=1" % (id, sortstring)
else:
url = "{server}/mediabrowser/Users/{UserId}/items?ParentId=%s%s&Fields=CumulativeRunTimeTicks&Recursive=true&SortOrder=Descending&IncludeItemTypes=Movie&CollapseBoxSetItems=false&format=json&ImageTypeLimit=1" % (id, sortstring)
#only get basic info for our sync-compares
sortstring = "&SortBy=SortName"
url = "{server}/mediabrowser/Users/{UserId}/items?ParentId=%s%s&Fields=CumulativeRunTimeTicks,Etag&Recursive=true&SortOrder=Descending&IncludeItemTypes=Movie&CollapseBoxSetItems=false&format=json&ImageTypeLimit=1" % (id, sortstring)
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):
@ -37,8 +29,8 @@ class ReadEmbyDB():
if (jsonData[u'Items'] != ""):
result = jsonData[u'Items']
# work around for now until ParetnId and Id work together
# Work around to only return items from the given list
if (result != None and len(result) > 0 and len(itemList) > 0):
newResult = []
for item in result:
@ -48,20 +40,14 @@ class ReadEmbyDB():
return result
def getMusicVideos(self, fullinfo = False, fullSync = True):
def getMusicVideos(self, itemList = []):
result = None
doUtils = DownloadUtils()
if not fullSync:
sortstring = "&Limit=20&SortBy=DateCreated"
else:
sortstring = "&SortBy=SortName"
if fullinfo:
url = "{server}/mediabrowser/Users/{UserId}/items?%s&Fields=Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&Recursive=true&SortOrder=Descending&IncludeItemTypes=MusicVideo&format=json&ImageTypeLimit=1" % sortstring
else:
url = "{server}/mediabrowser/Users/{UserId}/items?%s&Fields=CumulativeRunTimeTicks&Recursive=true&SortOrder=Descending&IncludeItemTypes=MusicVideo&CollapseBoxSetItems=false&format=json&ImageTypeLimit=1" % sortstring
#only get basic info for our sync-compares
sortstring = "&SortBy=SortName"
url = "{server}/mediabrowser/Users/{UserId}/items?%s&Fields=CumulativeRunTimeTicks,Etag&Recursive=true&SortOrder=Descending&IncludeItemTypes=MusicVideo&CollapseBoxSetItems=false&format=json&ImageTypeLimit=1" % sortstring
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):
@ -69,6 +55,14 @@ class ReadEmbyDB():
if (jsonData[u'Items'] != ""):
result = jsonData[u'Items']
# Work around to only return items from the given list
if (result != None and len(result) > 0 and len(itemList) > 0):
newResult = []
for item in result:
if (item[u'Id'] in itemList):
newResult.append(item)
result = newResult
return result
@ -98,20 +92,14 @@ class ReadEmbyDB():
return result
def getTVShows(self, id, fullinfo = False, fullSync = False):
def getTvShows(self, id, itemList = []):
result = None
doUtils = DownloadUtils()
if not fullSync:
sortstring = "&Limit=20&SortBy=DateCreated"
else:
sortstring = "&SortBy=SortName"
if fullinfo:
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s%s&Fields=Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&Recursive=true&SortOrder=Descending&IncludeItemTypes=Series&format=json&ImageTypeLimit=1" % (id, sortstring)
else:
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s%s&Fields=CumulativeRunTimeTicks&Recursive=true&SortOrder=Descending&IncludeItemTypes=Series&format=json&ImageTypeLimit=1" % (id, sortstring)
#only get basic info for our sync-compares
sortstring = "&SortBy=SortName"
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s%s&Fields=CumulativeRunTimeTicks,Etag&Recursive=true&SortOrder=Descending&IncludeItemTypes=Series&format=json&ImageTypeLimit=1" % (id, sortstring)
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):
@ -119,6 +107,14 @@ class ReadEmbyDB():
if (jsonData[u'Items'] != ""):
result = jsonData[u'Items']
# Work around to only return items from the given list
if (result != None and len(result) > 0 and len(itemList) > 0):
newResult = []
for item in result:
if (item[u'Id'] in itemList):
newResult.append(item)
result = newResult
return result
@ -138,15 +134,12 @@ class ReadEmbyDB():
return result
def getEpisodes(self, showId, fullinfo = False):
def getEpisodes(self, showId, itemList = []):
result = None
doUtils = DownloadUtils()
if fullinfo:
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s&IsVirtualUnaired=false&IsMissing=False&SortBy=SortName&Fields=Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&Recursive=true&SortOrder=Ascending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % showId
else:
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s&IsVirtualUnaired=false&IsMissing=False&SortBy=SortName&Fields=Name,SortName,CumulativeRunTimeTicks&Recursive=true&SortOrder=Ascending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % showId
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s&IsVirtualUnaired=false&IsMissing=False&SortBy=SortName&Fields=Name,SortName,CumulativeRunTimeTicks,Etag&Recursive=true&SortOrder=Ascending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % showId
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):
@ -154,6 +147,14 @@ class ReadEmbyDB():
if (jsonData[u'Items'] != ""):
result = jsonData[u'Items']
# Work around to only return items from the given list
if (result != None and len(result) > 0 and len(itemList) > 0):
newResult = []
for item in result:
if (item[u'Id'] in itemList):
newResult.append(item)
result = newResult
return result
@ -167,9 +168,9 @@ class ReadEmbyDB():
limitString = "Ids=" + ",".join(itemList) + "&"
if fullinfo:
url = "{server}/mediabrowser/Users/{UserId}/Items?%sIsVirtualUnaired=false&IsMissing=False&Fields=ParentId,Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&Recursive=true&SortOrder=Descending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % limitString
url = "{server}/mediabrowser/Users/{UserId}/Items?%sIsVirtualUnaired=false&IsMissing=False&Fields=ParentId,Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview,Etag&Recursive=true&SortOrder=Descending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % limitString
else:
url = "{server}/mediabrowser/Users/{UserId}/Items?%sIsVirtualUnaired=false&IsMissing=False&Fields=ParentId,Name,SortName,CumulativeRunTimeTicks&Recursive=true&SortOrder=Descending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % limitString
url = "{server}/mediabrowser/Users/{UserId}/Items?%sIsVirtualUnaired=false&IsMissing=False&Fields=ParentId,Name,SortName,CumulativeRunTimeTicks,Etag&Recursive=true&SortOrder=Descending&IncludeItemTypes=Episode&format=json&ImageTypeLimit=1" % limitString
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):
@ -225,33 +226,43 @@ class ReadEmbyDB():
doUtils = DownloadUtils()
viewsUrl = "{server}/mediabrowser/Users/{UserId}/Views?format=json&ImageTypeLimit=1"
jsonData = doUtils.downloadUrl(viewsUrl)
result = doUtils.downloadUrl(viewsUrl)
collections=[]
if (jsonData != ""):
views = views[u'Items']
if (result == ""):
return []
result = result[u'Items']
for view in views:
if (view[u'Type'] == 'UserView'): # Need to grab the real main node
newViewsUrl = "{server}/mediabrowser/Users/{UserId}/items?ParentId=%s&SortBy=SortName&SortOrder=Ascending&format=json&ImageTypeLimit=1" % view[u'Id']
jsonData = doUtils.downloadUrl(newViewsUrl)
if (jsonData != ""):
newViews = newViews[u'Items']
for newView in newViews:
# There are multiple nodes in here like 'Latest', 'NextUp' - below we grab the full node.
if newView[u'CollectionType'] == "MovieMovies" or newView[u'CollectionType'] == "TvShowSeries":
view=newView
if (view[u'ChildCount'] != 0):
Name = view[u'Name']
total = str(view[u'ChildCount'])
type = view[u'CollectionType']
if type == None:
type = "None" # User may not have declared the type
if type == type:
collections.append( {'title' : Name,
'type' : type,
'id' : view[u'Id']})
for view in result:
if (view[u'Type'] == 'UserView'): # Need to grab the real main node
newViewsUrl = "{server}/mediabrowser/Users/{UserId}/items?ParentId=%s&SortBy=SortName&SortOrder=Ascending&format=json&ImageTypeLimit=1" % view[u'Id']
newViews = doUtils.downloadUrl(newViewsUrl)
if (result == ""):
return []
newViews = newViews[u'Items']
print str(newViews)
for newView in newViews:
# There are multiple nodes in here like 'Latest', 'NextUp' - below we grab the full node.
if newView[u'CollectionType'] != None:
if newView[u'CollectionType'] == "MovieMovies" or newView[u'CollectionType'] == "TvShowSeries":
view=newView
if (view[u'ChildCount'] != 0):
Name = view[u'Name']
total = str(view[u'ChildCount'])
try:
itemtype = view[u'CollectionType']
except:
itemtype = "movies"
if itemtype == "MovieMovies":
itemtype = "movies"
if itemtype == "TvShowSeries":
itemtype = "tvshows"
if itemtype == type:
collections.append( {'title' : Name,
'type' : type,
'id' : view[u'Id']})
return collections
def getBoxSets(self):
@ -259,7 +270,7 @@ class ReadEmbyDB():
result = None
doUtils = DownloadUtils()
url = "{server}/mediabrowser/Users/{UserId}/Items?SortBy=SortName&IsVirtualUnaired=false&IsMissing=False&Fields=Name,SortName,CumulativeRunTimeTicks&Recursive=true&SortOrder=Ascending&IncludeItemTypes=BoxSet&format=json&ImageTypeLimit=1"
url = "{server}/mediabrowser/Users/{UserId}/Items?SortBy=SortName&IsVirtualUnaired=false&IsMissing=False&Fields=Name,SortName,CumulativeRunTimeTicks,Etag&Recursive=true&SortOrder=Ascending&IncludeItemTypes=BoxSet&format=json&ImageTypeLimit=1"
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):
@ -275,7 +286,7 @@ class ReadEmbyDB():
result = None
doUtils = DownloadUtils()
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s&Fields=ItemCounts&format=json&ImageTypeLimit=1" % boxsetId
url = "{server}/mediabrowser/Users/{UserId}/Items?ParentId=%s&Fields=ItemCounts,Etag&format=json&ImageTypeLimit=1" % boxsetId
jsonData = doUtils.downloadUrl(url)
if (jsonData == ""):

View file

@ -11,302 +11,49 @@ import os
import Utils as utils
#sleepval is used to throttle the calls to the xbmc json API
sleepVal = 15
class ReadKodiDB():
def getKodiMovie(self, id):
#returns a single movie from Kodi db selected on MB item ID
xbmc.sleep(sleepVal)
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "properties" : ["art", "rating", "thumbnail", "fanart", "resume", "runtime", "year", "genre", "cast", "trailer", "country", "studio", "set", "imdbnumber", "mpaa", "tagline", "plotoutline","plot", "sorttitle", "director", "lastplayed", "writer", "playcount", "tag", "file"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libMovies"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
movie = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('movies')):
movies = result['movies']
movie = movies[0]
for item in movies:
if item["imdbnumber"] == id:
movie = item
break
return movie
def getEmbyIdByKodiId(self, kodiid, type):
#returns the emby id by search on kodi id
xbmc.sleep(sleepVal)
embyId = None
json_response = None
if type == "movie":
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovieDetails", "params": { "movieid": %d, "properties" : ["imdbnumber","file"] }, "id": "libMovies"}' %kodiid)
if type == "episode":
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodeDetails", "params": {"episodeid": %d, "properties": ["file","uniqueid"]}, "id": 1}' %kodiid)
if type == "musicvideo":
connection = utils.KodiSQL()
cursor = connection.cursor()
cursor.execute("SELECT c23 as MBid FROM musicvideo WHERE idMVideo = ?",(kodiid,))
result = cursor.fetchone()
cursor.close()
if result != None:
embyId = result[0]
if json_response != None:
jsonobject = json.loads(json_response.decode('utf-8','replace'))
if(jsonobject.has_key('result')):
result = jsonobject['result']
resulttype = type + "details"
if(result.has_key(resulttype)):
item = result[resulttype]
if type == "movie":
if item.has_key('imdbnumber'):
embyId = item['imdbnumber']
if type == "episode":
if item.has_key('uniqueid'):
if item['uniqueid'].has_key('unknown'):
embyId = item["uniqueid"]["unknown"]
return embyId
def getKodiMovies(self,fullInfo = False):
def getKodiMovies(self, connection, cursor):
#returns all movies in Kodi db
xbmc.sleep(sleepVal)
if fullInfo:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "properties" : ["art", "rating", "thumbnail", "fanart", "resume", "runtime", "year", "genre", "cast", "trailer", "country", "lastplayed", "studio", "set", "imdbnumber", "mpaa", "tagline", "plotoutline","plot", "sorttitle", "director", "writer", "playcount", "tag", "file"] }, "id": "libMovies"}')
else:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "properties" : ["resume", "playcount", "imdbnumber", "lastplayed", "file"] }, "id": "libMovies"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
movies = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('movies')):
movies = result['movies']
kodiMovieMap = None
if(movies != None and len(movies) > 0):
kodiMovieMap = {}
for kodimovie in movies:
key = kodimovie["imdbnumber"] #extract the id from the imdbnumber
kodiMovieMap[key] = kodimovie
return kodiMovieMap
cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type='movie'")
allmovies = cursor.fetchall()
#this will return a list with tuples of all items returned from the database
return allmovies
def getKodiMoviesIds(self,returnMB3Ids = False):
# returns a list of movieIds or MB3 Id's from all movies currently in the Kodi library
allKodiMovies = self.getKodiMovies(False)
if(allKodiMovies == None):
return list()
if(returnMB3Ids):
allKodiMovieIds = list(allKodiMovies.keys())
return allKodiMovieIds
else:
allKodiMovieIds = list()
for kodimovie in allKodiMovies.values():
id = str(kodimovie["movieid"])
allKodiMovieIds.append(id)
return allKodiMovieIds
def getKodiMusicVideos(self, connection, cursor):
#returns all musicvideos in Kodi db
cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type='musicvideo'")
allvideos = cursor.fetchall()
#this will return a list with tuples of all items returned from the database
return allvideos
def getKodiTvShowsIds(self,returnMB3Ids = False):
# returns a list of tvshowIds or MB3 Id's from all tvshows currently in the Kodi library
allKodiTvShows = self.getKodiTvShows(False)
if allKodiTvShows == None:
return list()
if(returnMB3Ids):
allKodiTvShowsIds = list(allKodiTvShows.keys())
return allKodiTvShowsIds
else:
allKodiTvShowsIds = list()
for kodishow in allKodiTvShows.values():
id = str(kodishow["tvshowid"])
allKodiTvShowsIds.append(id)
return allKodiTvShowsIds
def getKodiTvShows(self,fullInfo = False):
#returns all tvshows in Kodi db inserted by MB
xbmc.sleep(sleepVal)
if fullInfo:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "properties": ["art", "genre", "plot", "mpaa", "cast", "studio", "sorttitle", "title", "originaltitle", "imdbnumber", "year", "premiered", "rating", "thumbnail", "playcount", "lastplayed", "file", "fanart", "tag"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libTvShows"}')
else:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "properties": ["sorttitle", "title", "playcount", "lastplayed", "imdbnumber", "file"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libTvShows"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
tvshows = None
def getKodiTvShows(self, connection, cursor):
cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type='tvshow'")
allshows = cursor.fetchall()
#this will return a list with tuples of all items returned from the database
return allshows
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('tvshows')):
tvshows = result['tvshows']
kodiShowMap = None
if(tvshows != None and len(tvshows) > 0):
kodiShowMap = {}
for kodishow in tvshows:
key = kodishow["imdbnumber"] #extract the id from the imdb number
kodiShowMap[key] = kodishow
return kodiShowMap
def getKodiTVShow(self, id):
xbmc.sleep(sleepVal)
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "properties": ["art", "genre", "plot", "mpaa", "cast", "studio", "sorttitle", "title", "originaltitle", "imdbnumber", "year", "lastplayed", "premiered", "rating", "thumbnail", "playcount", "file", "fanart", "tag"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libTvShows"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
tvshow = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('tvshows')):
tvshows = result['tvshows']
for show in tvshows:
if show["imdbnumber"] == id:
tvshow = show
break
return tvshow
def getKodiEpisodes(self, KodiTvShowId, fullInfo = True, returnmap = True):
xbmc.sleep(sleepVal)
episodes = None
if fullInfo:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": {"tvshowid": %d, "properties": ["title", "playcount", "plot", "season", "episode", "showtitle", "file", "lastplayed", "rating", "resume", "art", "streamdetails", "firstaired", "runtime", "writer", "cast", "director", "dateadded", "uniqueid", "thumbnail", "fanart"], "sort": {"method": "episode"}}, "id": 1}' %KodiTvShowId)
def getKodiEpisodes(self, connection, cursor, showid=None):
if showid == None:
cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type=?",("episode",))
else:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": {"tvshowid": %d, "properties": ["title", "playcount", "season", "episode", "lastplayed", "resume","file","uniqueid"], "sort": {"method": "episode"}}, "id": 1}' %KodiTvShowId)
jsonobject = json.loads(json_response.decode('utf-8','replace'))
episodes = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('episodes')):
episodes = result['episodes']
if returnmap:
episodeMap = None
if(episodes != None):
episodeMap = {}
for KodiItem in episodes:
episodeMap[KodiItem["uniqueid"]["unknown"]] = KodiItem
return episodeMap
else:
return episodes
cursor.execute("SELECT kodi_id, emby_id, checksum FROM emby WHERE media_type=? AND parent_id=?",("episode", showid))
def getKodiEpisodeByMbItem(self, episodeid, tvshowid):
episode = None
tvshow = self.getKodiTVShow(tvshowid)
allepisodes = cursor.fetchall()
#this will return a list with tuples of all items returned from the database
return allepisodes
if tvshow != None:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": {"tvshowid": ' + str(tvshow['tvshowid']) + ', "properties": ["playcount","season", "resume", "episode", "lastplayed", "uniqueid", "file"], "sort": {"method": "episode"}}, "id": 1}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('episodes')):
episodes = result['episodes']
for ep in episodes:
if ep["uniqueid"]["unknown"] == episodeid:
episode = ep
break
return episode
def getKodiEpisodeByMbItemEx(self, id):
connection = utils.KodiSQL()
cursor = connection.cursor()
cursor.execute("SELECT idEpisode FROM episode WHERE c20 = ?", (id,))
result = cursor.fetchone()
kodiId = None
if result != None:
kodiId = result[0]
cursor.close()
episode = None
if(kodiId != None):
print "Kodi Episode ID : " + str(kodiId)
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodeDetails", "params": {"episodeid": %d, "properties": ["playcount", "season", "resume", "episode", "lastplayed", "uniqueid", "file"]}, "id": 1}' %kodiId)
#json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodeDetails", "params": {"episodeid": ' + str(kodiId) + ', "properties": ["playcount", "season", "resume", "episode", "lastplayed", "uniqueid", "file"], "sort": {"method": "episode"}}, "id": 1}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
print "Kodi_Item: " + str(jsonobject)
if(jsonobject.has_key("result")):
result = jsonobject["result"]
if(result.has_key("episodedetails")):
episode = result["episodedetails"]
return episode
def getKodiMusicVideo(self, id):
#returns a single musicvideo from Kodi db selected on MB item ID
xbmc.sleep(sleepVal)
#get the mediabrowser ID from DB
connection = utils.KodiSQL()
cursor = connection.cursor()
cursor.execute("SELECT idMVideo as musicvideoid FROM musicvideo WHERE c23 = ?",(id,))
result = cursor.fetchone()
musicvideoid = None
if result != None:
musicvideoid = result[0]
cursor.close()
musicvideo = None
if musicvideoid != None:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMusicVideosDetails", "params": { "musicvideoid": ' + musicvideoid + ', "properties" : ["art", "thumbnail", "fanart", "resume", "runtime", "year", "genre", "studio", "artist", "album", "track","plot", "director", "playcount", "lastplayed", "tag", "file"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libMusicVideos"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
musicvideo = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('musicvideodetails')):
musicvideo = result['musicvideodetails']
return musicvideo
def getKodiMusicVideos(self,fullInfo = False):
#returns all musicvideos in Kodi db inserted by MB
xbmc.sleep(sleepVal)
if fullInfo:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMusicVideos", "params": { "properties" : ["art", "thumbnail", "fanart", "resume", "runtime", "year", "genre", "studio", "artist", "album", "track", "lastplayed", "plot", "director", "playcount", "tag", "file"] }, "id": "libMusicVideos"}')
else:
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMusicVideos", "params": { "properties" : ["resume", "playcount", "lastplayed", "file", "track"] }, "id": "libMusicVideos"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
musicvideos = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('musicvideos')):
musicvideos = result['musicvideos']
kodiMusicVideoMap = None
if(musicvideos != None and len(musicvideos) > 0):
kodiMusicVideoMap = {}
def getEmbyIdByKodiId(self, id, type, connection=None, cursor=None):
if not connection:
connection = utils.KodiSQL()
cursor = connection.cursor()
for kodivideo in musicvideos:
cursor.execute("SELECT c23 as MBid FROM musicvideo WHERE idMVideo = ?",(kodivideo["musicvideoid"],))
result = cursor.fetchone()
if result != None:
key = result[0]
kodiMusicVideoMap[key] = kodivideo
cursor.close()
return kodiMusicVideoMap
def getKodiMusicVideoIds(self,returnMB3Ids = False):
# returns a list of movieIds or MB3 Id's from all movies currently in the Kodi library
allKodiMusicVideos = self.getKodiMusicVideos(False)
if(allKodiMusicVideos == None):
return list()
if(returnMB3Ids):
allKodiMusicVideoIds = list(allKodiMusicVideos.keys())
return allKodiMusicVideoIds
cursor.execute("SELECT emby_id FROM emby WHERE media_type=? AND kodi_id=?",(type,id))
result = cursor.fetchone()
if result:
return result[0]
else:
allKodiMusicVideoIds = list()
for kodivideo in allKodiMusicVideos.values():
id = str(kodivideo["musicvideoid"])
allKodiMusicVideoIds.append(id)
return allKodiMusicVideoIds
return None

View file

@ -45,17 +45,17 @@ class UserClient(threading.Thread):
def __init__(self, *args):
self.__dict__ = self._shared_state
self.className = self.__class__.__name__
threading.Thread.__init__(self, *args)
def logMsg(self, msg, lvl=1):
utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl))
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl))
def getUsername(self):
username = self.addon.getSetting('username')
addon = xbmcaddon.Addon(id=self.addonId)
username = addon.getSetting('username')
if (username == ""):
self.logMsg("No username saved.", 2)
@ -90,7 +90,7 @@ class UserClient(threading.Thread):
def getServer(self, prefix=True):
# For https support
addon = self.addon
addon = xbmcaddon.Addon(id=self.addonId)
HTTPS = addon.getSetting('https')
host = addon.getSetting('ipaddress')
port = addon.getSetting('port')
@ -161,6 +161,9 @@ class UserClient(threading.Thread):
if (result != ""):
users = result
else:
# Server connection failed
return False
return users
@ -226,9 +229,6 @@ class UserClient(threading.Thread):
users = self.getPublicUsers()
password = ""
'''if users == "":
self.WINDOW.setProperty("Server_status", "Stop")
return'''
# Find user in list
for user in users:
name = user[u'Name']

View file

@ -53,7 +53,7 @@ def convertEncoding(data):
def KodiSQL():
connection = sqlite3.connect(getKodiDBPath())
return connection
def getKodiDBPath():
@ -62,7 +62,7 @@ def getKodiDBPath():
dbVersion = "78"
if xbmc.getInfoLabel("System.BuildVersion").startswith("15"):
#isengard
dbVersion = "91"
dbVersion = "92"
else:
#helix
dbVersion = "90"

View file

@ -20,6 +20,7 @@ from DownloadUtils import DownloadUtils
from PlaybackUtils import PlaybackUtils
from LibrarySync import LibrarySync
from WriteKodiDB import WriteKodiDB
from ReadEmbyDB import ReadEmbyDB
pendingUserDataList = []
pendingItemsToRemove = []
@ -179,39 +180,40 @@ class WebSocketThread(threading.Thread):
self.update_items(itemsToUpdate)
def remove_items(self, itemsRemoved):
connection = utils.KodiSQL()
cursor = connection.cursor()
for item in itemsRemoved:
self.logMsg("Message : Doing LibraryChanged : Items Removed : Calling deleteEpisodeFromKodiLibraryByMbId: " + item, 0)
WriteKodiDB().deleteEpisodeFromKodiLibraryByMbId(item)
self.logMsg("Message : Doing LibraryChanged : Items Removed : Calling deleteMovieFromKodiLibrary: " + item, 0)
WriteKodiDB().deleteMovieFromKodiLibrary(item)
self.logMsg("Message : Doing LibraryChanged : Items Removed : Calling deleteMusicVideoFromKodiLibrary: " + item, 0)
WriteKodiDB().deleteMusicVideoFromKodiLibrary(item)
WriteKodiDB().deleteItemFromKodiLibrary(item, connection, cursor)
connection.commit()
cursor.close()
def update_items(self, itemsToUpdate):
# doing adds and updates
if(len(itemsToUpdate) > 0):
self.logMsg("Message : Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0)
connection = utils.KodiSQL()
cursor = connection.cursor()
LibrarySync().MoviesSync(connection, cursor, fullsync = False, installFirstRun = False, itemList = itemsToUpdate)
LibrarySync().TvShowsSync(connection, cursor, fullsync = False, installFirstRun = False, itemList = itemsToUpdate)
cursor.close()
LibrarySync().IncrementalSync(itemsToUpdate)
def user_data_update(self, userDataList):
itemsToUpdate = list()
for userData in userDataList:
self.logMsg("Message : Doing UserDataChanged : UserData : " + str(userData), 0)
itemId = userData.get("ItemId")
if(itemId != None):
self.logMsg("Message : Doing UserDataChanged : calling updatePlayCount with ID : " + str(itemId), 0)
LibrarySync().updatePlayCount(itemId)
itemsToUpdate.append(itemId)
if(len(itemsToUpdate) > 0):
self.logMsg("Message : Doing UserDataChanged : Processing Updated : " + str(itemsToUpdate), 0)
LibrarySync().IncrementalSync(itemsToUpdate)
def on_error(self, ws, error):
self.logMsg("Error : " + str(error))
if "10061" in str(error):
# Server is offline
pass
else:
self.logMsg("Error: %s" % error, 1)
#raise
def on_close(self, ws):
self.logMsg("Closed")
self.logMsg("Closed", 2)
def on_open(self, ws):
pass
@ -245,12 +247,19 @@ class WebSocketThread(threading.Thread):
self.client.on_open = self.on_open
while not self.KodiMonitor.abortRequested():
self.logMsg("Client Starting")
self.client.run_forever()
if(self.keepRunning):
self.logMsg("Client Needs To Restart")
if (self.keepRunning):
# Server is not online
if WINDOW.getProperty("Server_online") == "true":
self.logMsg("Server is unreachable.", 1)
WINDOW.setProperty("Server_online", "false")
xbmcgui.Dialog().notification("Error connecting", "Server is unreachable.")
if self.KodiMonitor.waitForAbort(5):
break
self.logMsg("Thread Exited")

File diff suppressed because it is too large Load diff

View file

@ -12,12 +12,9 @@
<setting id="deviceName" type="text" label="30016" default="Kodi" />
<setting id="playFromStream" type="bool" label="30002" visible="true" enable="true" default="false" />
</category>
<!-- <category label="Manual sync"> <setting label="Run manual full sync now" type="action" action="RunScript(plugin.video.mbsync, fullsync)" /> <setting label="Run manual incremental sync now" type="action" action="RunScript(plugin.video.mbsync, incrementalsync)" /> <setting label="Reset entire local library" type="action" action="RunScript(plugin.video.mbsync, reset)" /> </category> -->
<category label="Sync Options">
<!-- <setting id="syncMovieBoxSets" type="bool" label="30238" default="true" visible="true" enable="true" /> -->
<setting id="enablePlayCountSync" type="bool" label="30240" default="true" visible="true" enable="true" />
<setting id="dbSyncIndication" type="labelenum" label="30241" values="None|Notify OnChange|Notify OnFinish|BG Progress|Dialog Progress" default="None" />
<setting id="playCountSyncIndication" type="labelenum" label="30242" values="None|Notify OnChange|Notify OnFinish|BG Progress|Dialog Progress" default="None" />
<setting id="dbSyncIndication" type="bool" label="30241" default="false" visible="true" enable="true" />
</category>
<category label="Playback"> <!-- Extra Sync options -->
<setting id="smbusername" type="text" label="30007" default="" visible="true" enable="true" />
@ -27,6 +24,9 @@
<setting id="videoBitRate" type="enum" label="30160" values="664 Kbps SD|996 Kbps HD|1.3 Mbps HD|2.0 Mbps HD|3.2 Mbps HD|4.7 Mbps HD|6.2 Mbps HD|7.7 Mbps HD|9.2 Mbps HD|10.7 Mbps HD|12.2 Mbps HD|13.7 Mbps HD|15.2 Mbps HD|16.7 Mbps HD|18.2 Mbps HD|20.0 Mbps HD|40.0 Mbps HD|100.0 Mbps HD [default]|1000.0 Mbps HD" default="17" />
<setting id="forceTranscodingCodecs" type="text" label="30245" />
</category>
<category label="Artwork">
<setting id="disableCoverArt" type="bool" label="30157" default="false" visible="true" enable="true" />
</category>
<category label="30022">
<setting id="logLevel" type="enum" label="30004" values="None|Info|Debug" default="1" />
<setting label="30239" type="action" action="RunScript(plugin.video.emby, reset)" />

View file

@ -19,6 +19,7 @@ from ConnectionManager import ConnectionManager
from ClientInformation import ClientInformation
from WebSocketClient import WebSocketThread
from UserClient import UserClient
from PlaybackUtils import PlaybackUtils
librarySync = LibrarySync()
@ -30,7 +31,10 @@ class Service():
clientInfo = ClientInformation()
addonName = clientInfo.getAddonName()
className = None
WINDOW = xbmcgui.Window(10000)
warn_auth = True
server_online = True
def __init__(self, *args ):
self.KodiMonitor = KodiMonitor.Kodi_Monitor()
@ -40,24 +44,21 @@ class Service():
self.logMsg("======== START %s ========" % addonName, 0)
self.logMsg("KODI Version: %s" % xbmc.getInfoLabel("System.BuildVersion"), 0)
self.logMsg("%s Version: %s" % (addonName, self.clientInfo.getVersion()), 0)
self.logMsg("Platform: %s" % (self.clientInfo.getPlatform()), 0)
def logMsg(self, msg, lvl=1):
self.className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl))
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl))
def ServiceEntryPoint(self):
WINDOW = self.WINDOW
WINDOW.setProperty("Server_online", "")
ConnectionManager().checkServer()
lastProgressUpdate = datetime.today()
startupComplete = False
#interval_FullSync = 600
#interval_IncrementalSync = 300
#cur_seconds_fullsync = interval_FullSync
#cur_seconds_incrsync = interval_IncrementalSync
user = UserClient()
player = Player()
@ -70,71 +71,101 @@ class Service():
if self.KodiMonitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
break
if xbmc.Player().isPlaying():
try:
playTime = xbmc.Player().getTime()
totalTime = xbmc.Player().getTotalTime()
currentFile = xbmc.Player().getPlayingFile()
if(player.played_information.get(currentFile) != None):
player.played_information[currentFile]["currentPosition"] = playTime
# send update
td = datetime.today() - lastProgressUpdate
secDiff = td.seconds
if(secDiff > 3):
try:
player.reportPlayback()
except Exception, msg:
self.logMsg("Exception reporting progress: %s" % msg)
pass
lastProgressUpdate = datetime.today()
# only try autoplay when there's 20 seconds or less remaining and only once!
if (totalTime - playTime <= 20 and (lastFile==None or lastFile!=currentFile)):
lastFile = currentFile
player.autoPlayPlayback()
except Exception, e:
self.logMsg("Exception in Playback Monitor Service: %s" % e)
pass
else:
if (self.newUserClient == None):
self.newUserClient = "Started"
user.start()
# background worker for database sync
if (user.currUser != None):
# Correctly launch the websocket, if user manually launches the add-on
if (self.newWebSocketThread == None):
self.newWebSocketThread = "Started"
ws.start()
#full sync
if(startupComplete == False):
self.logMsg("Doing_Db_Sync: syncDatabase (Started)")
libSync = librarySync.syncDatabase()
self.logMsg("Doing_Db_Sync: syncDatabase (Finished) " + str(libSync))
countSync = librarySync.updatePlayCounts()
self.logMsg("Doing_Db_Sync: updatePlayCounts (Finished) " + str(countSync))
if WINDOW.getProperty('Server_online') == "true":
# Server is online
if xbmc.Player().isPlaying():
try:
playTime = xbmc.Player().getTime()
totalTime = xbmc.Player().getTotalTime()
currentFile = xbmc.Player().getPlayingFile()
# Force refresh newly set thumbnails
xbmc.executebuiltin("UpdateLibrary(video)")
if(libSync and countSync):
startupComplete = True
else:
if self.KodiMonitor.waitForAbort(10):
# Abort was requested while waiting. We should exit
break
WebSocketThread().processPendingActions()
if(player.played_information.get(currentFile) != None):
player.played_information[currentFile]["currentPosition"] = playTime
# send update
td = datetime.today() - lastProgressUpdate
secDiff = td.seconds
if(secDiff > 3):
try:
player.reportPlayback()
except Exception, msg:
self.logMsg("Exception reporting progress: %s" % msg)
pass
lastProgressUpdate = datetime.today()
# only try autoplay when there's 20 seconds or less remaining and only once!
if (totalTime - playTime <= 20 and (lastFile==None or lastFile!=currentFile)):
lastFile = currentFile
player.autoPlayPlayback()
except Exception, e:
self.logMsg("Exception in Playback Monitor Service: %s" % e)
pass
else:
self.logMsg("Not authenticated yet", 0)
# background worker for database sync
if (user.currUser != None):
self.warn_auth = True
# Correctly launch the websocket, if user manually launches the add-on
if (self.newWebSocketThread == None):
self.newWebSocketThread = "Started"
ws.start()
#full sync
if (startupComplete == False):
self.logMsg("Doing_Db_Sync: syncDatabase (Started)")
libSync = librarySync.FullLibrarySync()
self.logMsg("Doing_Db_Sync: syncDatabase (Finished) " + str(libSync))
if (libSync):
startupComplete = True
else:
if self.KodiMonitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
break
WebSocketThread().processPendingActions()
else:
if self.warn_auth:
self.logMsg("Not authenticated yet.", 1)
self.warn_auth = False
else:
# Wait until server becomes online or shut down is requested
while not self.KodiMonitor.abortRequested():
self.logMsg("stopping Service", 0)
if user.getServer() == "":
pass
elif not user.getPublicUsers():
# Server is not online, suppress future warning
if self.server_online:
WINDOW.setProperty("Server_online", "false")
self.logMsg("Server is offline.", 1)
xbmcgui.Dialog().notification("Error connecting", "%s Server is unreachable." % self.addonName)
self.server_online = False
else:
# Server is online
if not self.server_online:
# Server was not online when Kodi started.
# Wait for server to be fully established.
if self.KodiMonitor.waitForAbort(5):
# Abort was requested while waiting.
break
self.server_online = True
self.logMsg("Server is online and ready.", 1)
xbmcgui.Dialog().notification("Connection successful", "%s Server is online." % self.addonName, time=2000)
WINDOW.setProperty("Server_online", "true")
# Server is online, proceed.
if (self.newUserClient == None):
self.newUserClient = "Started"
user.start()
break
if self.KodiMonitor.waitForAbort(1):
# Abort was requested while waiting.
break
# If user reset library database.
WINDOW = xbmcgui.Window(10000)
if WINDOW.getProperty("SyncInstallRunDone") == "false":
addon = xbmcaddon.Addon('plugin.video.emby')
addon.setSetting("SyncInstallRunDone", "false")
@ -143,8 +174,9 @@ class Service():
ws.stopClient()
if (self.newUserClient != None):
user.stopClient()
user.stopClient()
self.logMsg("======== STOP %s ========" % self.addonName, 0)
#start the service
Service().ServiceEntryPoint()