diff --git a/resources/lib/API.py b/resources/lib/API.py
deleted file mode 100644
index d15822bb..00000000
--- a/resources/lib/API.py
+++ /dev/null
@@ -1,378 +0,0 @@
-# -*- coding: utf-8 -*-
-
-##################################################################################################
-
-import clientinfo
-import utils
-
-##################################################################################################
-
-
-class API():
-
- def __init__(self, item):
-
- self.item = item
- self.clientinfo = clientinfo.ClientInfo()
- self.addonName = self.clientinfo.getAddonName()
-
- def logMsg(self, msg, lvl=1):
-
- className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
-
-
- def getUserData(self):
- # Default
- favorite = False
- playcount = None
- played = False
- lastPlayedDate = None
- resume = 0
- rating = 0
-
- try:
- userdata = self.item['UserData']
-
- except KeyError: # No userdata found.
- pass
-
- else:
- favorite = userdata['IsFavorite']
- likes = userdata.get('Likes')
- # Rating for album and songs
- if favorite:
- rating = 5
- elif likes:
- rating = 3
- elif likes == False:
- rating = 1
- else:
- rating = 0
-
- lastPlayedDate = userdata.get('LastPlayedDate')
- if lastPlayedDate:
- lastPlayedDate = lastPlayedDate.split('.')[0].replace('T', " ")
-
- if userdata['Played']:
- # Playcount is tied to the watch status
- played = True
- playcount = userdata['PlayCount']
- if playcount == 0:
- playcount = 1
-
- if lastPlayedDate is None:
- lastPlayedDate = self.getDateCreated()
-
- playbackPosition = userdata.get('PlaybackPositionTicks')
- if playbackPosition:
- resume = playbackPosition / 10000000.0
-
- return {
-
- 'Favorite': favorite,
- 'PlayCount': playcount,
- 'Played': played,
- 'LastPlayedDate': lastPlayedDate,
- 'Resume': resume,
- 'Rating': rating
- }
-
- def getPeople(self):
- # Process People
- director = []
- writer = []
- cast = []
-
- try:
- people = self.item['People']
-
- except KeyError:
- pass
-
- else:
- for person in people:
-
- type = person['Type']
- name = person['Name']
-
- if "Director" in type:
- director.append(name)
- elif "Actor" in type:
- cast.append(name)
- elif type in ("Writing", "Writer"):
- writer.append(name)
-
- return {
-
- 'Director': director,
- 'Writer': writer,
- 'Cast': cast
- }
-
- def getMediaStreams(self):
- item = self.item
- videotracks = []
- audiotracks = []
- subtitlelanguages = []
-
- try:
- media_streams = item['MediaSources'][0]['MediaStreams']
-
- except KeyError:
- media_streams = item['MediaStreams']
-
- for media_stream in media_streams:
- # Sort through Video, Audio, Subtitle
- stream_type = media_stream['Type']
- codec = media_stream.get('Codec', "").lower()
- profile = media_stream.get('Profile', "").lower()
-
- if stream_type == "Video":
- # Height, Width, Codec, AspectRatio, AspectFloat, 3D
- track = {
-
- 'videocodec': codec,
- 'height': media_stream.get('Height'),
- 'width': media_stream.get('Width'),
- 'video3DFormat': item.get('Video3DFormat'),
- 'aspectratio': 1.85
- }
-
- try:
- container = item['MediaSources'][0]['Container'].lower()
- except:
- container = ""
-
- # Sort codec vs container/profile
- if "msmpeg4" in codec:
- track['videocodec'] = "divx"
- elif "mpeg4" in codec:
- if "simple profile" in profile or not profile:
- track['videocodec'] = "xvid"
- elif "h264" in codec:
- if container in ("mp4", "mov", "m4v"):
- track['videocodec'] = "avc1"
-
- # Aspect ratio
- if item.get('AspectRatio'):
- # Metadata AR
- aspectratio = item['AspectRatio']
- else: # File AR
- aspectratio = media_stream.get('AspectRatio', "0")
-
- try:
- aspectwidth, aspectheight = aspectratio.split(':')
- track['aspectratio'] = round(float(aspectwidth) / float(aspectheight), 6)
-
- except ValueError:
- width = track['width']
- height = track['height']
-
- if width and height:
- track['aspectratio'] = round(float(width / height), 6)
-
- videotracks.append(track)
-
- elif stream_type == "Audio":
- # Codec, Channels, language
- track = {
-
- 'audiocodec': codec,
- 'channels': media_stream.get('Channels'),
- 'audiolanguage': media_stream.get('Language')
- }
-
- if "dca" in codec and "dts-hd ma" in profile:
- track['audiocodec'] = "dtshd_ma"
-
- audiotracks.append(track)
-
- elif stream_type == "Subtitle":
- # Language
- subtitlelanguages.append(media_stream.get('Language', "Unknown"))
-
- return {
-
- 'video': videotracks,
- 'audio': audiotracks,
- 'subtitle': subtitlelanguages
- }
-
- def getRuntime(self):
- item = self.item
- try:
- runtime = item['RunTimeTicks'] / 10000000.0
-
- except KeyError:
- runtime = item.get('CumulativeRunTimeTicks', 0) / 10000000.0
-
- return runtime
-
- def adjustResume(self, resume_seconds):
-
- resume = 0
- if resume_seconds:
- resume = round(float(resume_seconds), 6)
- jumpback = int(utils.settings('resumeJumpBack'))
- if resume > jumpback:
- # To avoid negative bookmark
- resume = resume - jumpback
-
- return resume
-
- def getStudios(self):
- # Process Studios
- item = self.item
- studios = []
-
- try:
- studio = item['SeriesStudio']
- studios.append(self.verifyStudio(studio))
-
- except KeyError:
- studioList = item['Studios']
- for studio in studioList:
-
- name = studio['Name']
- studios.append(self.verifyStudio(name))
-
- return studios
-
- def verifyStudio(self, studioName):
- # Convert studio for Kodi to properly detect them
- studios = {
-
- 'abc (us)': "ABC",
- 'fox (us)': "FOX",
- 'mtv (us)': "MTV",
- 'showcase (ca)': "Showcase",
- 'wgn america': "WGN"
- }
-
- return studios.get(studioName.lower(), studioName)
-
- def getChecksum(self):
- # Use the etags checksum and userdata
- item = self.item
- userdata = item['UserData']
-
- checksum = "%s%s%s%s%s%s" % (
-
- item['Etag'],
- userdata['Played'],
- userdata['IsFavorite'],
- userdata['PlaybackPositionTicks'],
- userdata.get('UnplayedItemCount', ""),
- userdata.get('LastPlayedDate', "")
- )
-
- return checksum
-
- def getGenres(self):
- item = self.item
- all_genres = ""
- genres = item.get('Genres', item.get('SeriesGenres'))
-
- if genres:
- all_genres = " / ".join(genres)
-
- return all_genres
-
- def getDateCreated(self):
-
- try:
- dateadded = self.item['DateCreated']
- dateadded = dateadded.split('.')[0].replace('T', " ")
- except KeyError:
- dateadded = None
-
- return dateadded
-
- def getPremiereDate(self):
-
- try:
- premiere = self.item['PremiereDate']
- premiere = premiere.split('.')[0].replace('T', " ")
- except KeyError:
- premiere = None
-
- return premiere
-
- def getOverview(self):
-
- try:
- overview = self.item['Overview']
- overview = overview.replace("\"", "\'")
- overview = overview.replace("\n", " ")
- overview = overview.replace("\r", " ")
- except KeyError:
- overview = ""
-
- return overview
-
- def getTagline(self):
-
- try:
- tagline = self.item['Taglines'][0]
- except IndexError:
- tagline = None
-
- return tagline
-
- def getProvider(self, providername):
-
- try:
- provider = self.item['ProviderIds'][providername]
- except KeyError:
- provider = None
-
- return provider
-
- def getMpaa(self):
- # Convert more complex cases
- mpaa = self.item.get('OfficialRating', "")
-
- if mpaa in ("NR", "UR"):
- # Kodi seems to not like NR, but will accept Not Rated
- mpaa = "Not Rated"
-
- return mpaa
-
- def getCountry(self):
-
- try:
- country = self.item['ProductionLocations'][0]
- except IndexError:
- country = None
-
- return country
-
- def getFilePath(self):
-
- item = self.item
- try:
- filepath = item['Path']
-
- except KeyError:
- filepath = ""
-
- else:
- if "\\\\" in filepath:
- # append smb protocol
- filepath = filepath.replace("\\\\", "smb://")
- filepath = filepath.replace("\\", "/")
-
- if item.get('VideoType'):
- videotype = item['VideoType']
- # Specific format modification
- if 'Dvd'in videotype:
- filepath = "%s/VIDEO_TS/VIDEO_TS.IFO" % filepath
- elif 'Bluray' in videotype:
- filepath = "%s/BDMV/index.bdmv" % filepath
-
- if "\\" in filepath:
- # Local path scenario, with special videotype
- filepath = filepath.replace("/", "\\")
-
- return filepath
\ No newline at end of file
diff --git a/resources/lib/DownloadUtils.py b/resources/lib/DownloadUtils.py
deleted file mode 100644
index 04d2da85..00000000
--- a/resources/lib/DownloadUtils.py
+++ /dev/null
@@ -1,347 +0,0 @@
-import xbmc
-import xbmcgui
-import xbmcaddon
-
-import requests
-import json
-import logging
-
-import Utils as utils
-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)
-
-class DownloadUtils():
-
- # Borg - multiple instances, shared state
- _shared_state = {}
- clientInfo = ClientInformation()
-
- addonName = clientInfo.getAddonName()
- addon = xbmcaddon.Addon()
- WINDOW = xbmcgui.Window(10000)
-
- # Requests session
- s = None
- timeout = 60
-
- def __init__(self):
-
- self.__dict__ = self._shared_state
-
- def logMsg(self, msg, lvl=1):
-
- self.className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
-
- def setUsername(self, username):
- # Reserved for UserClient only
- self.username = username
- self.logMsg("Set username: %s" % username, 2)
-
- def setUserId(self, userId):
- # Reserved for UserClient only
- self.userId = userId
- self.logMsg("Set userId: %s" % userId, 2)
-
- def setServer(self, server):
- # Reserved for UserClient only
- self.server = server
- self.logMsg("Set server: %s" % server, 2)
-
- def setToken(self, token):
- # Reserved for UserClient only
- self.token = token
- self.logMsg("Set token: %s" % token, 2)
-
- def setSSL(self, ssl, sslclient):
- # Reserved for UserClient only
- self.sslverify = ssl
- self.sslclient = sslclient
- self.logMsg("Verify SSL host certificate: %s" % ssl, 2)
- self.logMsg("SSL client side certificate: %s" % sslclient, 2)
-
- def postCapabilities(self, deviceId):
-
- # Post settings to session
- url = "{server}/mediabrowser/Sessions/Capabilities/Full"
- data = {
- 'PlayableMediaTypes': "Audio,Video",
- 'SupportsMediaControl': True,
- 'SupportedCommands': (
-
- "MoveUp,MoveDown,MoveLeft,MoveRight,Select,"
- "Back,ToggleContextMenu,ToggleFullscreen,ToggleOsdMenu,"
- "GoHome,PageUp,NextLetter,GoToSearch,"
- "GoToSettings,PageDown,PreviousLetter,TakeScreenshot,"
- "VolumeUp,VolumeDown,ToggleMute,SendString,DisplayMessage,"
- "SetAudioStreamIndex,SetSubtitleStreamIndex,"
-
- "Mute,Unmute,SetVolume,"
- "Play,Playstate,PlayNext"
- )
- }
-
- self.logMsg("Capabilities URL: %s" % url, 2)
- self.logMsg("PostData: %s" % data, 2)
-
- try:
- self.downloadUrl(url, postBody=data, type="POST")
- self.logMsg("Posted capabilities to %s" % self.server, 1)
- except:
- self.logMsg("Posted capabilities failed.")
-
- # Attempt at getting sessionId
- url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
-
- try:
- result = self.downloadUrl(url)
- self.logMsg("Session: %s" % result, 2)
-
- sessionId = result[0][u'Id']
- self.logMsg("SessionId: %s" % sessionId)
- self.WINDOW.setProperty("sessionId%s" % self.username, sessionId)
- except:
- self.logMsg("Failed to retrieve sessionId.", 1)
- else:
- # Post any permanent additional users
- additionalUsers = utils.settings('additionalUsers').split(',')
- self.logMsg("List of permanent users that should be added to the session: %s" % str(additionalUsers), 1)
- # Get the user list from server to get the userId
- url = "{server}/mediabrowser/Users?format=json"
- result = self.downloadUrl(url)
-
- if result:
- for user in result:
- username = user['Name'].lower()
- userId = user['Id']
- for additional in additionalUsers:
- addUser = additional.decode('utf-8').lower()
- if username in addUser:
- url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, userId)
- postdata = {}
- self.downloadUrl(url, postBody=postdata, type="POST")
- #xbmcgui.Dialog().notification("Success!", "%s added to viewing session" % username, time=1000)
-
- def startSession(self):
-
- self.deviceId = self.clientInfo.getMachineId()
-
- # User is identified from this point
- # Attach authenticated header to the session
- verify = None
- cert = None
- header = self.getHeader()
-
- # If user enabled host certificate verification
- try:
- verify = self.sslverify
- cert = self.sslclient
- except:
- self.logMsg("Could not load SSL settings.", 1)
-
- # Start session
- self.s = requests.Session()
- self.s.headers = header
- self.s.verify = verify
- self.s.cert = cert
- # Retry connections to the server
- self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
- self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
-
- self.logMsg("Requests session started on: %s" % self.server)
-
- def stopSession(self):
- try:
- self.s.close()
- except:
- self.logMsg("Requests session could not be terminated.", 1)
-
- def getHeader(self, authenticate=True):
-
- clientInfo = self.clientInfo
-
- deviceName = clientInfo.getDeviceName()
- deviceId = clientInfo.getMachineId()
- version = clientInfo.getVersion()
-
- if not authenticate:
- # If user is not authenticated
- auth = 'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (deviceName, deviceId, version)
- header = {'Content-type': 'application/json', 'Accept-encoding': 'gzip', 'Accept-Charset': 'UTF-8,*', 'Authorization': auth}
-
- self.logMsg("Header: %s" % header, 2)
- return header
-
- else:
- userId = self.userId
- token = self.token
- # Attached to the requests session
- auth = 'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (userId, deviceName, deviceId, version)
- header = {'Content-type': 'application/json', 'Accept-encoding': 'gzip', 'Accept-Charset': 'UTF-8,*', 'Authorization': auth, 'X-MediaBrowser-Token': token}
-
- self.logMsg("Header: %s" % header, 2)
- return header
-
- def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
-
- self.logMsg("=== ENTER downloadUrl ===", 2)
-
- WINDOW = self.WINDOW
- timeout = self.timeout
- default_link = ""
-
- try:
-
- # 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)
-
- 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 utils.settings('sslverify') == "true":
- verifyssl = True
- if utils.settings('sslcert') != "None":
- cert = utils.settings('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 body in the response
- self.logMsg("====== 204 Success ======", 2)
- return default_link
-
- elif r.status_code == requests.codes.ok:
- try:
- # UTF-8 - JSON object
- r = r.json()
- self.logMsg("====== 200 Success ======", 2)
- self.logMsg("Response: %s" % r, 2)
- return r
- except:
- if r.headers.get('content-type') == "text/html":
- pass
- else:
- 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:
- # 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)
- self.logMsg(e, 1)
-
- except requests.exceptions.HTTPError as e:
-
- if r.status_code == 401:
- # Unauthorized
- status = WINDOW.getProperty("Server_status")
-
- if 'x-application-error-code' in r.headers:
- if r.headers['X-Application-Error-Code'] == "ParentalControl":
- # Parental control - access restricted
- WINDOW.setProperty("Server_status", "restricted")
- xbmcgui.Dialog().notification("Emby server", "Access restricted.", xbmcgui.NOTIFICATION_ERROR, time=5000)
- return False
- elif r.headers['X-Application-Error-Code'] == "UnauthorizedAccessException":
- # User tried to do something his emby account doesn't allow - admin restricted in some way
- pass
-
- elif (status == "401") or (status == "Auth"):
- pass
-
- else:
- # 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)
- return 401
-
- elif (r.status_code == 301) or (r.status_code == 302):
- # Redirects
- pass
- elif r.status_code == 400:
- # Bad requests
- pass
-
- except requests.exceptions.SSLError as e:
- self.logMsg("Invalid SSL certificate for: %s" % url, 0)
- self.logMsg(e, 1)
-
- except requests.exceptions.RequestException as e:
- self.logMsg("Unknown error connecting to: %s" % url, 0)
- self.logMsg(e, 1)
-
- return default_link
diff --git a/resources/lib/Entrypoint.py b/resources/lib/Entrypoint.py
deleted file mode 100644
index 95a5a327..00000000
--- a/resources/lib/Entrypoint.py
+++ /dev/null
@@ -1,694 +0,0 @@
-import xbmcaddon
-import xbmcplugin
-import xbmc
-import xbmcgui
-import xbmcvfs
-import os, sys
-import threading
-import json
-import urllib
-import time
-
-WINDOW = xbmcgui.Window(10000)
-
-import Utils as utils
-from ClientInformation import ClientInformation
-from PlaybackUtils import PlaybackUtils
-from PlayUtils import PlayUtils
-from DownloadUtils import DownloadUtils
-from ReadEmbyDB import ReadEmbyDB
-from API import API
-from UserPreferences import UserPreferences
-
-
-##### Play items via plugin://plugin.video.emby/ #####
-def doPlayback(id):
- url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % id
- result = DownloadUtils().downloadUrl(url)
- item = PlaybackUtils().PLAY(result, setup="default")
-
-#### DO RESET AUTH #####
-def resetAuth():
- # User tried login and failed too many times
- resp = xbmcgui.Dialog().yesno("Warning", "Emby might lock your account if you fail to log in too many times. Proceed anyway?")
- if resp == 1:
- xbmc.log("Reset login attempts.")
- WINDOW.setProperty("Server_status", "Auth")
- else:
- xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
-
-### ADD ADDITIONAL USERS ###
-def addUser():
-
- doUtils = DownloadUtils()
- clientInfo = ClientInformation()
- currUser = WINDOW.getProperty("currUser")
- deviceId = clientInfo.getMachineId()
- deviceName = clientInfo.getDeviceName()
-
- # Get session
- url = "{server}/mediabrowser/Sessions?DeviceId=%s" % deviceId
- result = doUtils.downloadUrl(url)
-
- try:
- sessionId = result[0][u'Id']
- additionalUsers = result[0][u'AdditionalUsers']
- # Add user to session
- userlist = {}
- users = []
- url = "{server}/mediabrowser/Users?IsDisabled=false&IsHidden=false"
- result = doUtils.downloadUrl(url)
-
- # pull the list of users
- for user in result:
- name = user[u'Name']
- userId = user[u'Id']
- if currUser not in name:
- userlist[name] = userId
- users.append(name)
-
- # Display dialog if there's additional users
- if additionalUsers:
-
- option = xbmcgui.Dialog().select("Add/Remove user from the session", ["Add user", "Remove user"])
- # Users currently in the session
- additionalUserlist = {}
- additionalUsername = []
- # Users currently in the session
- for user in additionalUsers:
- name = user[u'UserName']
- userId = user[u'UserId']
- additionalUserlist[name] = userId
- additionalUsername.append(name)
-
- if option == 1:
- # User selected Remove user
- resp = xbmcgui.Dialog().select("Remove user from the session", additionalUsername)
- if resp > -1:
- selected = additionalUsername[resp]
- selected_userId = additionalUserlist[selected]
- url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, selected_userId)
- postdata = {}
- doUtils.downloadUrl(url, postBody=postdata, type="DELETE")
- xbmcgui.Dialog().notification("Success!", "%s removed from viewing session" % selected, time=1000)
-
- # clear picture
- position = WINDOW.getProperty('EmbyAdditionalUserPosition.' + selected_userId)
- WINDOW.clearProperty('EmbyAdditionalUserImage.' + str(position))
- return
- else:
- return
-
- elif option == 0:
- # User selected Add user
- for adduser in additionalUsername:
- try: # Remove from selected already added users. It is possible they are hidden.
- users.remove(adduser)
- except: pass
-
- elif option < 0:
- # User cancelled
- return
-
- # Subtract any additional users
- xbmc.log("Displaying list of users: %s" % users)
- resp = xbmcgui.Dialog().select("Add user to the session", users)
- # post additional user
- if resp > -1:
- selected = users[resp]
- selected_userId = userlist[selected]
- url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, selected_userId)
- postdata = {}
- doUtils.downloadUrl(url, postBody=postdata, type="POST")
- xbmcgui.Dialog().notification("Success!", "%s added to viewing session" % selected, time=1000)
-
- except:
- xbmc.log("Failed to add user to session.")
- xbmcgui.Dialog().notification("Error", "Unable to add/remove user from the session.", xbmcgui.NOTIFICATION_ERROR)
-
- try:
- # Add additional user images
- #always clear the individual items first
- totalNodes = 10
- for i in range(totalNodes):
- if not WINDOW.getProperty('EmbyAdditionalUserImage.' + str(i)):
- break
- WINDOW.clearProperty('EmbyAdditionalUserImage.' + str(i))
-
- url = "{server}/mediabrowser/Sessions?DeviceId=%s" % deviceId
- result = doUtils.downloadUrl(url)
- additionalUsers = result[0][u'AdditionalUsers']
- count = 0
- for additionaluser in additionalUsers:
- url = "{server}/mediabrowser/Users/%s?format=json" % (additionaluser[u'UserId'])
- result = doUtils.downloadUrl(url)
- WINDOW.setProperty("EmbyAdditionalUserImage." + str(count),API().getUserArtwork(result,"Primary"))
- WINDOW.setProperty("EmbyAdditionalUserPosition." + str(additionaluser[u'UserId']),str(count))
- count +=1
- except:
- pass
-
-# THEME MUSIC/VIDEOS
-def getThemeMedia():
-
- doUtils = DownloadUtils()
- playUtils = PlayUtils()
-
- currUser = WINDOW.getProperty('currUser')
- server = WINDOW.getProperty('server%s' % currUser)
- playback = None
-
- library = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/").decode('utf-8')
-
- # Choose playback method
- resp = xbmcgui.Dialog().select("Choose playback method for your themes", ["Direct Play", "Direct Stream"])
- if resp == 0:
- # Direct Play
- playback = "DirectPlay"
- elif resp == 1:
- # Direct Stream
- playback = "DirectStream"
- else:return
-
- # Set custom path for user
- tvtunes_path = xbmc.translatePath("special://profile/addon_data/script.tvtunes/").decode('utf-8')
- if xbmcvfs.exists(tvtunes_path):
- tvtunes = xbmcaddon.Addon(id="script.tvtunes")
- tvtunes.setSetting('custom_path_enable', "true")
- tvtunes.setSetting('custom_path', library)
- xbmc.log("TV Tunes custom path is enabled and set.")
- else:
- # if it does not exist this will not work so warn user, often they need to edit the settings first for it to be created.
- dialog = xbmcgui.Dialog()
- dialog.ok('Warning', 'The settings file does not exist in tvtunes. Go to the tvtunes addon and change a setting, then come back and re-run')
- return
-
-
- # Create library directory
- if not xbmcvfs.exists(library):
- xbmcvfs.mkdir(library)
-
- # Get every user view Id
- userViews = []
- url = "{server}/mediabrowser/Users/{UserId}/Items?format=json"
- result = doUtils.downloadUrl(url)
-
- for view in result[u'Items']:
- userviewId = view[u'Id']
- userViews.append(userviewId)
-
-
- # Get Ids with Theme Videos
- itemIds = {}
- for view in userViews:
- url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view
- result = doUtils.downloadUrl(url)
- if result[u'TotalRecordCount'] != 0:
- for item in result[u'Items']:
- itemId = item[u'Id']
- folderName = item[u'Name']
- folderName = utils.normalize_string(folderName.encode('utf-8'))
- itemIds[itemId] = folderName
-
- # Get paths for theme videos
- for itemId in itemIds:
- nfo_path = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId])
- # Create folders for each content
- if not xbmcvfs.exists(nfo_path):
- xbmcvfs.mkdir(nfo_path)
- # Where to put the nfos
- nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo")
-
- url = "{server}/mediabrowser/Items/%s/ThemeVideos?format=json" % itemId
- result = doUtils.downloadUrl(url)
-
- # Create nfo and write themes to it
- nfo_file = open(nfo_path, 'w')
- pathstowrite = ""
- # May be more than one theme
- for theme in result[u'Items']:
- if playback == "DirectPlay":
- playurl = playUtils.directPlay(theme)
- else:
- playurl = playUtils.directStream(result, server, theme[u'Id'], "ThemeVideo")
- pathstowrite += ('%s' % playurl.encode('utf-8'))
-
- # Check if the item has theme songs and add them
- url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
- result = doUtils.downloadUrl(url)
-
- # May be more than one theme
- for theme in result[u'Items']:
- if playback == "DirectPlay":
- playurl = playUtils.directPlay(theme)
- else:
- playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
- pathstowrite += ('%s' % playurl.encode('utf-8'))
-
- nfo_file.write(
- '%s' % pathstowrite
- )
- # Close nfo file
- nfo_file.close()
-
- # Get Ids with Theme songs
- musicitemIds = {}
- for view in userViews:
- url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view
- result = doUtils.downloadUrl(url)
- if result[u'TotalRecordCount'] != 0:
- for item in result[u'Items']:
- itemId = item[u'Id']
- folderName = item[u'Name']
- folderName = utils.normalize_string(folderName.encode('utf-8'))
- musicitemIds[itemId] = folderName
-
- # Get paths
- for itemId in musicitemIds:
-
- # if the item was already processed with video themes back out
- if itemId in itemIds:
- continue
-
- nfo_path = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/%s/" % musicitemIds[itemId])
- # Create folders for each content
- if not xbmcvfs.exists(nfo_path):
- xbmcvfs.mkdir(nfo_path)
- # Where to put the nfos
- nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo")
-
- url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
- result = doUtils.downloadUrl(url)
-
- # Create nfo and write themes to it
- nfo_file = open(nfo_path, 'w')
- pathstowrite = ""
- # May be more than one theme
- for theme in result[u'Items']:
- if playback == "DirectPlay":
- playurl = playUtils.directPlay(theme)
- else:
- playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
- pathstowrite += ('%s' % playurl.encode('utf-8'))
-
- nfo_file.write(
- '%s' % pathstowrite
- )
- # Close nfo file
- nfo_file.close()
-
-def userPreferences():
- doUtils = DownloadUtils()
- addonSettings = xbmcaddon.Addon(id='plugin.video.emby')
- userPreferencesPage = UserPreferences("script-emby-kodi-UserPreferences.xml", addonSettings.getAddonInfo('path'), "default", "1080i")
- url = "{server}/mediabrowser/Users/{UserId}"
- result = doUtils.downloadUrl(url)
- configuration = result[u'Configuration']
- userPreferencesPage.setConfiguration(configuration)
- userPreferencesPage.setName(result[u'Name'])
- userPreferencesPage.setImage(API().getUserArtwork(result,"Primary"))
-
- userPreferencesPage.doModal()
- if userPreferencesPage.isSave():
- url = "{server}/mediabrowser/Users/{UserId}/Configuration"
- postdata = userPreferencesPage.getConfiguration()
- doUtils.downloadUrl(url, postBody=postdata, type="POST")
-
-##### BROWSE EMBY CHANNELS #####
-def BrowseChannels(id, folderid=None):
-
- _addon_id = int(sys.argv[1])
- _addon_url = sys.argv[0]
-
- xbmcplugin.setContent(int(sys.argv[1]), 'files')
- if folderid:
- url = "{server}/mediabrowser/Channels/" + id + "/Items?userid={UserId}&folderid=" + folderid + "&format=json"
- else:
- if id == "0": # id 0 is the root channels folder
- url = "{server}/mediabrowser/Channels?{UserId}&format=json"
- else:
- url = "{server}/mediabrowser/Channels/" + id + "/Items?userid={UserId}&format=json"
-
- results = DownloadUtils().downloadUrl(url)
- if results:
- result = results.get("Items")
- if(result == None):
- result = []
-
- item_count = len(result)
- current_item = 1;
-
- for item in result:
- id=str(item.get("Id")).encode('utf-8')
- type=item.get("Type").encode('utf-8')
-
-
- if(item.get("Name") != None):
- tempTitle = item.get("Name")
- tempTitle=tempTitle.encode('utf-8')
- else:
- tempTitle = "Missing Title"
-
- if type=="ChannelFolderItem":
- isFolder = True
- else:
- isFolder = False
- item_type = str(type).encode('utf-8')
-
- if(item.get("ChannelId") != None):
- channelId = str(item.get("ChannelId")).encode('utf-8')
-
- channelName = ''
- if(item.get("ChannelName") != None):
- channelName = item.get("ChannelName").encode('utf-8')
-
- if(item.get("PremiereDate") != None):
- premieredatelist = (item.get("PremiereDate")).split("T")
- premieredate = premieredatelist[0]
- else:
- premieredate = ""
-
- #mediaStreams=API().getMediaStreams(item, True)
-
- #people = API().getPeople(item)
-
- # Process Genres
- genre = API().getGenre(item)
-
- # Process UserData
- userData = item.get("UserData")
- PlaybackPositionTicks = '100'
- overlay = "0"
- favorite = "False"
- seekTime = 0
- if(userData != None):
- if userData.get("Played") != True:
- overlay = "7"
- watched = "true"
- else:
- overlay = "6"
- watched = "false"
- if userData.get("IsFavorite") == True:
- overlay = "5"
- favorite = "True"
- else:
- favorite = "False"
- if userData.get("PlaybackPositionTicks") != None:
- PlaybackPositionTicks = str(userData.get("PlaybackPositionTicks"))
- reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
- seekTime = reasonableTicks / 10000
-
- playCount = 0
- if(userData != None and userData.get("Played") == True):
- playCount = 1
- # Populate the details list
- details={'title' : tempTitle,
- 'channelname' : channelName,
- 'plot' : item.get("Overview"),
- 'Overlay' : overlay,
- 'playcount' : str(playCount)}
-
- if item.get("Type") == "ChannelVideoItem":
- xbmcplugin.setContent(_addon_id, 'movies')
- elif item.get("Type") == "ChannelAudioItem":
- xbmcplugin.setContent(_addon_id, 'songs')
-
- # Populate the extraData list
- extraData={'thumb' : API().getArtwork(item, "Primary") ,
- 'fanart_image' : API().getArtwork(item, "Backdrop") ,
- 'poster' : API().getArtwork(item, "poster") ,
- 'tvshow.poster': API().getArtwork(item, "tvshow.poster") ,
- 'banner' : API().getArtwork(item, "Banner") ,
- 'clearlogo' : API().getArtwork(item, "Logo") ,
- 'discart' : API().getArtwork(item, "Disc") ,
- 'clearart' : API().getArtwork(item, "Art") ,
- 'landscape' : API().getArtwork(item, "Thumb") ,
- 'id' : id ,
- 'rating' : item.get("CommunityRating"),
- 'year' : item.get("ProductionYear"),
- 'premieredate' : premieredate,
- 'genre' : genre,
- 'playcount' : str(playCount),
- 'itemtype' : item_type}
-
- if extraData['thumb'] == '':
- extraData['thumb'] = extraData['fanart_image']
-
- liz = xbmcgui.ListItem(tempTitle)
-
- artTypes=['poster', 'tvshow.poster', 'fanart_image', 'clearlogo', 'discart', 'banner', 'clearart', 'landscape', 'small_poster', 'tiny_poster', 'medium_poster','small_fanartimage', 'medium_fanartimage', 'medium_landscape', 'fanart_noindicators']
-
- for artType in artTypes:
- imagePath=str(extraData.get(artType,''))
- liz=PlaybackUtils().setArt(liz,artType, imagePath)
-
- liz.setThumbnailImage(API().getArtwork(item, "Primary"))
- liz.setIconImage('DefaultTVShows.png')
- #liz.setInfo( type="Video", infoLabels={ "Rating": item.get("CommunityRating") })
- #liz.setInfo( type="Video", infoLabels={ "Plot": item.get("Overview") })
-
- if type=="Channel":
- file = _addon_url + "?id=%s&mode=channels"%id
- xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz, isFolder=True)
-
- elif isFolder == True:
- file = _addon_url + "?id=%s&mode=channelsfolder&folderid=%s" %(channelId, id)
- xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz, isFolder=True)
- else:
- file = _addon_url + "?id=%s&mode=play"%id
- liz.setProperty('IsPlayable', 'true')
- xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz)
-
- xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
-
-##### GET NEXTUP EPISODES FOR TAGNAME #####
-def getNextUpEpisodes(tagname,limit):
- count=0
-
- #if the addon is called with nextup parameter, we return the nextepisodes list of the given tagname
- xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
- # First we get a list of all the in-progress TV shows - filtered by tag
- json_query_string = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "sort": { "order": "descending", "method": "lastplayed" }, "filter": {"and": [{"operator":"true", "field":"inprogress", "value":""}, {"operator": "is", "field": "tag", "value": "%s"}]}, "properties": [ "title", "studio", "mpaa", "file", "art" ] }, "id": "libTvShows"}' %tagname)
-
- json_result = json.loads(json_query_string)
- # If we found any, find the oldest unwatched show for each one.
- if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
- for item in json_result['result']['tvshows']:
-
- # If Ignore Specials is true only choose episodes from seasons greater than 0.
- if utils.settings("ignoreSpecialsNextEpisodes")=="true":
- json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "tvshowid": %d, "sort": {"method":"episode"}, "filter": {"and": [ {"field": "playcount", "operator": "lessthan", "value":"1"}, {"field": "season", "operator": "greaterthan", "value": "0"} ]}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "writer", "dateadded", "lastplayed" ], "limits":{"end":1}}, "id": "1"}' %item['tvshowid'])
- else:
- json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "tvshowid": %d, "sort": {"method":"episode"}, "filter": {"field": "playcount", "operator": "lessthan", "value":"1"}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "writer", "dateadded", "lastplayed" ], "limits":{"end":1}}, "id": "1"}' %item['tvshowid'])
-
- if json_query2:
- json_query2 = json.loads(json_query2)
- if json_query2.has_key('result') and json_query2['result'].has_key('episodes'):
- for item in json_query2['result']['episodes']:
- liz = createListItem(item)
- xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item['file'], listitem=liz)
- count +=1
- if count == limit:
- break
- xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
-
-def getInProgressEpisodes(tagname,limit):
- count = 0
- #if the addon is called with inprogressepisodes parameter, we return the inprogressepisodes list of the given tagname
- xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
- # First we get a list of all the in-progress TV shows - filtered by tag
- json_query_string = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "sort": { "order": "descending", "method": "lastplayed" }, "filter": {"and": [{"operator":"true", "field":"inprogress", "value":""}, {"operator": "contains", "field": "tag", "value": "%s"}]}, "properties": [ "title", "studio", "mpaa", "file", "art" ] }, "id": "libTvShows"}' %tagname)
- json_result = json.loads(json_query_string)
- # If we found any, find all in progress episodes for each one.
- if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
- for item in json_result['result']['tvshows']:
- json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "tvshowid": %d, "sort": {"method":"episode"}, "filter": {"field": "inprogress", "operator": "true", "value":""}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "cast", "streamdetails", "firstaired", "runtime", "writer", "dateadded", "lastplayed" ]}, "id": "1"}' %item['tvshowid'])
-
- if json_query2:
- json_query2 = json.loads(json_query2)
- if json_query2.has_key('result') and json_query2['result'].has_key('episodes'):
- for item in json_query2['result']['episodes']:
- liz = createListItem(item)
- xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item['file'], listitem=liz)
- count +=1
- if count == limit:
- break
- xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
-
-def getRecentEpisodes(tagname,limit):
- #if the addon is called with recentepisodes parameter, we return the recentepisodes list of the given tagname
- xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
- # First we get a list of all the TV shows - filtered by tag
- json_query_string = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "sort": { "order": "descending", "method": "dateadded" }, "properties": [ "title","sorttitle" ], "filter": {"operator": "contains", "field": "tag", "value": "%s"} }, "id": "libTvShows"}' %tagname)
- json_result = json.loads(json_query_string)
-
- # If we found any, put all tv show id's in a list
- if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
- alltvshowIds = list()
- for tvshow in json_result['result']['tvshows']:
- alltvshowIds.append(tvshow["tvshowid"])
- alltvshowIds = set(alltvshowIds)
-
- #get all recently added episodes
- json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "sort": {"order": "descending", "method": "dateadded"}, "filter": {"field": "playcount", "operator": "lessthan", "value":"1"}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed" ]}, "limits":{"end":%d}, "id": "1"}' %limit)
- count = 0
- if json_query2:
- json_query2 = json.loads(json_query2)
- if json_query2.has_key('result') and json_query2['result'].has_key('episodes'):
- for item in json_query2['result']['episodes']:
- if item["tvshowid"] in alltvshowIds:
- liz = createListItem(item)
- xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item['file'], listitem=liz)
- count += 1
- if count == limit:
- break
- xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
-
-def createListItem(item):
-
- liz = xbmcgui.ListItem(item['title'])
- liz.setInfo( type="Video", infoLabels={ "Title": item['title'] })
- liz.setProperty('IsPlayable', 'true')
- liz.setInfo( type="Video", infoLabels={ "duration": str(item['runtime']/60) })
-
- if "episode" in item:
- episode = "%.2d" % float(item['episode'])
- liz.setInfo( type="Video", infoLabels={ "Episode": item['episode'] })
-
- if "season" in item:
- season = "%.2d" % float(item['season'])
- liz.setInfo( type="Video", infoLabels={ "Season": item['season'] })
-
- if season and episode:
- episodeno = "s%se%s" %(season,episode)
- liz.setProperty("episodeno", episodeno)
-
- if "firstaired" in item:
- liz.setInfo( type="Video", infoLabels={ "Premiered": item['firstaired'] })
-
- plot = item['plot']
- liz.setInfo( type="Video", infoLabels={ "Plot": plot })
-
- if "showtitle" in item:
- liz.setInfo( type="Video", infoLabels={ "TVshowTitle": item['showtitle'] })
-
- if "rating" in item:
- liz.setInfo( type="Video", infoLabels={ "Rating": str(round(float(item['rating']),1)) })
- liz.setInfo( type="Video", infoLabels={ "Playcount": item['playcount'] })
- if "director" in item:
- liz.setInfo( type="Video", infoLabels={ "Director": " / ".join(item['director']) })
- if "writer" in item:
- liz.setInfo( type="Video", infoLabels={ "Writer": " / ".join(item['writer']) })
-
- if "cast" in item:
- listCast = []
- listCastAndRole = []
- for castmember in item["cast"]:
- listCast.append( castmember["name"] )
- listCastAndRole.append( (castmember["name"], castmember["role"]) )
- cast = [listCast, listCastAndRole]
- liz.setInfo( type="Video", infoLabels={ "Cast": cast[0] })
- liz.setInfo( type="Video", infoLabels={ "CastAndRole": cast[1] })
-
- liz.setProperty("resumetime", str(item['resume']['position']))
- liz.setProperty("totaltime", str(item['resume']['total']))
- liz.setArt(item['art'])
- liz.setThumbnailImage(item['art'].get('thumb',''))
- liz.setIconImage('DefaultTVShows.png')
- liz.setProperty("dbid", str(item['episodeid']))
- liz.setProperty("fanart_image", item['art'].get('tvshow.fanart',''))
- for key, value in item['streamdetails'].iteritems():
- for stream in value:
- liz.addStreamInfo( key, stream )
-
- return liz
-
-##### GET EXTRAFANART FOR LISTITEM #####
-def getExtraFanArt():
- itemPath = ""
- embyId = ""
-
- #get extrafanart for listitem - this will only be used for skins that actually call the listitem's path + fanart dir...
- 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]
-
- utils.logMsg("%s %s" % ("Emby addon", "getExtraFanArt"), "requesting extraFanArt for Id: " + embyId, 1)
-
- #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"]) > 0):
- WINDOW = xbmcgui.Window(10000)
- username = WINDOW.getProperty('currUser')
- server = WINDOW.getProperty('server%s' % username)
- totalbackdrops = len(item["BackdropImageTags"])
- count = 0
- for backdrop in item["BackdropImageTags"]:
- backgroundUrl = "%s/mediabrowser/Items/%s/Images/Backdrop/%s/?MaxWidth=10000&MaxHeight=10000&Format=original&Tag=%s&EnableImageEnhancers=false" % (server, embyId, str(count), backdrop)
- count += 1
- fanartFile = os.path.join(fanartDir,"fanart" + backdrop + ".jpg")
- li = xbmcgui.ListItem(backdrop, 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 Exception as e:
- utils.logMsg("%s %s" % ("Emby addon", "Error in getExtraFanArt"), str(e), 1)
- pass
-
- #always do endofdirectory to prevent errors in the logs
- xbmcplugin.endOfDirectory(int(sys.argv[1]))
-
-def addDirectoryItem(label, path, folder=True):
- li = xbmcgui.ListItem(label, path=path)
- li.setThumbnailImage("special://home/addons/plugin.video.emby/icon.png")
- li.setArt({"fanart":"special://home/addons/plugin.video.emby/fanart.jpg"})
- li.setArt({"landscape":"special://home/addons/plugin.video.emby/fanart.jpg"})
- xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=folder)
-
-# if the addon is called without parameters we show the listing...
-def doMainListing():
-
- xbmcplugin.setContent(int(sys.argv[1]), 'files')
- #get emby nodes from the window props
- embyProperty = WINDOW.getProperty("Emby.nodes.total")
- if embyProperty:
- totalNodes = int(embyProperty)
- for i in range(totalNodes):
- path = WINDOW.getProperty("Emby.nodes.%s.index" %str(i))
- if not path:
- path = WINDOW.getProperty("Emby.nodes.%s.content" %str(i))
- label = WINDOW.getProperty("Emby.nodes.%s.title" %str(i))
- if path:
- addDirectoryItem(label, path)
-
- # some extra entries for settings and stuff. TODO --> localize the labels
- addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings")
- addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync")
- addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser")
- addDirectoryItem("Configure user preferences", "plugin://plugin.video.emby/?mode=userprefs")
- addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset")
- addDirectoryItem("Cache all images to Kodi texture cache (advanced)", "plugin://plugin.video.emby/?mode=texturecache")
- addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia")
-
- xbmcplugin.endOfDirectory(int(sys.argv[1]))
\ No newline at end of file
diff --git a/resources/lib/KodiMonitor.py b/resources/lib/KodiMonitor.py
deleted file mode 100644
index 2d4dc943..00000000
--- a/resources/lib/KodiMonitor.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#################################################################################################
-# Kodi Monitor
-# Watched events that occur in Kodi, like setting media watched
-#################################################################################################
-
-import xbmc
-import xbmcgui
-import xbmcaddon
-import json
-
-import Utils as utils
-from WriteKodiVideoDB import WriteKodiVideoDB
-from ReadKodiDB import ReadKodiDB
-from PlayUtils import PlayUtils
-from DownloadUtils import DownloadUtils
-from PlaybackUtils import PlaybackUtils
-
-
-class Kodi_Monitor( xbmc.Monitor ):
-
- WINDOW = xbmcgui.Window(10000)
-
- def __init__(self, *args, **kwargs):
- xbmc.Monitor.__init__(self)
-
- def logMsg(self, msg, lvl = 1):
-
- className = self.__class__.__name__
- utils.logMsg("%s %s" % ("EMBY", className), msg, int(lvl))
-
- def onScanStarted(self, library):
- utils.window('kodiScan', value="true")
- self.logMsg("Kodi library scan running.", 2)
-
- def onScanFinished(self, library):
- utils.window('kodiScan', clear=True)
- self.logMsg("Kodi library scan finished.", 2)
-
- #this library monitor is used to detect a watchedstate change by the user through the library
- #as well as detect when a library item has been deleted to pass the delete to the Emby server
- def onNotification (self, sender, method, data):
-
- WINDOW = self.WINDOW
- downloadUtils = DownloadUtils()
- #player started playing an item -
- if ("Playlist.OnAdd" in method or "Player.OnPlay" in method):
-
- jsondata = json.loads(data)
- if jsondata:
- 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")
-
- if (utils.settings('useDirectPaths')=='true' and not type == "song") or (type == "song" and utils.settings('enableMusicSync') == "true"):
-
- if type == "song":
- connection = utils.KodiSQL('music')
- cursor = connection.cursor()
- embyid = ReadKodiDB().getEmbyIdByKodiId(id, type, connection, cursor)
- cursor.close()
- else:
- embyid = ReadKodiDB().getEmbyIdByKodiId(id,type)
-
- if embyid:
-
- url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % embyid
- result = downloadUtils.downloadUrl(url)
- self.logMsg("Result: %s" % result, 2)
-
- playurl = None
- count = 0
- while not playurl and count < 2:
- try:
- playurl = xbmc.Player().getPlayingFile()
- except RuntimeError:
- xbmc.sleep(200)
- else:
- listItem = xbmcgui.ListItem()
- PlaybackUtils().setProperties(playurl, result, listItem)
-
- if type == "song" and utils.settings('directstreammusic') == "true":
- utils.window('%splaymethod' % playurl, value="DirectStream")
- else:
- utils.window('%splaymethod' % playurl, value="DirectPlay")
-
- count += 1
-
- if method == "VideoLibrary.OnUpdate":
- # Triggers 4 times, the following is only for manually marking as watched/unwatched
- jsondata = json.loads(data)
-
- try:
- playcount = jsondata.get('playcount')
- item = jsondata['item']['id']
- type = jsondata['item']['type']
- prop = utils.window('Played%s%s' % (type, item))
- except:
- self.logMsg("Could not process VideoLibrary.OnUpdate data.", 1)
- else:
- self.logMsg("VideoLibrary.OnUpdate: %s" % data, 2)
- if prop != "true":
- # Set property to prevent the multi triggering
- utils.window('Played%s%s' % (type, item), "true")
- WriteKodiVideoDB().updatePlayCountFromKodi(item, type, playcount)
-
- self.clearProperty(type, item)
-
- if method == "System.OnWake":
- xbmc.sleep(10000) #Allow network to wake up
- WINDOW.setProperty("OnWakeSync", "true")
-
- if method == "VideoLibrary.OnRemove":
- xbmc.log('Intercepted remove from sender: ' + sender + ' method: ' + method + ' data: ' + data)
- jsondata = json.loads(data)
- id = ReadKodiDB().getEmbyIdByKodiId(jsondata.get("id"), jsondata.get("type"))
- if id == None:
- return
- xbmc.log("Deleting Emby ID: " + id + " from database")
- connection = utils.KodiSQL()
- cursor = connection.cursor()
- cursor.execute("DELETE FROM emby WHERE emby_id = ?", (id,))
- connection.commit()
- cursor.close
-
- if jsondata:
- if jsondata.get("type") == "episode" or "movie":
- url='{server}/mediabrowser/Items?Ids=' + id + '&format=json'
- #This is a check to see if the item exists on the server, if it doesn't it may have already been deleted by another client
- result = DownloadUtils().downloadUrl(url)
- item = result.get("Items")[0]
- if data:
- return_value = xbmcgui.Dialog().yesno("Confirm Delete", "Delete file on Emby Server?")
- if return_value:
- url='{server}/mediabrowser/Items/' + id
- xbmc.log('Deleting via URL: ' + url)
- DownloadUtils().downloadUrl(url, type="DELETE")
-
- elif method == "Playlist.OnClear":
- self.logMsg("Clear playback properties.", 2)
- utils.window('propertiesPlayback', clear=True)
-
- def clearProperty(self, type, id):
- # The sleep is necessary since VideoLibrary.OnUpdate
- # triggers 4 times in a row.
- xbmc.sleep(100)
- utils.window('Played%s%s' % (type,id), clear=True)
-
- # Clear the widget cache
- utils.window('clearwidgetcache', value="clear")
\ No newline at end of file
diff --git a/resources/lib/LibrarySync.py b/resources/lib/LibrarySync.py
deleted file mode 100644
index f50c88e2..00000000
--- a/resources/lib/LibrarySync.py
+++ /dev/null
@@ -1,1191 +0,0 @@
-#################################################################################################
-# LibrarySync
-#################################################################################################
-
-import xbmc
-import xbmcgui
-import xbmcaddon
-import xbmcvfs
-import json
-import sqlite3
-import inspect
-import threading
-import urllib
-from datetime import datetime, timedelta, time
-from itertools import chain
-import urllib2
-import os
-
-import KodiMonitor
-from API import API
-import Utils as utils
-from ClientInformation import ClientInformation
-from DownloadUtils import DownloadUtils
-from ReadEmbyDB import ReadEmbyDB
-from ReadKodiDB import ReadKodiDB
-from WriteKodiVideoDB import WriteKodiVideoDB
-from WriteKodiMusicDB import WriteKodiMusicDB
-from VideoNodes import VideoNodes
-
-addondir = xbmc.translatePath(xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('profile'))
-dataPath = os.path.join(addondir,"library")
-movieLibrary = os.path.join(dataPath,'movies')
-tvLibrary = os.path.join(dataPath,'tvshows')
-
-WINDOW = xbmcgui.Window( 10000 )
-
-class LibrarySync(threading.Thread):
-
- _shared_state = {}
-
- KodiMonitor = KodiMonitor.Kodi_Monitor()
- clientInfo = ClientInformation()
-
- addonName = clientInfo.getAddonName()
-
- updateItems = []
- userdataItems = []
- removeItems = []
- forceUpdate = False
-
- def __init__(self, *args):
-
- self.__dict__ = self._shared_state
- threading.Thread.__init__(self, *args)
-
- def logMsg(self, msg, lvl=1):
-
- className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
-
- def FullLibrarySync(self,manualRun=False):
-
- startupDone = WINDOW.getProperty("startup") == "done"
- syncInstallRunDone = utils.settings("SyncInstallRunDone") == "true"
- performMusicSync = utils.settings("enableMusicSync") == "true"
- dbSyncIndication = utils.settings("dbSyncIndication") == "true"
-
- ### BUILD VIDEO NODES LISTING ###
- VideoNodes().buildVideoNodesListing()
- ### CREATE SOURCES ###
- if utils.settings("Sources") != "true":
- # Only create sources once
- self.logMsg("Sources.xml created.", 0)
- utils.createSources()
- utils.settings("Sources", "true")
-
- # just do a incremental sync if that is what is required
- if(utils.settings("useIncSync") == "true" and utils.settings("SyncInstallRunDone") == "true") and manualRun == False:
- utils.logMsg("Sync Database", "Using incremental sync instead of full sync useIncSync=True)", 0)
-
- du = DownloadUtils()
-
- lastSync = utils.settings("LastIncrenetalSync")
- if(lastSync == None or len(lastSync) == 0):
- lastSync = "2010-01-01T00:00:00Z"
- utils.logMsg("Sync Database", "Incremental Sync Setting Last Run Time Loaded : " + lastSync, 0)
-
- lastSync = urllib2.quote(lastSync)
-
- url = "{server}/Emby.Kodi.SyncQueue/{UserId}/GetItems?LastUpdateDT=" + lastSync + "&format=json"
- utils.logMsg("Sync Database", "Incremental Sync Get Items URL : " + url, 0)
-
- try:
- results = du.downloadUrl(url)
- changedItems = results["ItemsUpdated"] + results["ItemsAdded"]
- removedItems = results["ItemsRemoved"]
- userChanges = results["UserDataChanged"]
- except:
- utils.logMsg("Sync Database", "Incremental Sync Get Changes Failed", 0)
- pass
- else:
- maxItems = int(utils.settings("incSyncMaxItems"))
- utils.logMsg("Sync Database", "Incremental Sync Changes : " + str(results), 0)
- if(len(changedItems) < maxItems and len(removedItems) < maxItems and len(userChanges) < maxItems):
-
- WINDOW.setProperty("startup", "done")
-
- LibrarySync().remove_items(removedItems)
- LibrarySync().update_items(changedItems)
- LibrarySync().user_data_update(userChanges)
-
- return True
- else:
- utils.logMsg("Sync Database", "Too Many For Incremental Sync (" + str(maxItems) + "), changedItems" + str(len(changedItems)) + " removedItems:" + str(len(removedItems)) + " userChanges:" + str(len(userChanges)), 0)
-
- #set some variable to check if this is the first run
- WINDOW.setProperty("SyncDatabaseRunning", "true")
-
- #show the progress dialog
- pDialog = None
- if (syncInstallRunDone == False or dbSyncIndication or manualRun):
- pDialog = xbmcgui.DialogProgressBG()
- pDialog.create('Emby for Kodi', 'Performing full sync')
-
- if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"):
- utils.logMsg("Sync Database", "Can not start SyncDatabaseShouldStop=True", 0)
- return True
-
- try:
- completed = True
-
- ### PROCESS VIDEO LIBRARY ###
-
- #create the sql connection to video db
- connection = utils.KodiSQL("video")
- cursor = connection.cursor()
-
- #Add the special emby table
- cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER, kodi_file_id INTEGER)")
- try:
- cursor.execute("ALTER TABLE emby ADD COLUMN kodi_file_id INTEGER")
- except: pass
- self.dbCommit(connection)
-
- # sync movies
- self.MoviesFullSync(connection,cursor,pDialog)
-
- if (self.ShouldStop()):
- return False
-
- #sync Tvshows and episodes
- self.TvShowsFullSync(connection,cursor,pDialog)
-
- if (self.ShouldStop()):
- return False
-
- # sync musicvideos
- self.MusicVideosFullSync(connection,cursor,pDialog)
-
- #close sql connection
- cursor.close()
-
- ### PROCESS MUSIC LIBRARY ###
- if performMusicSync:
- #create the sql connection to music db
- connection = utils.KodiSQL("music")
- cursor = connection.cursor()
-
- #Add the special emby table
- cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER, kodi_file_id INTEGER)")
- try:
- cursor.execute("ALTER TABLE emby ADD COLUMN kodi_file_id INTEGER")
- except: pass
- self.dbCommit(connection)
-
- self.MusicFullSync(connection,cursor,pDialog)
- cursor.close()
-
- # set the install done setting
- if(syncInstallRunDone == False and completed):
- utils.settings("SyncInstallRunDone", "true")
- utils.settings("dbCreatedWithVersion", self.clientInfo.getVersion())
-
- # Commit all DB changes at once and Force refresh the library
- #xbmc.executebuiltin("UpdateLibrary(video)")
- #self.updateLibrary("video")
- #xbmc.executebuiltin("UpdateLibrary(music)")
-
- # set prop to show we have run for the first time
- WINDOW.setProperty("startup", "done")
-
- # tell any widgets to refresh because the content has changed
- WINDOW.setProperty("widgetreload", datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
-
- self.SaveLastSync()
-
- finally:
- WINDOW.setProperty("SyncDatabaseRunning", "false")
- utils.logMsg("Sync DB", "syncDatabase Exiting", 0)
-
- if(pDialog != None):
- pDialog.close()
-
- return True
-
- def SaveLastSync(self):
- # save last sync time
-
- du = DownloadUtils()
- url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
-
- try:
- results = du.downloadUrl(url)
- lastSync = results["ServerDateTime"]
- self.logMsg("Sync Database, Incremental Sync Using Server Time: %s" % lastSync, 0)
- lastSync = datetime.strptime(lastSync, "%Y-%m-%dT%H:%M:%SZ")
- lastSync = (lastSync - timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%SZ')
- self.logMsg("Sync Database, Incremental Sync Using Server Time -5 min: %s" % lastSync, 0)
- except:
- lastSync = (datetime.utcnow() - timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%SZ')
- self.logMsg("Sync Database, Incremental Sync Using Client Time -5 min: %s" % lastSync, 0)
-
- self.logMsg("Sync Database, Incremental Sync Setting Last Run Time Saved: %s" % lastSync, 0)
- utils.settings("LastIncrenetalSync", lastSync)
-
- def MoviesFullSync(self,connection, cursor, pDialog):
-
- views = ReadEmbyDB().getCollections("movies")
-
- allKodiMovieIds = list()
- allEmbyMovieIds = list()
-
- for view in views:
-
- allEmbyMovies = ReadEmbyDB().getMovies(view.get('id'))
- allKodiMovies = ReadKodiDB().getKodiMovies(connection, cursor)
-
- for kodimovie in allKodiMovies:
- allKodiMovieIds.append(kodimovie[1])
-
- title = view.get('title')
- content = view.get('content')
-
- if content == "mixed":
- title = "%s - Movies" % title
-
- for kodimovie in allKodiMovies:
- allKodiMovieIds.append(kodimovie[1])
-
- total = len(allEmbyMovies) + 1
- count = 1
-
- #### PROCESS ADDS AND UPDATES ###
- for item in allEmbyMovies:
-
- if (self.ShouldStop()):
- return False
-
- if not item.get('IsFolder'):
- allEmbyMovieIds.append(item["Id"])
-
- if(pDialog != None):
- progressTitle = "Processing " + view.get('title') + " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
-
- kodiMovie = None
- for kodimovie in allKodiMovies:
- if kodimovie[1] == item["Id"]:
- kodiMovie = kodimovie
-
- if kodiMovie == None:
- WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, title)
- else:
- if kodiMovie[2] != API().getChecksum(item):
- WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, title)
-
-
-
- #### PROCESS BOX SETS #####
- utils.logMsg("Sync Movies", "BoxSet Sync Started", 1)
- boxsets = ReadEmbyDB().getBoxSets()
-
- total = len(boxsets) + 1
- count = 1
- for boxset in boxsets:
- if(pDialog != None):
- progressTitle = "Processing BoxSets" + " (" + str(count) + " of " + str(total-1) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
- if(self.ShouldStop()):
- return False
- boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
- WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset, connection, cursor)
-
- WriteKodiVideoDB().removeMoviesFromBoxset(boxset, connection, cursor)
- for boxsetMovie in boxsetMovies:
- if(self.ShouldStop()):
- return False
- WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor)
-
- utils.logMsg("Sync Movies", "BoxSet Sync Finished", 1)
-
- #### PROCESS DELETES #####
- allEmbyMovieIds = set(allEmbyMovieIds)
- for kodiId in allKodiMovieIds:
- if not kodiId in allEmbyMovieIds:
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- ### commit all changes to database ###
- self.dbCommit(connection)
-
- def MusicVideosFullSync(self,connection,cursor, pDialog):
-
- allKodiMusicvideoIds = list()
- allEmbyMusicvideoIds = list()
-
- allEmbyMusicvideos = ReadEmbyDB().getMusicVideos()
- allKodiMusicvideos = ReadKodiDB().getKodiMusicVideos(connection, cursor)
-
- for kodivideo in allKodiMusicvideos:
- allKodiMusicvideoIds.append(kodivideo[1])
-
- total = len(allEmbyMusicvideos) + 1
- count = 1
-
- #### PROCESS ADDS AND UPDATES ###
- for item in allEmbyMusicvideos:
-
- if (self.ShouldStop()):
- return False
-
- if not item.get('IsFolder'):
- allEmbyMusicvideoIds.append(item["Id"])
-
- if(pDialog != None):
- progressTitle = "Processing MusicVideos (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
-
- kodiVideo = None
- for kodivideo in allKodiMusicvideos:
- if kodivideo[1] == item["Id"]:
- kodiVideo = kodivideo
-
- if kodiVideo == None:
- WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
- else:
- if kodiVideo[2] != API().getChecksum(item):
- WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
-
- #### PROCESS DELETES #####
- allEmbyMusicvideoIds = set(allEmbyMusicvideoIds)
- for kodiId in allKodiMusicvideoIds:
- if not kodiId in allEmbyMusicvideoIds:
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- ### commit all changes to database ###
- self.dbCommit(connection)
-
- def TvShowsFullSync(self,connection,cursor,pDialog):
-
- views = ReadEmbyDB().getCollections("tvshows")
-
- allKodiTvShowIds = list()
- allEmbyTvShowIds = list()
-
- for view in views:
-
- allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id'))
- allKodiTvShows = ReadKodiDB().getKodiTvShows(connection, cursor)
-
- title = view.get('title')
- content = view.get('content')
-
- if content == "mixed":
- title = "%s - TV Shows" % title
-
- total = len(allEmbyTvShows) + 1
- count = 1
-
- for kodishow in allKodiTvShows:
- allKodiTvShowIds.append(kodishow[1])
-
- #### TVSHOW: PROCESS ADDS AND UPDATES ###
- for item in allEmbyTvShows:
-
- if (self.ShouldStop()):
- return False
-
- if(pDialog != None):
- progressTitle = "Processing " + view.get('title') + " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
-
- if utils.settings('syncEmptyShows') == "true" or (item.get('IsFolder') and item.get('RecursiveItemCount') != 0):
- allEmbyTvShowIds.append(item["Id"])
-
- #build a list with all Id's and get the existing entry (if exists) in Kodi DB
- kodiShow = None
- for kodishow in allKodiTvShows:
- if kodishow[1] == item["Id"]:
- kodiShow = kodishow
-
- if kodiShow == None:
- # Tv show doesn't exist in Kodi yet so proceed and add it
- WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, title)
- else:
- # If there are changes to the item, perform a full sync of the item
- if kodiShow[2] != API().getChecksum(item):
- WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, title)
-
- #### PROCESS EPISODES ######
- self.EpisodesFullSync(connection,cursor,item["Id"])
-
- #### TVSHOW: PROCESS DELETES #####
- allEmbyTvShowIds = set(allEmbyTvShowIds)
- for kodiId in allKodiTvShowIds:
- if not kodiId in allEmbyTvShowIds:
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- ### commit all changes to database ###
- self.dbCommit(connection)
-
- def EpisodesFullSync(self,connection,cursor,showId):
-
- WINDOW = xbmcgui.Window( 10000 )
-
- allKodiEpisodeIds = list()
- allEmbyEpisodeIds = list()
-
- # Get the kodi parent id
- cursor.execute("SELECT kodi_id FROM emby WHERE emby_id=?",(showId,))
- try:
- kodiShowId = cursor.fetchone()[0]
- except:
- self.logMsg("Unable to find show itemId:%s" % showId, 1)
- return
-
- allEmbyEpisodes = ReadEmbyDB().getEpisodes(showId)
- allKodiEpisodes = ReadKodiDB().getKodiEpisodes(connection, cursor, kodiShowId)
-
- for kodiepisode in allKodiEpisodes:
- allKodiEpisodeIds.append(kodiepisode[1])
-
- #### EPISODES: PROCESS ADDS AND UPDATES ###
- for item in allEmbyEpisodes:
-
- if (self.ShouldStop()):
- return False
-
- allEmbyEpisodeIds.append(item["Id"])
-
- #get the existing entry (if exists) in Kodi DB
- kodiEpisode = None
- for kodiepisode in allKodiEpisodes:
- if kodiepisode[1] == item["Id"]:
- kodiEpisode = kodiepisode
-
- if kodiEpisode == None:
- # Episode doesn't exist in Kodi yet so proceed and add it
- WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(item["Id"], kodiShowId, connection, cursor)
- else:
- # If there are changes to the item, perform a full sync of the item
- if kodiEpisode[2] != API().getChecksum(item):
- WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(item["Id"], kodiShowId, connection, cursor)
-
- #### EPISODES: PROCESS DELETES #####
- allEmbyEpisodeIds = set(allEmbyEpisodeIds)
- for kodiId in allKodiEpisodeIds:
- if (not kodiId in allEmbyEpisodeIds):
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- def MusicFullSync(self, connection,cursor, pDialog):
-
- self.ProcessMusicArtists(connection,cursor,pDialog)
- self.dbCommit(connection)
- self.ProcessMusicAlbums(connection,cursor,pDialog)
- self.dbCommit(connection)
- self.ProcessMusicSongs(connection,cursor,pDialog)
-
- ### commit all changes to database ###
- self.dbCommit(connection)
-
- def ProcessMusicSongs(self,connection,cursor,pDialog):
-
- allKodiSongIds = list()
- allEmbySongIds = list()
-
- allEmbySongs = ReadEmbyDB().getMusicSongsTotal()
- allKodiSongs = ReadKodiDB().getKodiMusicSongs(connection, cursor)
-
- for kodisong in allKodiSongs:
- allKodiSongIds.append(kodisong[1])
-
- total = len(allEmbySongs) + 1
- count = 1
-
- #### PROCESS SONGS ADDS AND UPDATES ###
- for item in allEmbySongs:
-
- if (self.ShouldStop()):
- return False
-
- allEmbySongIds.append(item["Id"])
-
- if(pDialog != None):
- progressTitle = "Processing Music Songs (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
-
- kodiSong = None
- for kodisong in allKodiSongs:
- if kodisong[1] == item["Id"]:
- kodiSong = kodisong
-
- if kodiSong == None:
- WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item,connection, cursor)
- else:
- if kodiSong[2] != API().getChecksum(item):
- WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item,connection, cursor)
-
- #### PROCESS DELETES #####
- allEmbySongIds = set(allEmbySongIds)
- for kodiId in allKodiSongIds:
- if not kodiId in allEmbySongIds:
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- def ProcessMusicArtists(self,connection,cursor,pDialog):
-
- allKodiArtistIds = list()
- allEmbyArtistIds = list()
-
- allEmbyArtists = ReadEmbyDB().getMusicArtistsTotal()
- allKodiArtists = ReadKodiDB().getKodiMusicArtists(connection, cursor)
-
- for kodiartist in allKodiArtists:
- allKodiArtistIds.append(kodiartist[1])
-
- total = len(allEmbyArtists) + 1
- count = 1
-
- #### PROCESS ARTIST ADDS AND UPDATES ###
- for item in allEmbyArtists:
-
- if (self.ShouldStop()):
- return False
-
- allEmbyArtistIds.append(item["Id"])
-
- if(pDialog != None):
- progressTitle = "Processing Music Artists (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
-
- kodiArtist = None
- for kodiartist in allKodiArtists:
- if kodiartist[1] == item["Id"]:
- kodiArtist = kodiartist
-
- if kodiArtist == None:
- WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item,connection, cursor)
- else:
- if kodiArtist[2] != API().getChecksum(item):
- WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item,connection, cursor)
-
- #### PROCESS DELETES #####
- allEmbyArtistIds = set(allEmbyArtistIds)
- for kodiId in allKodiArtistIds:
- if not kodiId in allEmbyArtistIds:
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- def ProcessMusicAlbums(self,connection,cursor,pDialog):
-
- allKodiAlbumIds = list()
- allEmbyAlbumIds = list()
-
- allEmbyAlbums = ReadEmbyDB().getMusicAlbumsTotal()
- allKodiAlbums = ReadKodiDB().getKodiMusicAlbums(connection, cursor)
-
- for kodialbum in allKodiAlbums:
- allKodiAlbumIds.append(kodialbum[1])
-
- total = len(allEmbyAlbums) + 1
- count = 1
-
- #### PROCESS SONGS ADDS AND UPDATES ###
- for item in allEmbyAlbums:
-
- if (self.ShouldStop()):
- return False
-
- allEmbyAlbumIds.append(item["Id"])
-
- if(pDialog != None):
- progressTitle = "Processing Music Albums (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
- count += 1
-
- kodiAlbum = None
- for kodialbum in allKodiAlbums:
- if kodialbum[1] == item["Id"]:
- kodiAlbum = kodialbum
-
- if kodiAlbum == None:
- WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item,connection, cursor)
- else:
- if kodiAlbum[2] != API().getChecksum(item):
- WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item,connection, cursor)
-
- #### PROCESS DELETES #####
- allEmbyAlbumIds = set(allEmbyAlbumIds)
- for kodiId in allKodiAlbumIds:
- if not kodiId in allEmbyAlbumIds:
- WINDOW.setProperty(kodiId,"deleted")
- WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
-
- def IncrementalSync(self, itemList):
-
- startupDone = WINDOW.getProperty("startup") == "done"
-
- #only perform incremental scan when full scan is completed
- if startupDone:
-
- #this will only perform sync for items received by the websocket
- dbSyncIndication = utils.settings("dbSyncIndication") == "true"
- performMusicSync = utils.settings("enableMusicSync") == "true"
- WINDOW.setProperty("SyncDatabaseRunning", "true")
-
- #show the progress dialog
- pDialog = None
- if (dbSyncIndication and xbmc.Player().isPlaying() == False):
- pDialog = xbmcgui.DialogProgressBG()
- pDialog.create('Emby for Kodi', 'Incremental Sync')
- self.logMsg("Doing LibraryChanged : Show Progress IncrementalSync()", 0);
-
- connection = utils.KodiSQL("video")
- cursor = connection.cursor()
-
- try:
- #### PROCESS MOVIES ####
- views = ReadEmbyDB().getCollections("movies")
- for view in views:
- allEmbyMovies = ReadEmbyDB().getMovies(view.get('id'), itemList)
- count = 1
- total = len(allEmbyMovies) + 1
- for item in allEmbyMovies:
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync Movies", progressTitle)
- count = count + 1
- if not item.get('IsFolder'):
- WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
-
- #### PROCESS BOX SETS #####
- boxsets = ReadEmbyDB().getBoxSets()
- count = 1
- total = len(boxsets) + 1
- for boxset in boxsets:
- if(boxset["Id"] in itemList):
- utils.logMsg("IncrementalSync", "Updating box Set : " + str(boxset["Name"]), 1)
- boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
- WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset, connection, cursor)
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync BoxSet", progressTitle)
- count = count + 1
- WriteKodiVideoDB().removeMoviesFromBoxset(boxset, connection, cursor)
- for boxsetMovie in boxsetMovies:
- WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie, boxset, connection, cursor)
- else:
- utils.logMsg("IncrementalSync", "Skipping Box Set : " + boxset["Name"], 1)
-
- #### PROCESS TV SHOWS ####
- views = ReadEmbyDB().getCollections("tvshows")
- for view in views:
- allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id'),itemList)
- count = 1
- total = len(allEmbyTvShows) + 1
- for item in allEmbyTvShows:
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync Tv", progressTitle)
- count = count + 1
- if utils.settings('syncEmptyShows') == "true" or (item.get('IsFolder') and item.get('RecursiveItemCount') != 0):
- kodiId = WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
-
-
- #### PROCESS OTHERS BY THE ITEMLIST ######
- count = 1
- total = len(itemList) + 1
- for item in itemList:
-
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync Items", progressTitle)
- count = count + 1
-
- MBitem = ReadEmbyDB().getItem(item)
- itemType = MBitem.get('Type', "")
-
- #### PROCESS EPISODES ######
- if "Episode" in itemType:
-
- #get the tv show
- cursor.execute("SELECT kodi_id FROM emby WHERE media_type='tvshow' AND emby_id=?", (MBitem.get("SeriesId"),))
- result = cursor.fetchone()
- if result:
- kodi_show_id = result[0]
- else:
- kodi_show_id = None
-
- if kodi_show_id:
- WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(MBitem["Id"], kodi_show_id, connection, cursor)
- else:
- #tv show doesn't exist
- #perform full tvshow sync instead so both the show and episodes get added
- self.TvShowsFullSync(connection,cursor,None)
-
- elif "Season" in itemType:
-
- #get the tv show
- cursor.execute("SELECT kodi_id FROM emby WHERE media_type='tvshow' AND emby_id=?", (MBitem.get("SeriesId"),))
- result = cursor.fetchone()
- if result:
- kodi_show_id = result[0]
- # update season
- WriteKodiVideoDB().updateSeasons(MBitem["SeriesId"], kodi_show_id, connection, cursor)
-
- #### PROCESS BOXSETS ######
- elif "BoxSet" in itemType:
- boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
- WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset,connection, cursor)
-
- for boxsetMovie in boxsetMovies:
- WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor)
-
- #### PROCESS MUSICVIDEOS ####
- elif "MusicVideo" in itemType:
- if not MBitem.get('IsFolder'):
- WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(MBitem["Id"],connection, cursor)
-
- ### commit all changes to database ###
- self.dbCommit(connection)
- cursor.close()
-
- ### PROCESS MUSIC LIBRARY ###
- if performMusicSync:
- connection = utils.KodiSQL("music")
- cursor = connection.cursor()
- for item in itemList:
- MBitem = ReadEmbyDB().getItem(item)
- itemType = MBitem.get('Type', "")
-
- if "MusicArtist" in itemType:
- WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(MBitem, connection, cursor)
- if "MusicAlbum" in itemType:
- WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(MBitem, connection, cursor)
- if "Audio" in itemType:
- WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(MBitem, connection, cursor)
- self.dbCommit(connection)
- cursor.close()
-
- finally:
- if(pDialog != None):
- pDialog.close()
-
- #self.updateLibrary("video")
- WINDOW.setProperty("SyncDatabaseRunning", "false")
- # tell any widgets to refresh because the content has changed
- WINDOW.setProperty("widgetreload", datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
-
- def removefromDB(self, itemList, deleteEmbyItem = False):
-
- dbSyncIndication = utils.settings("dbSyncIndication") == "true"
-
- #show the progress dialog
- pDialog = None
- if (dbSyncIndication and xbmc.Player().isPlaying() == False):
- pDialog = xbmcgui.DialogProgressBG()
- pDialog.create('Emby for Kodi', 'Incremental Sync')
- self.logMsg("Doing LibraryChanged : Show Progress removefromDB()", 0);
-
- # Delete from Kodi before Emby
- # To be able to get mediaType
- doUtils = DownloadUtils()
- video = {}
- music = []
-
- # Database connection to myVideosXX.db
- connectionvideo = utils.KodiSQL()
- cursorvideo = connectionvideo.cursor()
- # Database connection to myMusicXX.db
- connectionmusic = utils.KodiSQL("music")
- cursormusic = connectionmusic.cursor()
-
- count = 1
- total = len(itemList) + 1
- for item in itemList:
-
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync Delete ", progressTitle)
- count = count + 1
-
- # Sort by type for database deletion
- try: # Search video database
- self.logMsg("Check video database.", 1)
- cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,))
- mediatype = cursorvideo.fetchone()[0]
- video[item] = mediatype
- #video.append(itemtype)
- except:
- self.logMsg("Check music database.", 1)
- try: # Search music database
- cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,))
- cursormusic.fetchone()[0]
- music.append(item)
- except: self.logMsg("Item %s is not found in Kodi database." % item, 1)
-
- if len(video) > 0:
- connection = connectionvideo
- cursor = cursorvideo
- # Process video library
- count = 1
- total = len(video) + 1
- for item in video:
-
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync Delete ", progressTitle)
- count = count + 1
-
- type = video[item]
- self.logMsg("Doing LibraryChanged: Items Removed: Calling deleteItemFromKodiLibrary: %s" % item, 1)
-
- if "episode" in type:
- # Get the TV Show Id for reference later
- showId = ReadKodiDB().getShowIdByEmbyId(item, connection, cursor)
- self.logMsg("ShowId: %s" % showId, 1)
- WriteKodiVideoDB().deleteItemFromKodiLibrary(item, connection, cursor)
- # Verification
- if "episode" in type:
- showTotalCount = ReadKodiDB().getShowTotalCount(showId, connection, cursor)
- self.logMsg("ShowTotalCount: %s" % showTotalCount, 1)
- # If there are no episodes left
- if showTotalCount == 0 or showTotalCount == None:
- # Delete show
- embyId = ReadKodiDB().getEmbyIdByKodiId(showId, "tvshow", connection, cursor)
- self.logMsg("Message: Doing LibraryChanged: Deleting show: %s" % embyId, 1)
- WriteKodiVideoDB().deleteItemFromKodiLibrary(embyId, connection, cursor)
-
- self.dbCommit(connection)
- # Close connection
- cursorvideo.close()
-
- if len(music) > 0:
- connection = connectionmusic
- cursor = cursormusic
- #Process music library
- if utils.settings('enableMusicSync') == "true":
-
- for item in music:
- self.logMsg("Message : Doing LibraryChanged : Items Removed : Calling deleteItemFromKodiLibrary (musiclibrary): " + item, 0)
- WriteKodiMusicDB().deleteItemFromKodiLibrary(item, connection, cursor)
-
- self.dbCommit(connection)
- # Close connection
- cursormusic.close()
-
- if deleteEmbyItem:
- for item in itemList:
- url = "{server}/mediabrowser/Items/%s" % item
- self.logMsg('Deleting via URL: %s' % url)
- doUtils.downloadUrl(url, type = "DELETE")
- xbmc.executebuiltin("Container.Refresh")
-
- if(pDialog != None):
- pDialog.close()
-
- def setUserdata(self, listItems):
-
- dbSyncIndication = utils.settings("dbSyncIndication") == "true"
- musicenabled = utils.settings('enableMusicSync') == "true"
-
- #show the progress dialog
- pDialog = None
- if (dbSyncIndication and xbmc.Player().isPlaying() == False):
- pDialog = xbmcgui.DialogProgressBG()
- pDialog.create('Emby for Kodi', 'Incremental Sync')
- self.logMsg("Doing LibraryChanged : Show Progress setUserdata()", 0);
-
- # We need to sort between video and music database
- video = []
- music = []
- # Database connection to myVideosXX.db
- connectionvideo = utils.KodiSQL()
- cursorvideo = connectionvideo.cursor()
- # Database connection to myMusicXX.db
- connectionmusic = utils.KodiSQL('music')
- cursormusic = connectionmusic.cursor()
-
- count = 1
- total = len(listItems) + 1
- for userdata in listItems:
- # Sort between video and music
- itemId = userdata['ItemId']
-
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle)
- count = count + 1
-
- cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (itemId,))
- try: # Search video database
- self.logMsg("Check video database.", 2)
- mediatype = cursorvideo.fetchone()[0]
- video.append(userdata)
- except:
- if musicenabled:
- cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (itemId,))
- try: # Search music database
- self.logMsg("Check the music database.", 2)
- mediatype = cursormusic.fetchone()[0]
- music.append(userdata)
- except: self.logMsg("Item %s is not found in Kodi database." % itemId, 1)
- else:
- self.logMsg("Item %s is not found in Kodi database." % itemId, 1)
-
- if len(video) > 0:
- connection = connectionvideo
- cursor = cursorvideo
- # Process the userdata update for video library
- count = 1
- total = len(video) + 1
- for userdata in video:
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle)
- count = count + 1
- WriteKodiVideoDB().updateUserdata(userdata, connection, cursor)
-
- self.dbCommit(connection)
- #self.updateLibrary("video")
- # Close connection
- cursorvideo.close()
-
- if len(music) > 0:
- connection = connectionmusic
- cursor = cursormusic
- #Process music library
- count = 1
- total = len(video) + 1
- # Process the userdata update for music library
- if musicenabled:
- for userdata in music:
- if(pDialog != None):
- progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
- percentage = int(((float(count) / float(total)) * 100))
- pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle)
- count = count + 1
- WriteKodiMusicDB().updateUserdata(userdata, connection, cursor)
-
- self.dbCommit(connection)
- #xbmc.executebuiltin("UpdateLibrary(music)")
- # Close connection
- cursormusic.close()
-
- if(pDialog != None):
- pDialog.close()
-
- def remove_items(self, itemsRemoved):
- # websocket client
- if(len(itemsRemoved) > 0):
- self.logMsg("Doing LibraryChanged : Processing Deleted : " + str(itemsRemoved), 0)
- self.removeItems.extend(itemsRemoved)
-
- def update_items(self, itemsToUpdate):
- # websocket client
- if(len(itemsToUpdate) > 0):
- self.logMsg("Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0)
- self.updateItems.extend(itemsToUpdate)
-
- def user_data_update(self, userDataList):
- # websocket client
- if(len(userDataList) > 0):
- self.logMsg("Doing LibraryChanged : Processing User Data Changed : " + str(userDataList), 0)
- self.userdataItems.extend(userDataList)
-
- def dbCommit(self, connection):
- # Central commit, will verify if Kodi database
- kodidb_scan = utils.window('kodiScan') == "true"
-
- while kodidb_scan:
-
- self.logMsg("Kodi scan running. Waiting...", 1)
- kodidb_scan = utils.window('kodiScan') == "true"
-
- if self.KodiMonitor.waitForAbort(1):
- # Abort was requested while waiting. We should exit
- self.logMsg("Commit unsuccessful.", 1)
- break
- else:
- connection.commit()
- self.logMsg("Commit successful.", 1)
-
- def updateLibrary(self, type):
-
- self.logMsg("Updating %s library." % type, 1)
- utils.window('kodiScan', value="true")
- xbmc.executebuiltin('UpdateLibrary(%s)' % type)
-
- def ShouldStop(self):
-
- if(xbmc.abortRequested):
- return True
-
- if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"):
- return True
-
- return False
-
- def checkDBVersion(self, currVersion, minVersion):
- currMajor, currMinor, currPatch = currVersion.split(".")
- minMajor, minMinor, minPatch = minVersion.split(".")
- if currMajor > minMajor:
- return True
- elif currMajor == minMajor and currMinor > minMinor:
- return True
- elif currMajor == minMajor and currMinor == minMinor and currPatch >= minPatch:
- return True
- else:
- return False
-
- def run(self):
-
- try:
- self.run_internal()
- except Exception as e:
- xbmcgui.Dialog().ok("Emby for Kodi", "Library sync thread has crashed!", "You will need to restart Kodi.", "Please report this on the forum, we will need your log.")
- raise
-
- def run_internal(self):
-
- startupComplete = False
- kodiProfile = xbmc.translatePath("special://profile")
-
- self.logMsg("--- Starting Library Sync Thread ---", 0)
-
- while not self.KodiMonitor.abortRequested():
-
- # In the event the server goes offline after
- # the thread has already been started.
- while self.suspendClient == True:
- # The service.py will change self.suspendClient to False
- if self.KodiMonitor.waitForAbort(5):
- # Abort was requested while waiting. We should exit
- break
-
- # Check if the version of Emby for Kodi the DB was created with is recent enough - controled by Window property set at top of service _INIT_
-
- # START TEMPORARY CODE
- # Only get in here for a while, can be removed later
- if utils.settings("dbCreatedWithVersion")=="" and utils.settings("SyncInstallRunDone") == "true":
- self.logMsg("Unknown DB version", 0)
- return_value = xbmcgui.Dialog().yesno("DB Version", "Can't detect version of Emby for Kodi the DB was created with.\nWas it at least version " + utils.window('minDBVersion') + "?")
- if return_value == 0:
- utils.settings("dbCreatedWithVersion","0.0.0")
- self.logMsg("DB version out of date according to user", 0)
- else:
- utils.settings("dbCreatedWithVersion", utils.window('minDBVersion'))
- self.logMsg("DB version okay according to user", 0)
- # END TEMPORARY CODE
-
- if (utils.settings("SyncInstallRunDone") == "true" and self.checkDBVersion(utils.settings("dbCreatedWithVersion"), utils.window('minDBVersion'))==False and utils.window('minDBVersionCheck') != "true"):
- self.logMsg("DB version out of date according to check", 0)
- return_value = xbmcgui.Dialog().yesno("DB Version", "Detected the DB needs to be recreated for\nthis version of Emby for Kodi.\nProceed?")
- if return_value == 0:
- self.logMsg("DB version out of date !!! USER IGNORED !!!", 0)
- xbmcgui.Dialog().ok("Emby for Kodi","Emby for Kodi may not work\ncorrectly until the database is reset.\n")
- utils.window('minDBVersionCheck', value="true")
- else:
- utils.reset()
-
- # Library sync
- if not startupComplete:
-
- # Verify the database for videos
- videodb = utils.getKodiVideoDBPath()
- if not xbmcvfs.exists(videodb):
- # Database does not exists.
- self.logMsg("The current Kodi version is incompatible with the Emby for Kodi add-on. Please visit here, to see currently supported Kodi versions: https://github.com/MediaBrowser/Emby.Kodi/wiki", 0)
- xbmcgui.Dialog().ok("Emby Warning", "Cancelling the database syncing process. Current Kodi version: %s is unsupported. Please verify your logs for more info." % xbmc.getInfoLabel('System.BuildVersion'))
- break
-
- # Run full sync
- self.logMsg("DB Version: " + utils.settings("dbCreatedWithVersion"), 0)
- self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1)
- startTime = datetime.now()
- libSync = self.FullLibrarySync()
- elapsedTime = datetime.now() - startTime
- self.logMsg("Doing_Db_Sync: syncDatabase (Finished in: %s) %s" % (str(elapsedTime).split('.')[0], libSync), 1)
-
- if libSync:
- startupComplete = True
-
- # Set via Kodi Monitor event
- if utils.window('OnWakeSync') == "true" and utils.window('Server_online') == "true":
- utils.window("OnWakeSync", clear=True)
- if utils.window("SyncDatabaseRunning") != "true":
- self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Started)", 0)
- libSync = self.FullLibrarySync()
- self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Finished) " + str(libSync), 0)
-
-
- doSaveLastSync = False
-
- if len(self.updateItems) > 0 and utils.window('kodiScan') != "true":
- # Add or update items
- self.logMsg("Processing items: %s" % (str(self.updateItems)), 1)
- listItems = self.updateItems
- self.updateItems = []
- self.IncrementalSync(listItems)
- self.forceUpdate = True
- doSaveLastSync = True
-
- if len(self.userdataItems) > 0 and utils.window('kodiScan') != "true":
- # Process userdata changes only
- self.logMsg("Processing items: %s" % (str(self.userdataItems)), 1)
- listItems = self.userdataItems
- self.userdataItems = []
- self.setUserdata(listItems)
- self.forceUpdate = True
- doSaveLastSync = True
-
- if len(self.removeItems) > 0 and utils.window('kodiScan') != "true":
- # Remove item from Kodi library
- self.logMsg("Removing items: %s" % self.removeItems, 1)
- listItems = self.removeItems
- self.removeItems = []
- self.removefromDB(listItems)
- self.forceUpdate = True
- doSaveLastSync = True
-
- if doSaveLastSync == True:
- self.SaveLastSync()
-
- if self.forceUpdate and not self.updateItems and not self.userdataItems and not self.removeItems:
- # Force update Kodi library
- self.forceUpdate = False
- self.updateLibrary("video")
-
- if utils.window("kodiProfile_emby") != kodiProfile:
- # Profile change happened, terminate this thread
- self.logMsg("Kodi profile was: %s and changed to: %s. Terminating Library thread." % (kodiProfile, utils.window("kodiProfile_emby")), 1)
- break
-
- if self.KodiMonitor.waitForAbort(1):
- # Abort was requested while waiting. We should exit
- break
-
- self.logMsg("--- Library Sync Thread stopped ---", 0)
-
- def suspendClient(self):
- self.suspendClient = True
- self.logMsg("--- Library Sync Thread paused ---", 0)
-
- def resumeClient(self):
- self.suspendClient = False
- self.logMsg("--- Library Sync Thread resumed ---", 0)
\ No newline at end of file
diff --git a/resources/lib/PlayUtils.py b/resources/lib/PlayUtils.py
deleted file mode 100644
index e8b9be58..00000000
--- a/resources/lib/PlayUtils.py
+++ /dev/null
@@ -1,364 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#################################################################################################
-
-import xbmc
-import xbmcgui
-import xbmcvfs
-
-from ClientInformation import ClientInformation
-import Utils as utils
-
-#################################################################################################
-
-class PlayUtils():
-
- clientInfo = ClientInformation()
- addonName = clientInfo.getAddonName()
-
- def logMsg(self, msg, lvl=1):
-
- className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
-
- def getPlayUrl(self, server, id, result):
-
- if self.isDirectPlay(result,True):
- # Try direct play
- playurl = self.directPlay(result)
- if playurl:
- self.logMsg("File is direct playing.", 1)
- utils.window("%splaymethod" % playurl.encode('utf-8'), value="DirectPlay")
-
- elif self.isDirectStream(result):
- # Try direct stream
- playurl = self.directStream(result, server, id)
- if playurl:
- self.logMsg("File is direct streaming.", 1)
- utils.window("%splaymethod" % playurl, value="DirectStream")
-
- elif self.isTranscoding(result):
- # Try transcoding
- playurl = self.transcoding(result, server, id)
- if playurl:
- self.logMsg("File is transcoding.", 1)
- utils.window("%splaymethod" % playurl, value="Transcode")
-
- else: # Error
- utils.window("playurlFalse", value="true")
- return
-
- return playurl.encode('utf-8')
-
-
- def isDirectPlay(self, result, dialog = False):
- # Requirements for Direct play:
- # FileSystem, Accessible path
- if utils.settings('playFromStream') == "true":
- # User forcing to play via HTTP instead of SMB
- self.logMsg("Can't direct play: Play from HTTP is enabled.", 1)
- return False
-
- # Avoid H265 1080p
- if (utils.settings('transcodeH265') == "true" and
- result['MediaSources'][0]['Name'].startswith("1080P/H265")):
- self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
- return False
-
- canDirectPlay = result['MediaSources'][0]['SupportsDirectPlay']
- # Make sure it's supported by server
- if not canDirectPlay:
- self.logMsg("Can't direct play: Server does not allow or support it.", 1)
- return False
-
- location = result['LocationType']
- # File needs to be "FileSystem"
- if 'FileSystem' in location:
- # Verify if path is accessible
- if self.fileExists(result):
- return True
- else:
- self.logMsg("Unable to direct play. Verify the following path is accessible by the device: %s. You might also need to add SMB credentials in the add-on settings." % result['MediaSources'][0]['Path'], 1)
- if dialog:
-
- failCount = int(utils.settings('directSteamFailedCount'))
- self.logMsg("Direct Play failCount: %s." % failCount, 1)
-
- if failCount < 2:
- # Let user know that direct play failed
- utils.settings('directSteamFailedCount', value=str(failCount + 1))
- xbmcgui.Dialog().notification("Emby server", "Unable to direct play. Verify your log for more information.", icon="special://home/addons/plugin.video.emby/icon.png", sound=False)
- elif utils.settings('playFromStream') != "true":
- # Permanently set direct stream as true
- utils.settings('playFromStream', value="true")
- xbmcgui.Dialog().notification("Emby server", "Enabled play from HTTP in add-on settings.", icon="special://home/addons/plugin.video.emby/icon.png", sound=False)
-
- return False
-
- def directPlay(self, result):
-
- try:
- playurl = result['MediaSources'][0]['Path']
- except KeyError:
- playurl = result['Path']
-
- if 'VideoType' in result:
- # Specific format modification
- if 'Dvd' in result['VideoType']:
- playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
- elif 'BluRay' in result['VideoType']:
- playurl = "%s/BDMV/index.bdmv" % playurl
-
- # Network - SMB protocol
- if "\\\\" in playurl:
- smbuser = utils.settings('smbusername')
- smbpass = utils.settings('smbpassword')
- # Network share
- if smbuser:
- playurl = playurl.replace("\\\\", "smb://%s:%s@" % (smbuser, smbpass))
- else:
- playurl = playurl.replace("\\\\", "smb://")
- playurl = playurl.replace("\\", "/")
-
- if "apple.com" in playurl:
- USER_AGENT = "QuickTime/7.7.4"
- playurl += "?|User-Agent=%s" % USER_AGENT
-
- return playurl
-
-
- def isDirectStream(self, result):
- # Requirements for Direct stream:
- # FileSystem or Remote, BitRate, supported encoding
-
- # Avoid H265 1080p
- if (utils.settings('transcodeH265') == "true" and
- result['MediaSources'][0]['Name'].startswith("1080P/H265")):
- self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
- return False
-
- canDirectStream = result['MediaSources'][0]['SupportsDirectStream']
- # Make sure it's supported by server
- if not canDirectStream:
- return False
-
- location = result['LocationType']
- # File can be FileSystem or Remote, not Virtual
- if 'Virtual' in location:
- self.logMsg("File location is virtual. Can't proceed.", 1)
- return False
-
- # Verify BitRate
- if not self.isNetworkQualitySufficient(result):
- self.logMsg("The network speed is insufficient to playback the file.", 1)
- return False
-
- return True
-
- def directStream(self, result, server, id, type = "Video"):
-
- if result['Path'].endswith('.strm'):
- # Allow strm loading when direct streaming
- playurl = self.directPlay(result)
- return playurl
-
- if "ThemeVideo" in type:
- playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
-
- elif "Video" in type:
- playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
-
- elif "Audio" in type:
- playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id)
-
- return playurl
-
-
- def isTranscoding(self, result):
- # Last resort, no requirements
- # BitRate
- canTranscode = result['MediaSources'][0]['SupportsTranscoding']
- # Make sure it's supported by server
- if not canTranscode:
- return False
-
- location = result['LocationType']
- # File can be FileSystem or Remote, not Virtual
- if 'Virtual' in location:
- return False
-
- return True
-
- def transcoding(self, result, server, id):
-
- if result['Path'].endswith('.strm'):
- # Allow strm loading when transcoding
- playurl = self.directPlay(result)
- return playurl
-
- # Play transcoding
- deviceId = self.clientInfo.getMachineId()
- playurl = "%s/mediabrowser/Videos/%s/master.m3u8?mediaSourceId=%s" % (server, id, id)
- playurl = "%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s" % (playurl, deviceId, self.getVideoBitRate()*1000)
-
- playurl = self.audioSubsPref(playurl, result.get('MediaSources'))
- self.logMsg("Playurl: %s" % playurl, 1)
-
- return playurl
-
-
- def isNetworkQualitySufficient(self, result):
- # Works out if the network quality can play directly or if transcoding is needed
- settingsVideoBitRate = self.getVideoBitRate()
- settingsVideoBitRate = settingsVideoBitRate * 1000
-
- try:
- mediaSources = result['MediaSources']
- sourceBitRate = int(mediaSources[0]['Bitrate'])
- except KeyError:
- self.logMsg("Bitrate value is missing.", 1)
- else:
- self.logMsg("The video quality selected is: %s, the video bitrate required to direct stream is: %s." % (settingsVideoBitRate, sourceBitRate), 1)
- if settingsVideoBitRate < sourceBitRate:
- return False
-
- return True
-
- def getVideoBitRate(self):
- # get the addon video quality
- videoQuality = utils.settings('videoBitRate')
- bitrate = {
-
- '0': 664,
- '1': 996,
- '2': 1320,
- '3': 2000,
- '4': 3200,
- '5': 4700,
- '6': 6200,
- '7': 7700,
- '8': 9200,
- '9': 10700,
- '10': 12200,
- '11': 13700,
- '12': 15200,
- '13': 16700,
- '14': 18200,
- '15': 20000,
- '16': 40000,
- '17': 100000,
- '18': 1000000
- }
-
- # max bit rate supported by server (max signed 32bit integer)
- return bitrate.get(videoQuality, 2147483)
-
- def fileExists(self, result):
-
- if 'Path' not in result:
- # File has no path in server
- return False
-
- # Convert Emby path to a path we can verify
- path = self.directPlay(result)
-
- try:
- pathexists = xbmcvfs.exists(path)
- except:
- pathexists = False
-
- # Verify the device has access to the direct path
- if pathexists:
- # Local or Network path
- self.logMsg("Path exists.", 2)
- return True
- elif ":" not in path:
- # Give benefit of the doubt for nfs.
- self.logMsg("Can't verify path (assumed NFS). Still try direct play.", 2)
- return True
- else:
- self.logMsg("Path is detected as follow: %s. Try direct streaming." % path, 2)
- return False
-
- def audioSubsPref(self, url, mediaSources):
- # For transcoding only
- # Present the list of audio to select from
- audioStreamsList = {}
- audioStreams = []
- audioStreamsChannelsList = {}
- subtitleStreamsList = {}
- subtitleStreams = ['No subtitles']
- selectAudioIndex = ""
- selectSubsIndex = ""
- playurlprefs = "%s" % url
-
- mediaStream = mediaSources[0].get('MediaStreams')
- for stream in mediaStream:
- # Since Emby returns all possible tracks together, have to sort them.
- index = stream['Index']
- type = stream['Type']
-
- if 'Audio' in type:
- codec = stream['Codec']
- channelLayout = stream['ChannelLayout']
-
- try:
- track = "%s - %s - %s %s" % (index, stream['Language'], codec, channelLayout)
- except:
- track = "%s - %s %s" % (index, codec, channelLayout)
-
- audioStreamsChannelsList[index] = stream['Channels']
- audioStreamsList[track] = index
- audioStreams.append(track)
-
- elif 'Subtitle' in type:
- try:
- track = "%s - %s" % (index, stream['Language'])
- except:
- track = "%s - %s" % (index, stream['Codec'])
-
- default = stream['IsDefault']
- forced = stream['IsForced']
- if default:
- track = "%s - Default" % track
- if forced:
- track = "%s - Forced" % track
-
- subtitleStreamsList[track] = index
- subtitleStreams.append(track)
-
-
- if len(audioStreams) > 1:
- resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
- if resp > -1:
- # User selected audio
- selected = audioStreams[resp]
- selectAudioIndex = audioStreamsList[selected]
- playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
- else: # User backed out of selection
- playurlprefs += "&AudioStreamIndex=%s" % mediaSources[0]['DefaultAudioStreamIndex']
- else: # There's only one audiotrack.
- selectAudioIndex = audioStreamsList[audioStreams[0]]
- playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
-
- if len(subtitleStreams) > 1:
- resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
- if resp == 0:
- # User selected no subtitles
- pass
- elif resp > -1:
- # User selected subtitles
- selected = subtitleStreams[resp]
- selectSubsIndex = subtitleStreamsList[selected]
- playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
- else: # User backed out of selection
- playurlprefs += "&SubtitleStreamIndex=%s" % mediaSources[0].get('DefaultSubtitleStreamIndex', "")
-
- # Get number of channels for selected audio track
- audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
- if audioChannels > 2:
- playurlprefs += "&AudioBitrate=384000"
- else:
- playurlprefs += "&AudioBitrate=192000"
-
- return playurlprefs
\ No newline at end of file
diff --git a/resources/lib/PlaybackUtils.py b/resources/lib/PlaybackUtils.py
deleted file mode 100644
index 1172e52b..00000000
--- a/resources/lib/PlaybackUtils.py
+++ /dev/null
@@ -1,462 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#################################################################################################
-
-import datetime
-import json as json
-import sys
-
-import xbmc
-import xbmcaddon
-import xbmcplugin
-import xbmcgui
-
-from API import API
-from DownloadUtils import DownloadUtils
-from PlayUtils import PlayUtils
-from ClientInformation import ClientInformation
-import Utils as utils
-
-#################################################################################################
-
-class PlaybackUtils():
-
- clientInfo = ClientInformation()
- doUtils = DownloadUtils()
- api = API()
-
- addon = xbmcaddon.Addon()
- language = addon.getLocalizedString
- addonName = clientInfo.getAddonName()
-
- def logMsg(self, msg, lvl=1):
-
- className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
-
- def PLAY(self, result, setup = "service"):
-
- self.logMsg("PLAY Called", 1)
-
- api = self.api
- doUtils = self.doUtils
- username = utils.window('currUser')
- server = utils.window('server%s' % username)
-
- id = result['Id']
- userdata = result['UserData']
- # Get the playurl - direct play, direct stream or transcoding
- playurl = PlayUtils().getPlayUrl(server, id, result)
- listItem = xbmcgui.ListItem()
-
- if utils.window('playurlFalse') == "true":
- # Playurl failed - set in PlayUtils.py
- utils.window('playurlFalse', clear=True)
- self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
- return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
-
-
- ############### -- SETUP MAIN ITEM ################
-
- # Set listitem and properties for main item
- self.logMsg("Returned playurl: %s" % playurl, 1)
- listItem.setPath(playurl)
- self.setProperties(playurl, result, listItem)
-
- mainArt = API().getArtwork(result, "Primary")
- listItem.setThumbnailImage(mainArt)
- listItem.setIconImage(mainArt)
-
-
- ############### ORGANIZE CURRENT PLAYLIST ################
-
- homeScreen = xbmc.getCondVisibility('Window.IsActive(home)')
- playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
- startPos = max(playlist.getposition(), 0) # Can return -1
- sizePlaylist = playlist.size()
-
- propertiesPlayback = utils.window('propertiesPlayback') == "true"
- introsPlaylist = False
- dummyPlaylist = False
- currentPosition = startPos
-
- self.logMsg("Playlist start position: %s" % startPos, 2)
- self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
- self.logMsg("Playlist size: %s" % sizePlaylist, 2)
-
-
- ############### RESUME POINT ################
-
- # Resume point for widget only
- timeInfo = api.getTimeInfo(result)
- jumpBackSec = int(utils.settings('resumeJumpBack'))
- seekTime = round(float(timeInfo.get('ResumeTime')), 6)
- if seekTime > jumpBackSec:
- # To avoid negative bookmark
- seekTime = seekTime - jumpBackSec
-
- # Show the additional resume dialog if launched from a widget
- if homeScreen and seekTime:
- # Dialog presentation
- displayTime = str(datetime.timedelta(seconds=(int(seekTime))))
- display_list = ["%s %s" % (self.language(30106), displayTime), self.language(30107)]
- resume_result = xbmcgui.Dialog().select(self.language(30105), display_list)
-
- if resume_result == 0:
- # User selected to resume, append resume point to listitem
- listItem.setProperty('StartOffset', str(seekTime))
-
- elif resume_result > 0:
- # User selected to start from beginning
- seekTime = 0
-
- else: # User cancelled the dialog
- self.logMsg("User cancelled resume dialog.", 1)
- return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
-
- # We need to ensure we add the intro and additional parts only once.
- # Otherwise we get a loop.
- if not propertiesPlayback:
-
- utils.window('propertiesPlayback', value="true")
- self.logMsg("Setting up properties in playlist.")
-
- ############### -- CHECK FOR INTROS ################
-
- if utils.settings('disableCinema') == "false" and not seekTime:
- # if we have any play them when the movie/show is not being resumed
- url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
- intros = doUtils.downloadUrl(url)
-
- if intros['TotalRecordCount'] != 0:
- getTrailers = True
-
- if utils.settings('askCinema') == "true":
- resp = xbmcgui.Dialog().yesno("Emby Cinema Mode", "Play trailers?")
- if not resp:
- # User selected to not play trailers
- getTrailers = False
- self.logMsg("Skip trailers.", 1)
-
- if getTrailers:
- for intro in intros['Items']:
- # The server randomly returns intros, process them.
- introId = intro['Id']
-
- introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
- introListItem = xbmcgui.ListItem()
- self.logMsg("Adding Intro: %s" % introPlayurl, 1)
-
- # Set listitem and properties for intros
- self.setProperties(introPlayurl, intro, introListItem)
- self.setListItemProps(server, introId, introListItem, intro)
-
- playlist.add(introPlayurl, introListItem, index=currentPosition)
- introsPlaylist = True
- currentPosition += 1
-
-
- ############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ###############
-
- if homeScreen and not sizePlaylist:
- # Extend our current playlist with the actual item to play only if there's no playlist first
- self.logMsg("Adding main item to playlist.", 1)
- self.setListItemProps(server, id, listItem, result)
- playlist.add(playurl, listItem, index=currentPosition)
-
- # Ensure that additional parts are played after the main item
- currentPosition += 1
-
-
- ############### -- CHECK FOR ADDITIONAL PARTS ################
-
- if result.get('PartCount'):
- # Only add to the playlist after intros have played
- partcount = result['PartCount']
- url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
- parts = doUtils.downloadUrl(url)
-
- for part in parts['Items']:
-
- partId = part['Id']
- additionalPlayurl = PlayUtils().getPlayUrl(server, partId, part)
- additionalListItem = xbmcgui.ListItem()
- self.logMsg("Adding additional part: %s" % partcount, 1)
-
- # Set listitem and properties for each additional parts
- self.setProperties(additionalPlayurl, part, additionalListItem)
- self.setListItemProps(server, partId, additionalListItem, part)
-
- playlist.add(additionalPlayurl, additionalListItem, index=currentPosition)
- currentPosition += 1
-
-
- ############### ADD DUMMY TO PLAYLIST #################
-
- if (not homeScreen and introsPlaylist) or (homeScreen and sizePlaylist > 0):
- # Playlist will fail on the current position. Adding dummy url
- dummyPlaylist = True
- self.logMsg("Adding dummy url to counter the setResolvedUrl error.", 2)
- playlist.add(playurl, index=startPos)
- currentPosition += 1
-
-
- # We just skipped adding properties. Reset flag for next time.
- elif propertiesPlayback:
- self.logMsg("Resetting properties playback flag.", 2)
- utils.window('propertiesPlayback', clear=True)
-
-
- self.verifyPlaylist()
-
- ############### PLAYBACK ################
-
- if not homeScreen and not introsPlaylist:
-
- self.logMsg("Processed as a single item.", 1)
- xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
-
- elif dummyPlaylist:
- # Added a dummy file to the playlist because the first item is going to fail automatically.
- self.logMsg("Processed as a playlist. First item is skipped.", 1)
- xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
-
- else:
- self.logMsg("Play as a regular item.", 1)
- xbmc.Player().play(playlist, startpos=startPos)
-
-
- def verifyPlaylist(self):
-
- playlistitems = '{"jsonrpc": "2.0", "method": "Playlist.GetItems", "params": { "playlistid": 1 }, "id": 1}'
- items = xbmc.executeJSONRPC(playlistitems)
- self.logMsg(items, 2)
-
- def removeFromPlaylist(self, pos):
-
- playlistremove = '{"jsonrpc": "2.0", "method": "Playlist.Remove", "params": { "playlistid": 1, "position": %d }, "id": 1}' % pos
- result = xbmc.executeJSONRPC(playlistremove)
- self.logMsg(result, 1)
-
-
- def externalSubs(self, id, playurl, mediaSources):
-
- username = utils.window('currUser')
- server = utils.window('server%s' % username)
- externalsubs = []
- mapping = {}
-
- mediaStream = mediaSources[0].get('MediaStreams')
- kodiindex = 0
- for stream in mediaStream:
-
- index = stream['Index']
- # Since Emby returns all possible tracks together, have to pull only external subtitles.
- # IsTextSubtitleStream if true, is available to download from emby.
- if "Subtitle" in stream['Type'] and stream['IsExternal'] and stream['IsTextSubtitleStream']:
-
- playmethod = utils.window("%splaymethod" % playurl)
-
- if "DirectPlay" in playmethod:
- # Direct play, get direct path
- url = PlayUtils().directPlay(stream)
- elif "DirectStream" in playmethod: # Direct stream
- url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" % (server, id, id, index)
-
- # map external subtitles for mapping
- mapping[kodiindex] = index
- externalsubs.append(url)
- kodiindex += 1
-
- mapping = json.dumps(mapping)
- utils.window('%sIndexMapping' % playurl, value=mapping)
-
- return externalsubs
-
-
- def setProperties(self, playurl, result, listItem):
- # Set runtimeticks, type, refresh_id and item_id
- id = result.get('Id')
- type = result.get('Type', "")
-
- utils.window("%sruntimeticks" % playurl, value=str(result.get('RunTimeTicks')))
- utils.window("%stype" % playurl, value=type)
- utils.window("%sitem_id" % playurl, value=id)
-
- if type == "Episode":
- utils.window("%srefresh_id" % playurl, value=result.get('SeriesId'))
- else:
- utils.window("%srefresh_id" % playurl, value=id)
-
- if utils.window("%splaymethod" % playurl) != "Transcode":
- # Only for direct play and direct stream
- # Append external subtitles to stream
- subtitleList = self.externalSubs(id, playurl, result['MediaSources'])
- listItem.setSubtitles(subtitleList)
-
- def setArt(self, list, name, path):
-
- if name in ("thumb", "fanart_image", "small_poster", "tiny_poster", "medium_landscape", "medium_poster", "small_fanartimage", "medium_fanartimage", "fanart_noindicators"):
- list.setProperty(name, path)
- else:
- list.setArt({name:path})
-
- return list
-
- def setListItemProps(self, server, id, listItem, result):
- # Set up item and item info
- api = self.api
-
- type = result.get('Type')
- people = api.getPeople(result)
- studios = api.getStudios(result)
-
- metadata = {
-
- 'title': result.get('Name', "Missing name"),
- 'year': result.get('ProductionYear'),
- 'plot': api.getOverview(result),
- 'director': people.get('Director'),
- 'writer': people.get('Writer'),
- 'mpaa': api.getMpaa(result),
- 'genre': api.getGenre(result),
- 'studio': " / ".join(studios),
- 'aired': api.getPremiereDate(result),
- 'rating': result.get('CommunityRating'),
- 'votes': result.get('VoteCount')
- }
-
- if "Episode" in type:
- # Only for tv shows
- thumbId = result.get('SeriesId')
- season = result.get('ParentIndexNumber', -1)
- episode = result.get('IndexNumber', -1)
- show = result.get('SeriesName', "")
-
- metadata['TVShowTitle'] = show
- metadata['season'] = season
- metadata['episode'] = episode
-
- listItem.setProperty('IsPlayable', 'true')
- listItem.setProperty('IsFolder', 'false')
- listItem.setLabel(metadata['title'])
- listItem.setInfo('video', infoLabels=metadata)
-
- # Set artwork for listitem
- self.setArt(listItem,'poster', API().getArtwork(result, "Primary"))
- self.setArt(listItem,'tvshow.poster', API().getArtwork(result, "SeriesPrimary"))
- self.setArt(listItem,'clearart', API().getArtwork(result, "Art"))
- self.setArt(listItem,'tvshow.clearart', API().getArtwork(result, "Art"))
- self.setArt(listItem,'clearlogo', API().getArtwork(result, "Logo"))
- self.setArt(listItem,'tvshow.clearlogo', API().getArtwork(result, "Logo"))
- self.setArt(listItem,'discart', API().getArtwork(result, "Disc"))
- self.setArt(listItem,'fanart_image', API().getArtwork(result, "Backdrop"))
- self.setArt(listItem,'landscape', API().getArtwork(result, "Thumb"))
-
- def seekToPosition(self, seekTo):
- # Set a loop to wait for positive confirmation of playback
- count = 0
- while not xbmc.Player().isPlaying():
- count += 1
- if count >= 10:
- return
- else:
- xbmc.sleep(500)
-
- # Jump to seek position
- count = 0
- while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
- count += 1
- xbmc.Player().seekTime(seekTo)
- xbmc.sleep(100)
-
- def PLAYAllItems(self, items, startPositionTicks):
-
- self.logMsg("== ENTER: PLAYAllItems ==")
- self.logMsg("Items: %s" % items)
-
- doUtils = self.doUtils
-
- playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
- playlist.clear()
- started = False
-
- for itemId in items:
- self.logMsg("Adding Item to playlist: %s" % itemId, 1)
- url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemId
- result = doUtils.downloadUrl(url)
-
- addition = self.addPlaylistItem(playlist, result)
- if not started and addition:
- started = True
- self.logMsg("Starting Playback Pre", 1)
- xbmc.Player().play(playlist)
-
- if not started:
- self.logMsg("Starting Playback Post", 1)
- xbmc.Player().play(playlist)
-
- # Seek to position
- if startPositionTicks:
- seekTime = startPositionTicks / 10000000.0
- self.seekToPosition(seekTime)
-
- def AddToPlaylist(self, itemIds):
-
- self.logMsg("== ENTER: PLAYAllItems ==")
-
- doUtils = self.doUtils
- playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
-
- for itemId in itemIds:
- self.logMsg("Adding Item to Playlist: %s" % itemId)
- url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemId
- result = doUtils.downloadUrl(url)
-
- self.addPlaylistItem(playlist, result)
-
- return playlist
-
- def addPlaylistItem(self, playlist, item):
-
- id = item['Id']
- username = utils.window('currUser')
- server = utils.window('server%s' % username)
-
- playurl = PlayUtils().getPlayUrl(server, id, item)
-
- if utils.window('playurlFalse') == "true":
- # Playurl failed - set in PlayUtils.py
- utils.window('playurlFalse', clear=True)
- self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
- return
-
- self.logMsg("Playurl: %s" % playurl)
-
- thumb = API().getArtwork(item, "Primary")
- listItem = xbmcgui.ListItem(path=playurl, iconImage=thumb, thumbnailImage=thumb)
- self.setListItemProps(server, id, listItem, item)
- self.setProperties(playurl, item, listItem)
-
- playlist.add(playurl, listItem)
-
- # Not currently being used
- '''def PLAYAllEpisodes(self, items):
- WINDOW = xbmcgui.Window(10000)
-
- username = WINDOW.getProperty('currUser')
- userid = WINDOW.getProperty('userId%s' % username)
- server = WINDOW.getProperty('server%s' % username)
-
- playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
- playlist.clear()
-
- for item in items:
-
- item_url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % item["Id"]
- jsonData = self.downloadUtils.downloadUrl(item_url)
-
- item_data = jsonData
- self.addPlaylistItem(playlist, item_data, server, userid)
-
- xbmc.Player().play(playlist)'''
\ No newline at end of file
diff --git a/resources/lib/Player.py b/resources/lib/Player.py
deleted file mode 100644
index 0b760a88..00000000
--- a/resources/lib/Player.py
+++ /dev/null
@@ -1,440 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#################################################################################################
-
-import json as json
-
-import xbmc
-import xbmcgui
-
-from DownloadUtils import DownloadUtils
-from WebSocketClient import WebSocketThread
-from ClientInformation import ClientInformation
-from LibrarySync import LibrarySync
-import Utils as utils
-
-#################################################################################################
-
-class Player( xbmc.Player ):
-
- # Borg - multiple instances, shared state
- _shared_state = {}
-
- xbmcplayer = xbmc.Player()
- doUtils = DownloadUtils()
- clientInfo = ClientInformation()
- ws = WebSocketThread()
- librarySync = LibrarySync()
-
- addonName = clientInfo.getAddonName()
-
- played_information = {}
- playStats = {}
- currentFile = None
-
- def __init__(self, *args):
-
- self.__dict__ = self._shared_state
- self.logMsg("Starting playback monitor.", 2)
-
- def logMsg(self, msg, lvl=1):
-
- self.className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
-
- def GetPlayStats(self):
- return self.playStats
-
- def onPlayBackStarted( self ):
- # Will be called when xbmc starts playing a file
- xbmcplayer = self.xbmcplayer
- self.stopAll()
-
- # Get current file
- try:
- currentFile = xbmcplayer.getPlayingFile()
- xbmc.sleep(300)
- except:
- currentFile = ""
- count = 0
- while not currentFile:
- xbmc.sleep(100)
- try:
- currentFile = xbmcplayer.getPlayingFile()
- except: pass
-
- if count == 5: # try 5 times
- self.logMsg("Cancelling playback report...", 1)
- break
- else: count += 1
-
-
- if currentFile:
-
- self.currentFile = currentFile
-
- # We may need to wait for info to be set in kodi monitor
- itemId = utils.window("%sitem_id" % currentFile)
- tryCount = 0
- while not itemId:
-
- xbmc.sleep(200)
- itemId = utils.window("%sitem_id" % currentFile)
- if tryCount == 20: # try 20 times or about 10 seconds
- self.logMsg("Could not find itemId, cancelling playback report...", 1)
- break
- else: tryCount += 1
-
- else:
- self.logMsg("ONPLAYBACK_STARTED: %s ITEMID: %s" % (currentFile, itemId), 0)
-
- # Only proceed if an itemId was found.
- runtime = utils.window("%sruntimeticks" % currentFile)
- refresh_id = utils.window("%srefresh_id" % currentFile)
- playMethod = utils.window("%splaymethod" % currentFile)
- itemType = utils.window("%stype" % currentFile)
- seekTime = xbmcplayer.getTime()
-
-
- # Get playback volume
- volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
- result = xbmc.executeJSONRPC(volume_query)
- result = json.loads(result)
- result = result.get('result')
-
- volume = result.get('volume')
- muted = result.get('muted')
-
- # Postdata structure to send to Emby server
- url = "{server}/mediabrowser/Sessions/Playing"
- postdata = {
-
- 'QueueableMediaTypes': "Video",
- 'CanSeek': True,
- 'ItemId': itemId,
- 'MediaSourceId': itemId,
- 'PlayMethod': playMethod,
- 'VolumeLevel': volume,
- 'PositionTicks': int(seekTime * 10000000),
- 'IsMuted': muted
- }
-
- # Get the current audio track and subtitles
- if playMethod == "Transcode":
- # property set in PlayUtils.py
- postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
- postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex" % currentFile)
-
- else:
- # Get the current kodi audio and subtitles and convert to Emby equivalent
- track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
- result = xbmc.executeJSONRPC(track_query)
- result = json.loads(result)
- result = result.get('result')
-
- try: # Audio tracks
- indexAudio = result['currentaudiostream']['index']
- except (KeyError, TypeError):
- indexAudio = 0
-
- try: # Subtitles tracks
- indexSubs = result['currentsubtitle']['index']
- except (KeyError, TypeError):
- indexSubs = 0
-
- try: # If subtitles are enabled
- subsEnabled = result['subtitleenabled']
- except (KeyError, TypeError):
- subsEnabled = ""
-
- # Postdata for the audio
- postdata['AudioStreamIndex'] = indexAudio + 1
-
- # Postdata for the subtitles
- if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
-
- # Number of audiotracks to help get Emby Index
- audioTracks = len(xbmc.Player().getAvailableAudioStreams())
- mapping = utils.window("%sIndexMapping" % currentFile)
-
- if mapping: # Set in PlaybackUtils.py
-
- self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
- externalIndex = json.loads(mapping)
-
- if externalIndex.get(str(indexSubs)):
- # If the current subtitle is in the mapping
- postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
- else:
- # Internal subtitle currently selected
- postdata['SubtitleStreamIndex'] = indexSubs - len(externalIndex) + audioTracks + 1
-
- else: # Direct paths enabled scenario or no external subtitles set
- postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
- else:
- postdata['SubtitleStreamIndex'] = ""
-
-
- # Post playback to server
- self.logMsg("Sending POST play started: %s." % postdata, 2)
- self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
-
- # Ensure we do have a runtime
- try:
- runtime = int(runtime)
- except ValueError:
- runtime = xbmcplayer.getTotalTime()
- self.logMsg("Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1)
-
- # Save data map for updates and position calls
- data = {
-
- 'runtime': runtime,
- 'item_id': itemId,
- 'refresh_id': refresh_id,
- 'currentfile': currentFile,
- 'AudioStreamIndex': postdata['AudioStreamIndex'],
- 'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
- 'playmethod': playMethod,
- 'Type': itemType,
- 'currentPosition': int(seekTime)
- }
-
- self.played_information[currentFile] = data
- self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
-
- # log some playback stats
- '''if(itemType != None):
- if(self.playStats.get(itemType) != None):
- count = self.playStats.get(itemType) + 1
- self.playStats[itemType] = count
- else:
- self.playStats[itemType] = 1
-
- if(playMethod != None):
- if(self.playStats.get(playMethod) != None):
- count = self.playStats.get(playMethod) + 1
- self.playStats[playMethod] = count
- else:
- self.playStats[playMethod] = 1'''
-
- def reportPlayback(self):
-
- self.logMsg("reportPlayback Called", 2)
- xbmcplayer = self.xbmcplayer
-
- # Get current file
- currentFile = self.currentFile
- data = self.played_information.get(currentFile)
-
- # only report playback if emby has initiated the playback (item_id has value)
- if data:
- # Get playback information
- itemId = data['item_id']
- audioindex = data['AudioStreamIndex']
- subtitleindex = data['SubtitleStreamIndex']
- playTime = data['currentPosition']
- playMethod = data['playmethod']
- paused = data.get('paused', False)
-
-
- # Get playback volume
- volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
- result = xbmc.executeJSONRPC(volume_query)
- result = json.loads(result)
- result = result.get('result')
-
- volume = result.get('volume')
- muted = result.get('muted')
-
-
- # Postdata for the websocketclient report
- postdata = {
-
- 'QueueableMediaTypes': "Video",
- 'CanSeek': True,
- 'ItemId': itemId,
- 'MediaSourceId': itemId,
- 'PlayMethod': playMethod,
- 'PositionTicks': int(playTime * 10000000),
- 'IsPaused': paused,
- 'VolumeLevel': volume,
- 'IsMuted': muted
- }
-
- if playMethod == "Transcode":
- # Track can't be changed, keep reporting the same index
- postdata['AudioStreamIndex'] = audioindex
- postdata['AudioStreamIndex'] = subtitleindex
-
- else:
- # Get current audio and subtitles track
- track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
- result = xbmc.executeJSONRPC(track_query)
- result = json.loads(result)
- result = result.get('result')
-
- try: # Audio tracks
- indexAudio = result['currentaudiostream']['index']
- except (KeyError, TypeError):
- indexAudio = 0
-
- try: # Subtitles tracks
- indexSubs = result['currentsubtitle']['index']
- except (KeyError, TypeError):
- indexSubs = 0
-
- try: # If subtitles are enabled
- subsEnabled = result['subtitleenabled']
- except (KeyError, TypeError):
- subsEnabled = ""
-
- # Postdata for the audio
- data['AudioStreamIndex'], postdata['AudioStreamIndex'] = [indexAudio + 1] * 2
-
- # Postdata for the subtitles
- if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
-
- # Number of audiotracks to help get Emby Index
- audioTracks = len(xbmc.Player().getAvailableAudioStreams())
- mapping = utils.window("%sIndexMapping" % currentFile)
-
- if mapping: # Set in PlaybackUtils.py
-
- self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
- externalIndex = json.loads(mapping)
-
- if externalIndex.get(str(indexSubs)):
- # If the current subtitle is in the mapping
- data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [externalIndex[str(indexSubs)]] * 2
- else:
- # Internal subtitle currently selected
- data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
-
- else: # Direct paths enabled scenario or no external subtitles set
- data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs + audioTracks + 1] * 2
- else:
- data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [""] * 2
-
- # Report progress via websocketclient
- postdata = json.dumps(postdata)
- self.logMsg("Report: %s" % postdata, 2)
- self.ws.sendProgressUpdate(postdata)
-
- def onPlayBackPaused( self ):
-
- currentFile = self.currentFile
- self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
-
- if self.played_information.get(currentFile):
- self.played_information[currentFile]['paused'] = True
-
- self.reportPlayback()
-
- def onPlayBackResumed( self ):
-
- currentFile = self.currentFile
- self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
-
- if self.played_information.get(currentFile):
- self.played_information[currentFile]['paused'] = False
-
- self.reportPlayback()
-
- def onPlayBackSeek( self, time, seekOffset ):
- # Make position when seeking a bit more accurate
- currentFile = self.currentFile
- self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
-
- if self.played_information.get(currentFile):
- position = self.xbmcplayer.getTime()
- self.played_information[currentFile]['currentPosition'] = position
-
- self.reportPlayback()
-
- def onPlayBackStopped( self ):
- # Will be called when user stops xbmc playing a file
- self.logMsg("ONPLAYBACK_STOPPED", 2)
- self.stopAll()
-
- def onPlayBackEnded( self ):
- # Will be called when xbmc stops playing a file
- self.logMsg("ONPLAYBACK_ENDED", 2)
- self.stopAll()
-
- def stopAll(self):
-
- if not self.played_information:
- return
-
- self.logMsg("Played_information: %s" % self.played_information, 1)
- # Process each items
- for item in self.played_information:
-
- data = self.played_information.get(item)
- if data:
-
- self.logMsg("Item path: %s" % item, 2)
- self.logMsg("Item data: %s" % data, 2)
-
- runtime = data['runtime']
- currentPosition = data['currentPosition']
- itemId = data['item_id']
- refresh_id = data['refresh_id']
- currentFile = data['currentfile']
- type = data['Type']
- playMethod = data['playmethod']
-
- if currentPosition and runtime:
- percentComplete = (currentPosition * 10000000) / int(runtime)
- markPlayedAt = float(utils.settings('markPlayed')) / 100
-
- self.logMsg("Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt), 1)
- # Prevent manually mark as watched in Kodi monitor > WriteKodiVideoDB().UpdatePlaycountFromKodi()
- utils.window('SkipWatched%s' % itemId, "true")
-
- self.stopPlayback(data)
- offerDelete = utils.settings('offerDelete') == "true"
- offerTypeDelete = False
-
- if type == "Episode" and utils.settings('offerDeleteTV') == "true":
- offerTypeDelete = True
-
- elif type == "Movie" and utils.settings('offerDeleteMovies') == "true":
- offerTypeDelete = True
-
- if percentComplete >= markPlayedAt and offerDelete and offerTypeDelete:
- # Make the bigger setting be able to disable option easily.
- self.logMsg("Offering deletion for: %s." % itemId, 1)
- return_value = xbmcgui.Dialog().yesno("Offer Delete", "Delete %s" % currentFile.split("/")[-1], "on Emby Server?")
- if return_value:
- # Delete Kodi entry before Emby
- listItem = [itemId]
- LibrarySync().removefromDB(listItem, True)
-
- # Stop transcoding
- if playMethod == "Transcode":
- self.logMsg("Transcoding for %s terminated." % itemId, 1)
- deviceId = self.clientInfo.getMachineId()
- url = "{server}/mediabrowser/Videos/ActiveEncodings?DeviceId=%s" % deviceId
- self.doUtils.downloadUrl(url, type="DELETE")
-
- self.played_information.clear()
-
- def stopPlayback(self, data):
-
- self.logMsg("stopPlayback called", 2)
-
- itemId = data['item_id']
- currentPosition = data['currentPosition']
- positionTicks = int(currentPosition * 10000000)
-
- url = "{server}/mediabrowser/Sessions/Playing/Stopped"
- postdata = {
-
- 'ItemId': itemId,
- 'MediaSourceId': itemId,
- 'PositionTicks': positionTicks
- }
-
- self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
\ No newline at end of file
diff --git a/resources/lib/UserClient.py b/resources/lib/UserClient.py
deleted file mode 100644
index f95f9291..00000000
--- a/resources/lib/UserClient.py
+++ /dev/null
@@ -1,460 +0,0 @@
-#################################################################################################
-# UserClient thread
-#################################################################################################
-
-import xbmc
-import xbmcgui
-import xbmcaddon
-import xbmcvfs
-
-import threading
-import hashlib
-import json as json
-
-import KodiMonitor
-import Utils as utils
-from ClientInformation import ClientInformation
-from DownloadUtils import DownloadUtils
-from Player import Player
-from API import API
-
-
-class UserClient(threading.Thread):
-
- # Borg - multiple instances, shared state
- _shared_state = {}
-
- clientInfo = ClientInformation()
- doUtils = DownloadUtils()
- KodiMonitor = KodiMonitor.Kodi_Monitor()
-
- addonName = clientInfo.getAddonName()
- addon = xbmcaddon.Addon()
- WINDOW = xbmcgui.Window(10000)
-
- stopClient = False
- logLevel = int(addon.getSetting('logLevel'))
- auth = True
- retry = 0
-
- currUser = None
- currUserId = None
- currServer = None
- currToken = None
- HasAccess = True
- AdditionalUser = []
-
- def __init__(self, *args):
-
- self.__dict__ = self._shared_state
- threading.Thread.__init__(self, *args)
-
- def logMsg(self, msg, lvl=1):
-
- className = self.__class__.__name__
- utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl))
-
- def getUsername(self):
-
- username = utils.settings('username')
-
- if (username == ""):
- self.logMsg("No username saved.", 2)
- return ""
-
- return username
-
- def getAdditionalUsers(self):
-
- additionalUsers = utils.settings('additionalUsers')
-
- if additionalUsers:
- self.AdditionalUser = additionalUsers.split(',')
-
- def getLogLevel(self):
-
- try:
- logLevel = int(utils.settings('logLevel'))
- except:
- logLevel = 0
-
- return logLevel
-
- def getUserId(self):
-
- username = self.getUsername()
- w_userId = self.WINDOW.getProperty('userId%s' % username)
- s_userId = utils.settings('userId%s' % username)
-
- # Verify the window property
- if (w_userId != ""):
- self.logMsg("Returning userId from WINDOW for username: %s UserId: %s" % (username, w_userId), 2)
- return w_userId
- # Verify the settings
- elif (s_userId != ""):
- self.logMsg("Returning userId from SETTINGS for username: %s userId: %s" % (username, s_userId), 2)
- return s_userId
- # No userId found
- else:
- self.logMsg("No userId saved for username: %s." % username)
- return
-
- def getServer(self, prefix=True):
-
- alternate = utils.settings('altip') == "true"
-
- # For https support
- HTTPS = utils.settings('https')
- host = utils.settings('ipaddress')
- port = utils.settings('port')
- # Alternate host
- if alternate:
- HTTPS = utils.settings('secondhttps')
- host = utils.settings('secondipaddress')
- port = utils.settings('secondport')
-
- server = host + ":" + port
-
- if host == "":
- self.logMsg("No server information saved.", 2)
- return ""
-
- # If https is true
- if prefix and (HTTPS == "true"):
- server = "https://%s" % server
- return server
- # If https is false
- elif prefix and (HTTPS == "false"):
- server = "http://%s" % server
- return server
- # If only the host:port is required
- elif (prefix == False):
- return server
-
- def getToken(self):
-
- username = self.getUsername()
- w_token = self.WINDOW.getProperty('accessToken%s' % username)
- s_token = utils.settings('accessToken')
-
- # Verify the window property
- if (w_token != ""):
- self.logMsg("Returning accessToken from WINDOW for username: %s accessToken: %s" % (username, w_token), 2)
- return w_token
- # Verify the settings
- elif (s_token != ""):
- self.logMsg("Returning accessToken from SETTINGS for username: %s accessToken: %s" % (username, s_token), 2)
- self.WINDOW.setProperty('accessToken%s' % username, s_token)
- return s_token
- else:
- self.logMsg("No token found.")
- return ""
-
- def getSSLverify(self):
- # Verify host certificate
- s_sslverify = utils.settings('sslverify')
- if utils.settings('altip') == "true":
- s_sslverify = utils.settings('secondsslverify')
-
- if s_sslverify == "true":
- return True
- else:
- return False
-
- def getSSL(self):
- # Client side certificate
- s_cert = utils.settings('sslcert')
- if utils.settings('altip') == "true":
- s_cert = utils.settings('secondsslcert')
-
- if s_cert == "None":
- return None
- else:
- return s_cert
-
- def setUserPref(self):
-
- player = Player()
- server = self.getServer()
- userId = self.getUserId()
-
- url = "{server}/mediabrowser/Users/{UserId}?format=json"
- result = self.doUtils.downloadUrl(url)
-
- # Set user image for skin display
- self.WINDOW.setProperty("EmbyUserImage",API().getUserArtwork(result,"Primary"))
-
- # Load the resume point from Emby and set as setting
- url = "{server}/mediabrowser/System/Configuration?format=json"
- result = self.doUtils.downloadUrl(url)
-
- utils.settings('markPlayed', value=str(result['MaxResumePct']))
-
- return True
-
- def getPublicUsers(self):
-
- server = self.getServer()
-
- # Get public Users
- url = "%s/mediabrowser/Users/Public?format=json" % server
- result = self.doUtils.downloadUrl(url, authenticate=False)
-
- users = []
-
- if (result != ""):
- users = result
- else:
- # Server connection failed
- return False
-
- return users
-
- def hasAccess(self):
-
- url = "{server}/mediabrowser/Users"
- result = self.doUtils.downloadUrl(url)
-
- if result is False:
- # Access is restricted
- self.logMsg("Access is restricted.")
- self.HasAccess = False
- return
- elif self.WINDOW.getProperty('Server_online') != "true":
- # Server connection failed
- return
-
- if self.WINDOW.getProperty("Server_status") == "restricted":
- self.logMsg("Access is granted.")
- self.HasAccess = True
- self.WINDOW.setProperty("Server_status", "")
- xbmcgui.Dialog().notification("Emby server", "Access is enabled.")
- return
-
- def loadCurrUser(self, authenticated=False):
-
- WINDOW = self.WINDOW
- doUtils = self.doUtils
- username = self.getUsername()
-
- # Only to be used if token exists
- self.currUserId = self.getUserId()
- self.currServer = self.getServer()
- self.currToken = self.getToken()
- self.ssl = self.getSSLverify()
- self.sslcert = self.getSSL()
-
- # Test the validity of current token
- if authenticated == False:
- url = "%s/mediabrowser/Users/%s" % (self.currServer, self.currUserId)
- WINDOW.setProperty("currUser", username)
- WINDOW.setProperty("accessToken%s" % username, self.currToken)
- result = doUtils.downloadUrl(url)
- if result == 401:
- # Token is no longer valid
- self.resetClient()
- return False
-
- # Set to windows property
- WINDOW.setProperty("currUser", username)
- WINDOW.setProperty("accessToken%s" % username, self.currToken)
- WINDOW.setProperty("server%s" % username, self.currServer)
- WINDOW.setProperty("server_%s" % username, self.getServer(prefix=False))
- WINDOW.setProperty("userId%s" % username, self.currUserId)
-
- # Set DownloadUtils values
- doUtils.setUsername(username)
- doUtils.setUserId(self.currUserId)
- doUtils.setServer(self.currServer)
- doUtils.setToken(self.currToken)
- doUtils.setSSL(self.ssl, self.sslcert)
- # parental control - let's verify if access is restricted
- self.hasAccess()
- # Start DownloadUtils session
- doUtils.startSession()
- self.getAdditionalUsers()
-
- self.currUser = username
- # Set user preferences in settings
- self.setUserPref()
-
- def authenticate(self):
-
- WINDOW = self.WINDOW
- addon = self.addon
-
- username = self.getUsername()
- server = self.getServer()
- addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
- hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
-
- # If there's no settings.xml
- if (hasSettings == 0):
- self.logMsg("No settings.xml found.")
- self.auth = False
- return
- # If no user information
- if (server == "") or (username == ""):
- self.logMsg("Missing server information.")
- self.auth = False
- return
- # If there's a token
- if (self.getToken() != ""):
- result = self.loadCurrUser()
-
- if result == False:
- pass
- else:
- self.logMsg("Current user: %s" % self.currUser, 0)
- self.logMsg("Current userId: %s" % self.currUserId, 0)
- self.logMsg("Current accessToken: %s" % self.currToken, 0)
- return
-
- users = self.getPublicUsers()
- password = ""
-
- # Find user in list
- for user in users:
- name = user[u'Name']
- userHasPassword = False
-
- if (unicode(username, 'utf-8') in name):
- # Verify if user has a password
- if (user.get("HasPassword") == True):
- userHasPassword = True
- # If user has password
- if (userHasPassword):
- password = xbmcgui.Dialog().input("Enter password for user: %s" % username, option=xbmcgui.ALPHANUM_HIDE_INPUT)
- # If password dialog is cancelled
- if (password == ""):
- self.logMsg("No password entered.", 0)
- self.WINDOW.setProperty("Server_status", "Stop")
- self.auth = False
- return
- break
- else:
- # Manual login, user is hidden
- password = xbmcgui.Dialog().input("Enter password for user: %s" % username, option=xbmcgui.ALPHANUM_HIDE_INPUT)
-
- sha1 = hashlib.sha1(password)
- sha1 = sha1.hexdigest()
-
- # Authenticate username and password
- url = "%s/mediabrowser/Users/AuthenticateByName?format=json" % server
- data = {'username': username, 'password': sha1}
- self.logMsg(data, 2)
-
- result = self.doUtils.downloadUrl(url, postBody=data, type="POST", authenticate=False)
-
- accessToken = None
- try:
- self.logMsg("Auth_Reponse: %s" % result, 1)
- accessToken = result[u'AccessToken']
- except:
- pass
-
- if (result != None and accessToken != None):
- self.currUser = username
- xbmcgui.Dialog().notification("Emby server", "Welcome %s!" % self.currUser)
- userId = result[u'User'][u'Id']
- utils.settings("accessToken", accessToken)
- utils.settings("userId%s" % username, userId)
- self.logMsg("User Authenticated: %s" % accessToken)
- self.loadCurrUser(authenticated=True)
- self.WINDOW.setProperty("Server_status", "")
- self.retry = 0
- return
- else:
- self.logMsg("User authentication failed.")
- utils.settings("accessToken", "")
- utils.settings("userId%s" % username, "")
- xbmcgui.Dialog().ok("Error connecting", "Invalid username or password.")
-
- # Give two attempts at entering password
- self.retry += 1
- if self.retry == 2:
- self.logMsg("Too many retries. You can retry by selecting the option in the addon settings.")
- self.WINDOW.setProperty("Server_status", "Stop")
- xbmcgui.Dialog().ok("Error connecting", "Failed to authenticate too many times. You can retry by selecting the option in the addon settings.")
-
- self.auth = False
- return
-
- def resetClient(self):
-
- username = self.getUsername()
- self.logMsg("Reset UserClient authentication.", 1)
- if (self.currToken != None):
- # In case of 401, removed saved token
- utils.settings("accessToken", "")
- self.WINDOW.setProperty("accessToken%s" % username, "")
- self.currToken = None
- self.logMsg("User token has been removed.", 1)
-
- self.auth = True
- self.currUser = None
- return
-
-
- def run(self):
-
- self.logMsg("|---- Starting UserClient ----|", 0)
-
- while not self.KodiMonitor.abortRequested():
-
- # Verify the log level
- currLogLevel = self.getLogLevel()
- if self.logLevel != currLogLevel:
- # Set new log level
- self.logLevel = currLogLevel
- self.logMsg("New Log Level: %s" % currLogLevel, 0)
- self.WINDOW.setProperty('getLogLevel', str(currLogLevel))
-
- if (self.WINDOW.getProperty("Server_status") != ""):
- status = self.WINDOW.getProperty("Server_status")
-
- if status == "restricted":
- # Parental control is restricting access
- self.HasAccess = False
-
- elif status == "401":
- self.WINDOW.setProperty("Server_status", "Auth")
- # Revoked token
- self.resetClient()
-
- if self.auth and (self.currUser == None):
- status = self.WINDOW.getProperty("Server_status")
-
- if (status == "") or (status == "Auth"):
- self.auth = False
- self.authenticate()
-
- if (self.auth == False) and (self.currUser == None):
- # Only if there's information found to login
- server = self.getServer()
- username = self.getUsername()
- status = self.WINDOW.getProperty("Server_status")
-
- # If user didn't enter a password when prompted
- if status == "Stop":
- pass
-
- elif (server != "") and (username != ""):
- self.logMsg("Server found: %s" % server)
- self.logMsg("Username found: %s" % username)
- self.auth = True
-
- # If stopping the client didn't work
- if self.stopClient == True:
- break
-
- if self.KodiMonitor.waitForAbort(1):
- # Abort was requested while waiting. We should exit
- break
-
- self.doUtils.stopSession()
- self.logMsg("|---- UserClient Stopped ----|", 0)
-
- def stopClient(self):
- # As last resort
- self.stopClient = True
\ No newline at end of file
diff --git a/resources/lib/Utils.py b/resources/lib/Utils.py
deleted file mode 100644
index 73c49c95..00000000
--- a/resources/lib/Utils.py
+++ /dev/null
@@ -1,425 +0,0 @@
-#################################################################################################
-# utils
-#################################################################################################
-
-import xbmc
-import xbmcgui
-import xbmcaddon
-import xbmcvfs
-import json
-import os
-import cProfile
-import pstats
-import time
-import inspect
-import sqlite3
-import string
-import unicodedata
-import xml.etree.ElementTree as etree
-
-from API import API
-from PlayUtils import PlayUtils
-from DownloadUtils import DownloadUtils
-
-downloadUtils = DownloadUtils()
-addon = xbmcaddon.Addon()
-language = addon.getLocalizedString
-
-
-def logMsg(title, msg, level = 1):
-
- WINDOW = xbmcgui.Window(10000)
- # Get the logLevel set in UserClient
- logLevel = int(WINDOW.getProperty('getLogLevel'))
-
- if(logLevel >= level):
- if(logLevel == 2): # inspect.stack() is expensive
- try:
- xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg))
- except UnicodeEncodeError:
- xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg.encode('utf-8')))
- else:
- try:
- xbmc.log(title + " -> " + str(msg))
- except UnicodeEncodeError:
- xbmc.log(title + " -> " + str(msg.encode('utf-8')))
-
-def convertEncoding(data):
- #nasty hack to make sure we have a unicode string
- try:
- return data.decode('utf-8')
- except:
- return data
-
-def KodiSQL(type="video"):
-
- if type == "music":
- dbPath = getKodiMusicDBPath()
- elif type == "texture":
- dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
- else:
- dbPath = getKodiVideoDBPath()
-
- connection = sqlite3.connect(dbPath)
- return connection
-
-def getKodiVideoDBPath():
-
- kodibuild = xbmc.getInfoLabel('System.BuildVersion')[:2]
- dbVersion = {
-
- "13": 78, # Gotham
- "14": 90, # Helix
- "15": 93, # Isengard
- "16": 99 # Jarvis
- }
-
- dbPath = xbmc.translatePath(
- "special://database/MyVideos%s.db"
- % dbVersion.get(kodibuild, "")).decode('utf-8')
- return dbPath
-
-def getKodiMusicDBPath():
-
- kodibuild = xbmc.getInfoLabel('System.BuildVersion')[:2]
- dbVersion = {
-
- "13": 46, # Gotham
- "14": 48, # Helix
- "15": 52, # Isengard
- "16": 56 # Jarvis
- }
-
- dbPath = xbmc.translatePath(
- "special://database/MyMusic%s.db"
- % dbVersion.get(kodibuild, "")).decode('utf-8')
- return dbPath
-
-def prettifyXml(elem):
- rough_string = etree.tostring(elem, "utf-8")
- reparsed = minidom.parseString(rough_string)
- return reparsed.toprettyxml(indent="\t")
-
-def startProfiling():
- pr = cProfile.Profile()
- pr.enable()
- return pr
-
-def stopProfiling(pr, profileName):
- pr.disable()
- ps = pstats.Stats(pr)
-
- addondir = xbmc.translatePath(xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('profile'))
-
- fileTimeStamp = time.strftime("%Y-%m-%d %H-%M-%S")
- tabFileNamepath = os.path.join(addondir, "profiles")
- tabFileName = os.path.join(addondir, "profiles" , profileName + "_profile_(" + fileTimeStamp + ").tab")
-
- if not xbmcvfs.exists(tabFileNamepath):
- xbmcvfs.mkdir(tabFileNamepath)
-
- f = open(tabFileName, 'wb')
- f.write("NumbCalls\tTotalTime\tCumulativeTime\tFunctionName\tFileName\r\n")
- for (key, value) in ps.stats.items():
- (filename, count, func_name) = key
- (ccalls, ncalls, total_time, cumulative_time, callers) = value
- try:
- f.write(str(ncalls) + "\t" + "{:10.4f}".format(total_time) + "\t" + "{:10.4f}".format(cumulative_time) + "\t" + func_name + "\t" + filename + "\r\n")
- except ValueError:
- f.write(str(ncalls) + "\t" + "{0}".format(total_time) + "\t" + "{0}".format(cumulative_time) + "\t" + func_name + "\t" + filename + "\r\n")
- f.close()
-
-def indent(elem, level=0):
- # Prettify xml trees
- i = "\n" + level*" "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- for elem in elem:
- indent(elem, level+1)
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- else:
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = i
-
-def createSources():
- # To make Master lock compatible
- path = xbmc.translatePath("special://profile/").decode("utf-8")
- xmlpath = "%ssources.xml" % path
-
- if xbmcvfs.exists(xmlpath):
- # Modify the existing file
- try:
- xmlparse = etree.parse(xmlpath)
- except:
- root = etree.Element('sources')
- else:
- root = xmlparse.getroot()
-
- video = root.find('video')
- if video is None:
- video = etree.SubElement(root, 'video')
- else:
- # We need to create the file
- root = etree.Element('sources')
- video = etree.SubElement(root, 'video')
-
-
- # Add elements
- etree.SubElement(video, 'default', attrib={'pathversion': "1"})
-
- # First dummy source
- source_one = etree.SubElement(video, 'source')
- etree.SubElement(source_one, 'name').text = "Emby"
- etree.SubElement(source_one, 'path', attrib={'pathversion': "1"}).text = (
-
- "smb://embydummy/dummypath1/"
- )
- etree.SubElement(source_one, 'allowsharing').text = "true"
-
- # Second dummy source
- source_two = etree.SubElement(video, 'source')
- etree.SubElement(source_two, 'name').text = "Emby"
- etree.SubElement(source_two, 'path', attrib={'pathversion': "1"}).text = (
-
- "smb://embydummy/dummypath2/"
- )
- etree.SubElement(source_two, 'allowsharing').text = "true"
-
- try:
- indent(root)
- except:pass
- etree.ElementTree(root).write(xmlpath)
-
-def pathsubstitution(add=True):
-
- path = xbmc.translatePath('special://userdata').decode('utf-8')
- xmlpath = "%sadvancedsettings.xml" % path
- xmlpathexists = xbmcvfs.exists(xmlpath)
-
- # original address
- originalServer = settings('ipaddress')
- originalPort = settings('port')
- originalHttp = settings('https') == "true"
-
- if originalHttp:
- originalHttp = "https"
- else:
- originalHttp = "http"
-
- # Process add or deletion
- if add:
- # second address
- secondServer = settings('secondipaddress')
- secondPort = settings('secondport')
- secondHttp = settings('secondhttps') == "true"
-
- if secondHttp:
- secondHttp = "https"
- else:
- secondHttp = "http"
-
- logMsg("EMBY", "Original address: %s://%s:%s, alternate is: %s://%s:%s" % (originalHttp, originalServer, originalPort, secondHttp, secondServer, secondPort), 1)
-
- if xmlpathexists:
- # we need to modify the file.
- try:
- xmlparse = etree.parse(xmlpath)
- except: # Document is blank
- root = etree.Element('advancedsettings')
- else:
- root = xmlparse.getroot()
-
- pathsubs = root.find('pathsubstitution')
- if pathsubs is None:
- pathsubs = etree.SubElement(root, 'pathsubstitution')
- else:
- # we need to create the file.
- root = etree.Element('advancedsettings')
- pathsubs = etree.SubElement(root, 'pathsubstitution')
-
- substitute = etree.SubElement(pathsubs, 'substitute')
- # From original address
- etree.SubElement(substitute, 'from').text = "%s://%s:%s" % (originalHttp, originalServer, originalPort)
- # To secondary address
- etree.SubElement(substitute, 'to').text = "%s://%s:%s" % (secondHttp, secondServer, secondPort)
-
- etree.ElementTree(root).write(xmlpath)
- settings('pathsub', "true")
-
- else: # delete the path substitution, we don't need it anymore.
- logMsg("EMBY", "Alternate address is disabled, removing path substitution for: %s://%s:%s" % (originalHttp, originalServer, originalPort), 1)
-
- xmlparse = etree.parse(xmlpath)
- root = xmlparse.getroot()
-
- iterator = root.getiterator("pathsubstitution")
-
- for substitutes in iterator:
- for substitute in substitutes:
- frominsert = substitute.find(".//from").text == "%s://%s:%s" % (originalHttp, originalServer, originalPort)
-
- if frominsert:
- # Found a match, in case there's more than one substitution.
- substitutes.remove(substitute)
-
- etree.ElementTree(root).write(xmlpath)
- settings('pathsub', "false")
-
-
-def settings(setting, value = None):
- # Get or add addon setting
- addon = xbmcaddon.Addon()
- if value:
- addon.setSetting(setting, value)
- else:
- return addon.getSetting(setting)
-
-def window(property, value = None, clear = False):
- # Get or set window property
- WINDOW = xbmcgui.Window(10000)
- if clear:
- WINDOW.clearProperty(property)
- elif value:
- WINDOW.setProperty(property, value)
- else:
- return WINDOW.getProperty(property)
-
-def normalize_string(text):
- # For theme media, do not modify unless
- # modified in TV Tunes
- text = text.replace(":", "")
- text = text.replace("/", "-")
- text = text.replace("\\", "-")
- text = text.replace("<", "")
- text = text.replace(">", "")
- text = text.replace("*", "")
- text = text.replace("?", "")
- text = text.replace('|', "")
- text = text.strip()
- # Remove dots from the last character as windows can not have directories
- # with dots at the end
- text = text.rstrip('.')
- text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
-
- return text
-
-def normalize_nodes(text):
- # For video nodes
- text = text.replace(":", "")
- text = text.replace("/", "-")
- text = text.replace("\\", "-")
- text = text.replace("<", "")
- text = text.replace(">", "")
- text = text.replace("*", "")
- text = text.replace("?", "")
- text = text.replace('|', "")
- text = text.replace('(', "")
- text = text.replace(')', "")
- text = text.strip()
- # Remove dots from the last character as windows can not have directories
- # with dots at the end
- text = text.rstrip('.')
- text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
-
- return text
-
-def reloadProfile():
- # Useful to reload the add-on without restarting Kodi.
- profile = xbmc.getInfoLabel('System.ProfileName')
- xbmc.executebuiltin("LoadProfile(%s)" % profile)
-
-
-def reset():
-
- WINDOW = xbmcgui.Window( 10000 )
- return_value = xbmcgui.Dialog().yesno("Warning", "Are you sure you want to reset your local Kodi database?")
-
- if return_value == 0:
- return
-
- # Because the settings dialog could be open
- # it seems to override settings so we need to close it before we reset settings.
- xbmc.executebuiltin("Dialog.Close(all,true)")
-
- #cleanup video nodes
- import shutil
- path = "special://profile/library/video/"
- if xbmcvfs.exists(path):
- allDirs, allFiles = xbmcvfs.listdir(path)
- for dir in allDirs:
- if dir.startswith("Emby "):
- shutil.rmtree(xbmc.translatePath("special://profile/library/video/" + dir))
- for file in allFiles:
- if file.startswith("emby"):
- xbmcvfs.delete(path + file)
-
- settings('SyncInstallRunDone', "false")
-
- # Ask if user information should be deleted too.
- return_user = xbmcgui.Dialog().yesno("Warning", "Reset all Emby Addon settings?")
- if return_user == 1:
- WINDOW.setProperty('deletesettings', "true")
- addon = xbmcaddon.Addon()
- addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
- dataPath = "%ssettings.xml" % addondir
- xbmcvfs.delete(dataPath)
- logMsg("EMBY", "Deleting: settings.xml", 1)
-
- # first stop any db sync
- WINDOW.setProperty("SyncDatabaseShouldStop", "true")
-
- count = 0
- while(WINDOW.getProperty("SyncDatabaseRunning") == "true"):
- xbmc.log("Sync Running, will wait : " + str(count))
- count += 1
- if(count > 10):
- dialog = xbmcgui.Dialog()
- dialog.ok('Warning', 'Could not stop DB sync, you should try again.')
- return
- xbmc.sleep(1000)
-
- # delete video db table data
- print "Doing Video DB Reset"
- connection = KodiSQL("video")
- cursor = connection.cursor( )
- cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
- rows = cursor.fetchall()
- for row in rows:
- tableName = row[0]
- if(tableName != "version"):
- cursor.execute("DELETE FROM " + tableName)
- cursor.execute("DROP TABLE IF EXISTS emby")
- connection.commit()
- cursor.close()
-
- if settings('enableMusicSync') == "true":
- # delete video db table data
- print "Doing Music DB Reset"
- connection = KodiSQL("music")
- cursor = connection.cursor( )
- cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
- rows = cursor.fetchall()
- for row in rows:
- tableName = row[0]
- if(tableName != "version"):
- cursor.execute("DELETE FROM " + tableName)
- cursor.execute("DROP TABLE IF EXISTS emby")
- connection.commit()
- cursor.close()
-
-
- # reset the install run flag
- #settings('SyncInstallRunDone', "false")
- #WINDOW.setProperty("SyncInstallRunDone", "false")
-
- dialog = xbmcgui.Dialog()
- # Reload would work instead of restart since the add-on is a service.
- #dialog.ok('Emby Reset', 'Database reset has completed, Kodi will now restart to apply the changes.')
- #WINDOW.clearProperty("SyncDatabaseShouldStop")
- #reloadProfile()
- dialog.ok('Emby Reset', 'Database reset has completed, Kodi will now restart to apply the changes.')
- xbmc.executebuiltin("RestartApp")
\ No newline at end of file
diff --git a/resources/lib/VideoNodes.py b/resources/lib/VideoNodes.py
deleted file mode 100644
index 056ee13f..00000000
--- a/resources/lib/VideoNodes.py
+++ /dev/null
@@ -1,466 +0,0 @@
-#################################################################################################
-# VideoNodes - utils to create video nodes listings in kodi for the emby addon
-#################################################################################################
-
-
-import xbmc
-import xbmcgui
-import xbmcaddon
-import xbmcvfs
-import json
-import os
-import shutil
-#import common elementree because cElementree has issues with kodi
-import xml.etree.ElementTree as etree
-
-import Utils as utils
-
-from ReadEmbyDB import ReadEmbyDB
-WINDOW = xbmcgui.Window(10000)
-
-addonSettings = xbmcaddon.Addon()
-language = addonSettings.getLocalizedString
-
-class VideoNodes():
-
-
- def buildVideoNodeForView(self, tagname, type, windowPropId):
- #this method will build a video node for a particular Emby view (= tag in kodi)
- #we set some window props here to for easy future reference and to be used in skins (for easy access only)
- tagname_normalized = utils.normalize_nodes(tagname.encode('utf-8'))
-
- libraryPath = xbmc.translatePath("special://profile/library/video/Emby - %s/" %tagname_normalized)
- kodiVersion = 14
- if xbmc.getInfoLabel("System.BuildVersion").startswith("15") or xbmc.getInfoLabel("System.BuildVersion").startswith("16"):
- kodiVersion = 15
-
- #create tag node - index
- xbmcvfs.mkdir(libraryPath)
- nodefile = os.path.join(libraryPath, "index.xml")
- root = etree.Element("node", {"order":"0"})
- etree.SubElement(root, "label").text = tagname
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- path = "library://video/Emby - %s/" %tagname_normalized
- WINDOW.setProperty("Emby.nodes.%s.index" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - all items
- nodefile = os.path.join(libraryPath, tagname_normalized + "_all.xml")
- root = etree.Element("node", {"order":"1", "type":"filter"})
- etree.SubElement(root, "label").text = tagname
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- WINDOW.setProperty("Emby.nodes.%s.title" %str(windowPropId),tagname)
- path = "library://video/Emby - %s/%s_all.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.content" %str(windowPropId),path)
- WINDOW.setProperty("Emby.nodes.%s.type" %str(windowPropId),type)
- etree.SubElement(Rule, "value").text = tagname
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - recent items
- nodefile = os.path.join(libraryPath, tagname_normalized + "_recent.xml")
- root = etree.Element("node", {"order":"2", "type":"filter"})
- if type == "tvshows":
- label = language(30170)
- else:
- label = language(30174)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- etree.SubElement(root, "order", {"direction":"descending"}).text = "dateadded"
- #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
- etree.SubElement(root, "limit").text = "25"
- #exclude watched items --> currently hardcoded --> TODO: add a setting for this ?
- Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
- etree.SubElement(Rule2, "value").text = "0"
- WINDOW.setProperty("Emby.nodes.%s.recent.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_recent.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.recent.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.recent.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - inprogress items
- nodefile = os.path.join(libraryPath, tagname_normalized + "_progress.xml")
- root = etree.Element("node", {"order":"3", "type":"filter"})
- if type == "tvshows":
- label = language(30171)
- else:
- label = language(30177)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
- etree.SubElement(root, "limit").text = "25"
- Rule2 = etree.SubElement(root, "rule", {"field":"inprogress","operator":"true"})
- WINDOW.setProperty("Emby.nodes.%s.inprogress.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_progress.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.inprogress.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.inprogress.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #some movies-only nodes
- if type == "movies":
-
- #unwatched movies
- nodefile = os.path.join(libraryPath, tagname_normalized + "_unwatched.xml")
- root = etree.Element("node", {"order":"4", "type":"filter"})
- label = language(30189)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = "movies"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- Rule = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
- etree.SubElement(Rule, "value").text = "0"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
- Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
- etree.SubElement(Rule2, "value").text = "0"
- WINDOW.setProperty("Emby.nodes.%s.unwatched.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_unwatched.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.unwatched.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.unwatched.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #sets
- nodefile = os.path.join(libraryPath, tagname_normalized + "_sets.xml")
- root = etree.Element("node", {"order":"9", "type":"filter"})
- label = xbmc.getLocalizedString(20434)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "group").text = "sets"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- WINDOW.setProperty("Emby.nodes.%s.sets.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_sets.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.sets.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.sets.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - genres
- nodefile = os.path.join(libraryPath, tagname_normalized + "_genres.xml")
- root = etree.Element("node", {"order":"9", "type":"filter"})
- label = xbmc.getLocalizedString(135)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "group").text = "genres"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- WINDOW.setProperty("Emby.nodes.%s.genres.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_genres.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.genres.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.genres.content" %str(windowPropId),path)
-
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - random items
- nodefile = os.path.join(libraryPath, tagname_normalized + "_random.xml")
- root = etree.Element("node", {"order":"10", "type":"filter"})
- label = language(30229)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
- etree.SubElement(root, "limit").text = "25"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "random"
- WINDOW.setProperty("Emby.nodes.%s.random.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_random.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.random.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.random.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - recommended items
- nodefile = os.path.join(libraryPath, tagname_normalized + "_recommended.xml")
- root = etree.Element("node", {"order":"10", "type":"filter"})
- label = language(30230)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = type
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
- etree.SubElement(Rule2, "value").text = "0"
- Rule3 = etree.SubElement(root, "rule", {"field":"rating","operator":"greaterthan"})
- etree.SubElement(Rule3, "value").text = "7"
- #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
- etree.SubElement(root, "limit").text = "25"
- etree.SubElement(root, "order", {"direction":"descending"}).text = "rating"
- WINDOW.setProperty("Emby.nodes.%s.random.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_recommended.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.recommended.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.recommended.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #### TAGS ONLY FOR TV SHOWS COLLECTIONS ####
- if type == "tvshows":
-
- #as from kodi isengard you can use tags for episodes to filter
- #for below isengard we still use the plugin's entrypoint to build a listing
- if kodiVersion == 15:
- #create tag node - recent episodes
- nodefile = os.path.join(libraryPath, tagname_normalized + "_recent_episodes.xml")
- root = etree.Element("node", {"order":"3", "type":"filter"})
- label = language(30175)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = "episodes"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- etree.SubElement(root, "order", {"direction":"descending"}).text = "dateadded"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
- etree.SubElement(root, "limit").text = "25"
- #exclude watched items --> currently hardcoded --> TODO: add a setting for this ?
- Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
- etree.SubElement(Rule2, "value").text = "0"
- WINDOW.setProperty("Emby.nodes.%s.recentepisodes.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_recent_episodes.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.recentepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.recentepisodes.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - inprogress episodes
- nodefile = os.path.join(libraryPath, tagname_normalized + "_progress_episodes.xml")
- root = etree.Element("node", {"order":"4", "type":"filter"})
- label = language(30178)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = "episodes"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = tagname
- #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
- etree.SubElement(root, "limit").text = "25"
- Rule2 = etree.SubElement(root, "rule", {"field":"inprogress","operator":"true"})
- WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_progress_episodes.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- if kodiVersion == 14:
- #create tag node - recent episodes
- nodefile = os.path.join(libraryPath, tagname_normalized + "_recent_episodes.xml")
- root = etree.Element("node", {"order":"4", "type":"folder"})
- label = language(30175)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "content").text = "episodes"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" %tagname
- etree.SubElement(root, "path").text = path
- WINDOW.setProperty("Emby.nodes.%s.recentepisodes.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_recent_episodes.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.recentepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.recentepisodes.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - inprogress items
- nodefile = os.path.join(libraryPath, tagname_normalized + "_progress_episodes.xml")
- root = etree.Element("node", {"order":"5", "type":"folder"})
- label = language(30178)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "content").text = "episodes"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25" %tagname
- etree.SubElement(root, "path").text = path
- WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_progress_episodes.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - nextup items
- #for nextup we always use the dynamic content approach with the plugin's entrypoint because it involves a custom query
- nodefile = os.path.join(libraryPath, tagname_normalized + "_nextup_episodes.xml")
- root = etree.Element("node", {"order":"6", "type":"folder"})
- label = language(30179)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "content").text = "episodes"
- path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" %tagname
- etree.SubElement(root, "path").text = path
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- WINDOW.setProperty("Emby.nodes.%s.nextepisodes.title" %str(windowPropId),label)
- path = "library://video/Emby - %s/%s_nextup_episodes.xml"%(tagname_normalized,tagname_normalized)
- WINDOW.setProperty("Emby.nodes.%s.nextepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.nextepisodes.content" %str(windowPropId),path)
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- def buildVideoNodesListing(self):
-
- try:
-
- # the library path doesn't exist on all systems
- if not xbmcvfs.exists("special://profile/library/"):
- xbmcvfs.mkdir("special://profile/library")
- if not xbmcvfs.exists("special://profile/library/video/"):
- #we need to copy over the default items
- shutil.copytree(xbmc.translatePath("special://xbmc/system/library/video"), xbmc.translatePath("special://profile/library/video"))
-
- #always cleanup existing Emby video nodes first because we don't want old stuff to stay in there
- path = "special://profile/library/video/"
- if xbmcvfs.exists(path):
- allDirs, allFiles = xbmcvfs.listdir(path)
- for dir in allDirs:
- if dir.startswith("Emby "):
- shutil.rmtree(xbmc.translatePath("special://profile/library/video/" + dir))
- for file in allFiles:
- if file.startswith("emby"):
- xbmcvfs.delete(path + file)
-
- #we build up a listing and set window props for all nodes we created
- #the window props will be used by the main entry point to quickly build up the listing and can be used in skins (like titan) too for quick reference
- #comment marcelveldt: please leave the window props as-is because I will be referencing them in titan skin...
- totalNodesCount = 0
-
- #build the listing for all views
- views_movies = ReadEmbyDB().getCollections("movies")
- if views_movies:
- for view in views_movies:
- title = view.get('title')
- content = view.get('content')
- if content == "mixed":
- title = "%s - Movies" % title
- self.buildVideoNodeForView(title, "movies", totalNodesCount)
- totalNodesCount +=1
-
- views_shows = ReadEmbyDB().getCollections("tvshows")
- if views_shows:
- for view in views_shows:
- title = view.get('title')
- content = view.get('content')
- if content == "mixed":
- title = "%s - TV Shows" % title
- self.buildVideoNodeForView(title, "tvshows", totalNodesCount)
- totalNodesCount +=1
-
- #create tag node for emby channels
- nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"), "emby_channels.xml")
- root = etree.Element("node", {"order":"1", "type":"folder"})
- label = language(30173)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "content").text = "movies"
- etree.SubElement(root, "path").text = "plugin://plugin.video.emby/?id=0&mode=channels"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label)
- WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"channels")
- path = "library://video/emby_channels.xml"
- WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path)
- totalNodesCount +=1
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - favorite shows
- nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"),"emby_favorite_shows.xml")
- root = etree.Element("node", {"order":"1", "type":"filter"})
- label = language(30181)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = "tvshows"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = "Favorite tvshows" #do not localize the tagname itself
- WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label)
- WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"favourites")
- path = "library://video/emby_favorite_shows.xml"
- WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path)
- totalNodesCount +=1
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- #create tag node - favorite movies
- nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"),"emby_favorite_movies.xml")
- root = etree.Element("node", {"order":"1", "type":"filter"})
- label = language(30180)
- etree.SubElement(root, "label").text = label
- etree.SubElement(root, "match").text = "all"
- etree.SubElement(root, "content").text = "movies"
- etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
- etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
- Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
- etree.SubElement(Rule, "value").text = "Favorite movies" #do not localize the tagname itself
- WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label)
- WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"favourites")
- path = "library://video/emby_favorite_movies.xml"
- WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path)
- WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path)
- totalNodesCount +=1
- try:
- etree.ElementTree(root).write(nodefile, xml_declaration=True)
- except:
- etree.ElementTree(root).write(nodefile)
-
- WINDOW.setProperty("Emby.nodes.total", str(totalNodesCount))
-
-
- except Exception as e:
- utils.logMsg("Emby addon","Error while creating videonodes listings, restart required ?")
- print e
\ No newline at end of file