Merge branch 'develop'

This commit is contained in:
tomkat83 2016-03-07 17:26:02 +01:00
commit f15ac366d8
20 changed files with 290 additions and 207 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" <addon id="plugin.video.plexkodiconnect"
name="PlexKodiConnect" name="PlexKodiConnect"
version="2.2.7" version="2.2.8"
provider-name="croneter"> provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>

View file

@ -1,3 +1,7 @@
version 2.2.8
- Fix to photos not displaying directories without picutres.
- Fix to grouped views causing crash
version 2.2.7 version 2.2.7
- Prevent Kodi screensaver during the initial sync - Prevent Kodi screensaver during the initial sync

View file

@ -51,7 +51,6 @@ class Main:
'reset': utils.reset, 'reset': utils.reset,
'resetauth': entrypoint.resetAuth, 'resetauth': entrypoint.resetAuth,
'extrafanart': entrypoint.getExtraFanArt,
'play': entrypoint.doPlayback, 'play': entrypoint.doPlayback,
'passwords': utils.passwordsXML, 'passwords': utils.passwordsXML,
'adduser': entrypoint.addUser, 'adduser': entrypoint.addUser,
@ -70,8 +69,15 @@ class Main:
'doPlexTvLogin': entrypoint.doPlexTvLogin 'doPlexTvLogin': entrypoint.doPlexTvLogin
} }
if "extrafanart" in sys.argv[0]: if "/extrafanart" in sys.argv[0]:
entrypoint.getExtraFanArt() embypath = sys.argv[2][1:]
embyid = params.get('id',[""])[0]
entrypoint.getExtraFanArt(embyid,embypath)
if "/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0]:
embypath = sys.argv[2][1:]
embyid = params.get('id',[""])[0]
entrypoint.getVideoFiles(embyid,embypath)
if modes.get(mode): if modes.get(mode):
# Simple functions # Simple functions

View file

@ -364,6 +364,7 @@
<string id="39023">Failed to authenticate. Did you login to plex.tv?</string> <string id="39023">Failed to authenticate. Did you login to plex.tv?</string>
<string id="39024">[COLOR yellow]Log into plex.tv[/COLOR]</string> <string id="39024">[COLOR yellow]Log into plex.tv[/COLOR]</string>
<string id="39025">Automatically log into plex.tv on startup</string> <string id="39025">Automatically log into plex.tv on startup</string>
<string id="39026">Enable constant background sync (restart Kodi!)</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->

View file

@ -295,6 +295,7 @@
<string id="39023">Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt?</string> <string id="39023">Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt?</string>
<string id="39024">[COLOR yellow]Bei plex.tv einloggen[/COLOR]</string> <string id="39024">[COLOR yellow]Bei plex.tv einloggen[/COLOR]</string>
<string id="39025">Automatisch beim Starten bei plex.tv einloggen</string> <string id="39025">Automatisch beim Starten bei plex.tv einloggen</string>
<string id="39026">Laufende Synchronisierung im Hintergrund aktivieren (Neustart!)</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->
<string id="39200">Plex Home Benutzer wechseln</string> <string id="39200">Plex Home Benutzer wechseln</string>

View file

@ -79,7 +79,6 @@ class PlexAPI():
self.deviceName = client.getDeviceName() self.deviceName = client.getDeviceName()
self.plexversion = client.getVersion() self.plexversion = client.getVersion()
self.platform = client.getPlatform() self.platform = client.getPlatform()
self.user = utils.window('plex_username')
self.userId = utils.window('emby_currUser') self.userId = utils.window('emby_currUser')
self.token = utils.window('emby_accessToken%s' % self.userId) self.token = utils.window('emby_accessToken%s' % self.userId)
self.server = utils.window('emby_server%s' % self.userId) self.server = utils.window('emby_server%s' % self.userId)
@ -95,13 +94,15 @@ class PlexAPI():
'plexid': utils.settings('plexid'), 'plexid': utils.settings('plexid'),
'myplexlogin': utils.settings('myplexlogin') 'myplexlogin': utils.settings('myplexlogin')
plexLogin is unicode or empty unicode string u''
Returns empty strings '' for a setting if not found. Returns empty strings '' for a setting if not found.
myplexlogin is 'true' if user opted to log into plex.tv (the default) myplexlogin is 'true' if user opted to log into plex.tv (the default)
plexhome is 'true' if plex home is used (the default) plexhome is 'true' if plex home is used (the default)
""" """
return { return {
'plexLogin': utils.settings('plexLogin'), 'plexLogin': utils.settings('plexLogin').decode('utf-8'),
'plexToken': utils.settings('plexToken'), 'plexToken': utils.settings('plexToken'),
'plexhome': utils.settings('plexhome'), 'plexhome': utils.settings('plexhome'),
'plexid': utils.settings('plexid'), 'plexid': utils.settings('plexid'),
@ -127,32 +128,31 @@ class PlexAPI():
retrievedPlexLogin = '' retrievedPlexLogin = ''
plexLogin = 'dummy' plexLogin = 'dummy'
authtoken = '' authtoken = ''
dialog = xbmcgui.Dialog()
while retrievedPlexLogin == '' and plexLogin != '': while retrievedPlexLogin == '' and plexLogin != '':
dialog = xbmcgui.Dialog()
# Enter plex.tv username. Or nothing to cancel. # Enter plex.tv username. Or nothing to cancel.
plexLogin = dialog.input( plexLogin = dialog.input(
self.addonName + string(39300), self.addonName.encode('utf-8') + string(39300).encode('utf-8'),
type=xbmcgui.INPUT_ALPHANUM, type=xbmcgui.INPUT_ALPHANUM,
) )
if plexLogin != "": if plexLogin != "":
dialog = xbmcgui.Dialog()
# Enter password for plex.tv user # Enter password for plex.tv user
plexPassword = dialog.input( plexPassword = dialog.input(
string(39301) + plexLogin, string(39301).encode('utf-8') + plexLogin.encode('utf-8'),
type=xbmcgui.INPUT_ALPHANUM, type=xbmcgui.INPUT_ALPHANUM,
option=xbmcgui.ALPHANUM_HIDE_INPUT option=xbmcgui.ALPHANUM_HIDE_INPUT
) )
retrievedPlexLogin, authtoken = self.MyPlexSignIn( retrievedPlexLogin, authtoken = self.MyPlexSignIn(
plexLogin, plexLogin,
plexPassword, plexPassword,
{'X-Plex-Client-Identifier': self.clientId} {'X-Plex-Client-Identifier': self.clientId})
)
self.logMsg("plex.tv username and token: %s, %s" self.logMsg("plex.tv username and token: %s, %s"
% (plexLogin, authtoken), 1) % (plexLogin, authtoken), 1)
if plexLogin == '': if plexLogin == '':
dialog = xbmcgui.Dialog()
# Could not sign in user # Could not sign in user
dialog.ok(self.addonName, string(39302) + plexLogin) dialog.ok(self.addonName,
string(39302).encode('utf-8')
+ plexLogin.encode('utf-8'))
# Write to Kodi settings file # Write to Kodi settings file
utils.settings('plexLogin', value=retrievedPlexLogin) utils.settings('plexLogin', value=retrievedPlexLogin)
utils.settings('plexToken', value=authtoken) utils.settings('plexToken', value=authtoken)
@ -177,12 +177,12 @@ class PlexAPI():
dialog = xbmcgui.Dialog() dialog = xbmcgui.Dialog()
if not code: if not code:
# Problems trying to contact plex.tv. Try again later # Problems trying to contact plex.tv. Try again later
dialog.ok(self.addonName, string(39303)) dialog.ok(self.addonName, string(39303).encode('utf-8'))
return False return False
# Go to https://plex.tv/pin and enter the code: # Go to https://plex.tv/pin and enter the code:
answer = dialog.yesno(self.addonName, answer = dialog.yesno(self.addonName,
string(39304) + "\n\n", (string(39304) + "\n\n").encode('utf-8'),
code) code.encode('utf-8'))
if not answer: if not answer:
return False return False
count = 0 count = 0
@ -196,7 +196,7 @@ class PlexAPI():
count += 1 count += 1
if not xml: if not xml:
# Could not sign in to plex.tv Try again later # Could not sign in to plex.tv Try again later
dialog.ok(self.addonName, string(39305)) dialog.ok(self.addonName, string(39305).encode('utf-8'))
return False return False
# Parse xml # Parse xml
userid = xml.attrib.get('id') userid = xml.attrib.get('id')
@ -259,11 +259,11 @@ class PlexAPI():
try: try:
code = xml.find('code').text code = xml.find('code').text
identifier = xml.find('id').text identifier = xml.find('id').text
self.logMsg('Successfully retrieved code and id from plex.tv', 1)
return code, identifier
except: except:
self.logMsg("Error, no PIN from plex.tv provided", -1) self.logMsg("Error, no PIN from plex.tv provided", -1)
self.logMsg("plex.tv/pin: Code is: %s" % code, 2) return None, None
self.logMsg("plex.tv/pin: Identifier is: %s" % identifier, 2)
return code, identifier
def TalkToPlexServer(self, url, talkType="GET", verify=True, token=None): def TalkToPlexServer(self, url, talkType="GET", verify=True, token=None):
""" """
@ -345,8 +345,8 @@ class PlexAPI():
sslverify = True sslverify = True
else: else:
sslverify = False sslverify = False
self.logMsg("Checking connection to server %s with header %s and " self.logMsg("Checking connection to server %s with sslverify=%s"
"sslverify=%s" % (url, header, sslverify), 1) % (url, sslverify), 1)
timeout = (3, 10) timeout = (3, 10)
if 'plex.tv' in url: if 'plex.tv' in url:
url = 'https://plex.tv/api/home/users' url = 'https://plex.tv/api/home/users'
@ -814,7 +814,6 @@ class PlexAPI():
'X-Plex-Version': self.plexversion, 'X-Plex-Version': self.plexversion,
'X-Plex-Client-Identifier': self.clientId, 'X-Plex-Client-Identifier': self.clientId,
'X-Plex-Provides': 'player', 'X-Plex-Provides': 'player',
'X-Plex-Username': self.user,
'X-Plex-Client-Capabilities': 'protocols=shoutcast,http-video;videoDecoders=h264{profile:high&resolution:1080&level:51};audioDecoders=mp3,aac,dts{bitrate:800000&channels:8},ac3{bitrate:800000&channels:8}', 'X-Plex-Client-Capabilities': 'protocols=shoutcast,http-video;videoDecoders=h264{profile:high&resolution:1080&level:51};audioDecoders=mp3,aac,dts{bitrate:800000&channels:8},ac3{bitrate:800000&channels:8}',
'X-Plex-Client-Profile-Extra': 'add-transcode-target-audio-codec(type=videoProfile&context=streaming&protocol=*&audioCodec=dca,ac3)', 'X-Plex-Client-Profile-Extra': 'add-transcode-target-audio-codec(type=videoProfile&context=streaming&protocol=*&audioCodec=dca,ac3)',
} }
@ -1042,6 +1041,8 @@ class PlexAPI():
self.logMsg("No URL for user avatar.", 1) self.logMsg("No URL for user avatar.", 1)
return False return False
for user in users: for user in users:
self.logMsg('type user: %s, type username: %s'
% (type(user['title']), type(username)))
if username in user['title']: if username in user['title']:
url = user['thumb'] url = user['thumb']
self.logMsg("Avatar url for user %s is: %s" % (username, url), 1) self.logMsg("Avatar url for user %s is: %s" % (username, url), 1)
@ -1060,6 +1061,7 @@ class PlexAPI():
Will return empty strings if failed. Will return empty strings if failed.
""" """
string = self.__language__ string = self.__language__
dialog = xbmcgui.Dialog()
plexLogin = utils.settings('plexLogin') plexLogin = utils.settings('plexLogin')
plexToken = utils.settings('plexToken') plexToken = utils.settings('plexToken')
@ -1074,19 +1076,21 @@ class PlexAPI():
return ('', '', '') return ('', '', '')
userlist = [] userlist = []
userlistCoded = []
for user in users: for user in users:
username = user['title'] username = user['title']
userlist.append(username) userlist.append(username)
userlistCoded.append(username.encode('utf-8'))
usernumber = len(userlist) usernumber = len(userlist)
usertoken = '' usertoken = ''
# Plex home not in use: only 1 user returned # Plex home not in use: only 1 user returned
trials = 0 trials = 0
while trials < 3: while trials < 3:
if usernumber > 1: if usernumber > 1:
dialog = xbmcgui.Dialog()
# Select user # Select user
user_select = dialog.select(self.addonName + string(39306), user_select = dialog.select(
userlist) (self.addonName + string(39306)).encode('utf-8'),
userlistCoded)
if user_select == -1: if user_select == -1:
self.logMsg("No user selected.", 1) self.logMsg("No user selected.", 1)
xbmc.executebuiltin('Addon.OpenSettings(%s)' xbmc.executebuiltin('Addon.OpenSettings(%s)'
@ -1101,11 +1105,10 @@ class PlexAPI():
user = users[user_select] user = users[user_select]
# Ask for PIN, if protected: # Ask for PIN, if protected:
if user['protected'] == '1': if user['protected'] == '1':
dialog = xbmcgui.Dialog()
# Please enter pin for user # Please enter pin for user
self.logMsg('Asking for users PIN', 1) self.logMsg('Asking for users PIN', 1)
pin = dialog.input( pin = dialog.input(
string(39307) + selected_user, (string(39307) + selected_user).encode('utf-8'),
type=xbmcgui.INPUT_NUMERIC, type=xbmcgui.INPUT_NUMERIC,
option=xbmcgui.ALPHANUM_HIDE_INPUT) option=xbmcgui.ALPHANUM_HIDE_INPUT)
# User chose to cancel # User chose to cancel
@ -1114,6 +1117,9 @@ class PlexAPI():
else: else:
pin = None pin = None
# Switch to this Plex Home user, if applicable # Switch to this Plex Home user, if applicable
# Plex bug: don't call url for protected user with empty PIN
if user['protected'] == '1' and not pin:
break
username, usertoken = self.PlexSwitchHomeUser( username, usertoken = self.PlexSwitchHomeUser(
user['id'], user['id'],
pin, pin,
@ -1122,12 +1128,11 @@ class PlexAPI():
) )
# Couldn't get user auth # Couldn't get user auth
if not username: if not username:
dialog = xbmcgui.Dialog()
# Could not login user, please try again # Could not login user, please try again
if not dialog.yesno( if not dialog.yesno(
self.addonName, self.addonName,
string(39308) + selected_user, (string(39308) + selected_user).encode('utf-8'),
string(39309) string(39309).encode('utf-8')
): ):
# User chose to cancel # User chose to cancel
break break
@ -1158,8 +1163,7 @@ class PlexAPI():
url = 'https://plex.tv/api/home/users/' + userId + '/switch' url = 'https://plex.tv/api/home/users/' + userId + '/switch'
if pin: if pin:
url += '?pin=' + pin url += '?pin=' + pin
self.logMsg('Switching to user %s with url %s and machineId %s' self.logMsg('Switching to user %s' % userId, 0)
% (userId, url, machineId), 0)
answer = self.TalkToPlexServer(url, talkType="POST", token=token) answer = self.TalkToPlexServer(url, talkType="POST", token=token)
if not answer: if not answer:
self.logMsg('Error: plex.tv switch HomeUser change failed', -1) self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
@ -1188,7 +1192,7 @@ class PlexAPI():
self.logMsg("username: %s, token: xxxx. " self.logMsg("username: %s, token: xxxx. "
"Saving to window and file settings" % username, 0) "Saving to window and file settings" % username, 0)
utils.window('emby_currUser', value=userId) utils.window('emby_currUser', value=userId)
utils.settings('userId%s' % username, value=userId) utils.settings('userId', value=userId)
utils.settings('username', value=username) utils.settings('username', value=username)
utils.window('emby_accessToken%s' % userId, value=token) utils.window('emby_accessToken%s' % userId, value=token)
return (username, token) return (username, token)
@ -1411,7 +1415,6 @@ class API():
self.part = 0 self.part = 0
self.clientinfo = clientinfo.ClientInfo() self.clientinfo = clientinfo.ClientInfo()
self.clientId = self.clientinfo.getDeviceId() self.clientId = self.clientinfo.getDeviceId()
self.user = utils.window('plex_username')
self.userId = utils.window('emby_currUser') self.userId = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userId) self.server = utils.window('emby_server%s' % self.userId)
self.token = utils.window('emby_accessToken%s' % self.userId) self.token = utils.window('emby_accessToken%s' % self.userId)

View file

@ -162,7 +162,8 @@ class Artwork():
import xbmcaddon import xbmcaddon
string = xbmcaddon.Addon().getLocalizedString string = xbmcaddon.Addon().getLocalizedString
if not xbmcgui.Dialog().yesno("Image Texture Cache", string(39250)): if not xbmcgui.Dialog().yesno(
"Image Texture Cache", string(39250).encode('utf-8')):
return return
self.logMsg("Doing Image Cache Sync", 1) self.logMsg("Doing Image Cache Sync", 1)
@ -171,11 +172,12 @@ class Artwork():
dialog.create("Emby for Kodi", "Image Cache Sync") dialog.create("Emby for Kodi", "Image Cache Sync")
# ask to rest all existing or not # ask to rest all existing or not
if xbmcgui.Dialog().yesno("Image Texture Cache", string(39251), ""): if xbmcgui.Dialog().yesno(
"Image Texture Cache", string(39251).encode('utf-8'), ""):
self.logMsg("Resetting all cache data first", 1) self.logMsg("Resetting all cache data first", 1)
# Remove all existing textures first # Remove all existing textures first
path = xbmc.translatePath("special://thumbnails/").decode('utf-8') path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
if xbmcvfs.exists(path): if utils.IfExists(path):
allDirs, allFiles = xbmcvfs.listdir(path) allDirs, allFiles = xbmcvfs.listdir(path)
for dir in allDirs: for dir in allDirs:
allDirs, allFiles = xbmcvfs.listdir(path+dir) allDirs, allFiles = xbmcvfs.listdir(path+dir)

View file

@ -5,7 +5,6 @@
import json import json
import os import os
import sys import sys
import urlparse
import xbmc import xbmc
import xbmcaddon import xbmcaddon
@ -156,7 +155,7 @@ def resetAuth():
string = xbmcaddon.Addon().getLocalizedString string = xbmcaddon.Addon().getLocalizedString
resp = xbmcgui.Dialog().yesno( resp = xbmcgui.Dialog().yesno(
heading="Warning", heading="Warning",
line1=string(39206)) line1=string(39206).encode('utf-8'))
if resp == 1: if resp == 1:
utils.logMsg("PLEX", "Reset login attempts.", 1) utils.logMsg("PLEX", "Reset login attempts.", 1)
utils.window('emby_serverStatus', value="Auth") utils.window('emby_serverStatus', value="Auth")
@ -226,14 +225,14 @@ def resetDeviceId():
"Failed to generate a new device Id: %s" % e, 1) "Failed to generate a new device Id: %s" % e, 1)
dialog.ok( dialog.ok(
heading=addonName, heading=addonName,
line1=language(33032)) line1=language(33032).encode('utf-8'))
else: else:
utils.logMsg(addonName, utils.logMsg(addonName,
"Successfully removed old deviceId: %s New deviceId: %s" "Successfully removed old deviceId: %s New deviceId: %s"
% (deviceId_old, deviceId), 1) % (deviceId_old, deviceId), 1)
dialog.ok( dialog.ok(
heading=addonName, heading=addonName,
line1=language(33033)) line1=language(33033).encode('utf-8'))
xbmc.executebuiltin('RestartApp') xbmc.executebuiltin('RestartApp')
##### ADD ADDITIONAL USERS ##### ##### ADD ADDITIONAL USERS #####
@ -399,7 +398,7 @@ def getThemeMedia():
library = xbmc.translatePath( library = xbmc.translatePath(
"special://profile/addon_data/plugin.video.plexkodiconnect/library/").decode('utf-8') "special://profile/addon_data/plugin.video.plexkodiconnect/library/").decode('utf-8')
# Create library directory # Create library directory
if not xbmcvfs.exists(library): if not utils.IfExists(library):
xbmcvfs.mkdir(library) xbmcvfs.mkdir(library)
# Set custom path for user # Set custom path for user
@ -572,10 +571,10 @@ def BrowseContent(viewname, type="", folderid=""):
if not folderid: if not folderid:
views = emby.getViews(type) views = emby.getViews(type)
for view in views: for view in views:
if view.get("name") == viewname: if view.get("name") == viewname.decode('utf-8'):
folderid = view.get("id") folderid = view.get("id")
utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname, type, folderid, filter)) utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname.decode('utf-8'), type.decode('utf-8'), folderid.decode('utf-8'), filter.decode('utf-8')))
#set the correct params for the content type #set the correct params for the content type
#only proceed if we have a folderid #only proceed if we have a folderid
if folderid: if folderid:
@ -584,7 +583,7 @@ def BrowseContent(viewname, type="", folderid=""):
itemtype = "Video,Folder,PhotoAlbum" itemtype = "Video,Folder,PhotoAlbum"
elif type.lower() == "photos": elif type.lower() == "photos":
xbmcplugin.setContent(int(sys.argv[1]), 'files') xbmcplugin.setContent(int(sys.argv[1]), 'files')
itemtype = "Photo,PhotoAlbum" itemtype = "Photo,PhotoAlbum,Folder"
else: else:
itemtype = "" itemtype = ""
@ -610,14 +609,13 @@ def BrowseContent(viewname, type="", folderid=""):
li = createListItemFromEmbyItem(item,art,doUtils) li = createListItemFromEmbyItem(item,art,doUtils)
if item.get("IsFolder") == True: if item.get("IsFolder") == True:
#for folders we add an additional browse request, passing the folderId #for folders we add an additional browse request, passing the folderId
path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0], viewname, type, item.get("Id")) path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), type.decode('utf-8'), item.get("Id").decode('utf-8'))
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True)
else: else:
#playable item, set plugin path and mediastreams #playable item, set plugin path and mediastreams
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=li.getProperty("path"), listitem=li) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=li.getProperty("path"), listitem=li)
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
if filter == "recent": if filter == "recent":
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
else: else:
@ -626,6 +624,8 @@ def BrowseContent(viewname, type="", folderid=""):
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING)
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
##### CREATE LISTITEM FROM EMBY METADATA ##### ##### CREATE LISTITEM FROM EMBY METADATA #####
def createListItemFromEmbyItem(item,art=artwork.Artwork(),doUtils=downloadutils.DownloadUtils()): def createListItemFromEmbyItem(item,art=artwork.Artwork(),doUtils=downloadutils.DownloadUtils()):
API = api.API(item) API = api.API(item)
@ -1066,28 +1066,45 @@ def getRecentEpisodes(tagname, limit):
xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
##### GET VIDEO EXTRAS FOR LISTITEM #####
def getVideoFiles(embyId,embyPath):
#returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc.
emby = embyserver.Read_EmbyServer()
if not embyId:
if "plugin.video.emby" in embyPath:
embyId = embyPath.split("/")[-2]
if embyId:
item = emby.getItem(embyId)
putils = playutils.PlayUtils(item)
if putils.isDirectPlay():
#only proceed if we can access the files directly. TODO: copy local on the fly if accessed outside
filelocation = putils.directPlay()
if not filelocation.endswith("/"):
filelocation = filelocation.rpartition("/")[0]
dirs, files = xbmcvfs.listdir(filelocation)
for file in files:
file = filelocation + file
li = xbmcgui.ListItem(file, path=file)
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=file, listitem=li)
for dir in dirs:
dir = filelocation + dir
li = xbmcgui.ListItem(dir, path=dir)
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=dir, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(int(sys.argv[1]))
##### GET EXTRAFANART FOR LISTITEM ##### ##### GET EXTRAFANART FOR LISTITEM #####
def getExtraFanArt(): def getExtraFanArt(embyId,embyPath):
emby = embyserver.Read_EmbyServer() emby = embyserver.Read_EmbyServer()
art = artwork.Artwork() art = artwork.Artwork()
embyId = ""
# Get extrafanart for listitem # Get extrafanart for listitem
# will be called by skinhelper script to get the extrafanart # will be called by skinhelper script to get the extrafanart
try: try:
# for tvshows we get the embyid just from the path # for tvshows we get the embyid just from the path
if xbmc.getCondVisibility("Container.Content(tvshows) | Container.Content(seasons) | Container.Content(episodes)"): if not embyId:
itemPath = xbmc.getInfoLabel("ListItem.Path").decode('utf-8') if "plugin.video.emby" in embyPath:
if "plugin.video.plexkodiconnect" in itemPath: embyId = embyPath.split("/")[-2]
embyId = itemPath.split("/")[-2]
else:
#for movies we grab the emby id from the params
itemPath = xbmc.getInfoLabel("ListItem.FileNameAndPath").decode('utf-8')
if "plugin.video.plexkodiconnect" in itemPath:
params = urlparse.parse_qs(itemPath)
embyId = params.get('id')
if embyId: embyId = embyId[0]
if embyId: if embyId:
#only proceed if we actually have a emby id #only proceed if we actually have a emby id
@ -1131,7 +1148,7 @@ def getExtraFanArt():
url=fanartFile, url=fanartFile,
listitem=li) listitem=li)
except Exception as e: except Exception as e:
utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 1) utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 0)
# Always do endofdirectory to prevent errors in the logs # Always do endofdirectory to prevent errors in the logs
xbmcplugin.endOfDirectory(int(sys.argv[1])) xbmcplugin.endOfDirectory(int(sys.argv[1]))
@ -1142,6 +1159,6 @@ def RunLibScan(mode):
# Server is not online, do not run the sync # Server is not online, do not run the sync
string = xbmcaddon.Addon().getLocalizedString string = xbmcaddon.Addon().getLocalizedString
xbmcgui.Dialog().ok(heading=addonName, xbmcgui.Dialog().ok(heading=addonName,
line1=string(39205)) line1=string(39205).encode('utf-8'))
else: else:
utils.window('plex_runLibScan', value='full') utils.window('plex_runLibScan', value='full')

View file

@ -20,10 +20,6 @@ import PlexAPI
class InitialSetup(): class InitialSetup():
def __init__(self): def __init__(self):
self.addon = xbmcaddon.Addon()
self.__language__ = self.addon.getLocalizedString
self.clientInfo = clientinfo.ClientInfo() self.clientInfo = clientinfo.ClientInfo()
self.addonId = self.clientInfo.getAddonId() self.addonId = self.clientInfo.getAddonId()
self.doUtils = downloadutils.DownloadUtils() self.doUtils = downloadutils.DownloadUtils()
@ -36,7 +32,7 @@ class InitialSetup():
Check server, user, direct paths, music, direct stream if not direct Check server, user, direct paths, music, direct stream if not direct
path. path.
""" """
string = self.__language__ string = xbmcaddon.Addon().getLocalizedString
# SERVER INFO ##### # SERVER INFO #####
self.logMsg("Initial setup called.", 0) self.logMsg("Initial setup called.", 0)
server = self.userClient.getServer() server = self.userClient.getServer()
@ -63,7 +59,7 @@ class InitialSetup():
# Could not login, please try again # Could not login, please try again
dialog.ok( dialog.ok(
self.addonName, self.addonName,
string(39009) string(39009).encode('utf-8')
) )
result = self.plx.PlexTvSignInWithPin() result = self.plx.PlexTvSignInWithPin()
if result: if result:
@ -74,7 +70,7 @@ class InitialSetup():
# Problems connecting to plex.tv. Network or internet issue? # Problems connecting to plex.tv. Network or internet issue?
dialog.ok( dialog.ok(
self.addonName, self.addonName,
string(39010) string(39010).encode('utf-8')
) )
# If a Plex server IP has already been set, return. # If a Plex server IP has already been set, return.
if server and forcePlexTV is False: if server and forcePlexTV is False:
@ -92,36 +88,40 @@ class InitialSetup():
plexToken = result['token'] plexToken = result['token']
plexid = result['plexid'] plexid = result['plexid']
# Get g_PMS list of servers (saved to plx.g_PMS) # Get g_PMS list of servers (saved to plx.g_PMS)
httpsUpdated = False
while True: while True:
tokenDict = {'MyPlexToken': plexToken} if plexToken else {} if httpsUpdated is False:
# Populate g_PMS variable with the found Plex servers tokenDict = {'MyPlexToken': plexToken} if plexToken else {}
self.plx.discoverPMS(clientId, # Populate g_PMS variable with the found Plex servers
None, self.plx.discoverPMS(clientId,
xbmc.getIPAddress(), None,
tokenDict=tokenDict) xbmc.getIPAddress(),
self.logMsg("Result of setting g_PMS variable: %s" tokenDict=tokenDict)
% self.plx.g_PMS, 2) self.logMsg("Result of setting g_PMS variable: %s"
isconnected = False % self.plx.g_PMS, 1)
serverlist = self.plx.returnServerList(clientId, self.plx.g_PMS) isconnected = False
# Let user pick server from a list serverlist = self.plx.returnServerList(
# Get a nicer list clientId, self.plx.g_PMS)
dialoglist = [] self.logMsg('PMS serverlist: %s' % serverlist)
# Exit if no servers found # Let user pick server from a list
if len(serverlist) == 0: # Get a nicer list
dialog.ok( dialoglist = []
self.addonName, # Exit if no servers found
string(39011) if len(serverlist) == 0:
) dialog.ok(
break self.addonName,
for server in serverlist: string(39011).encode('utf-8')
if server['local'] == '1': )
# server is in the same network as client. Add "local" break
dialoglist.append(str(server['name']) + string(39022)) for server in serverlist:
else: if server['local'] == '1':
dialoglist.append(str(server['name'])) # server is in the same network as client. Add "local"
resp = dialog.select( dialoglist.append(
string(39012), server['name'].encode('utf-8')
dialoglist) + string(39022).encode('utf-8'))
else:
dialoglist.append(server['name'].encode('utf-8'))
resp = dialog.select(string(39012).encode('utf-8'), dialoglist)
server = serverlist[resp] server = serverlist[resp]
activeServer = server['machineIdentifier'] activeServer = server['machineIdentifier']
url = server['scheme'] + '://' + server['ip'] + ':' + \ url = server['scheme'] + '://' + server['ip'] + ':' + \
@ -136,13 +136,19 @@ class InitialSetup():
self.logMsg("Setting SSL verify to true, because server is " self.logMsg("Setting SSL verify to true, because server is "
"not local", 1) "not local", 1)
chk = self.plx.CheckConnection(url, server['accesstoken']) chk = self.plx.CheckConnection(url, server['accesstoken'])
# Unauthorized if chk == 504 and httpsUpdated is False:
# Not able to use HTTP, try HTTPs for now
serverlist[resp]['scheme'] = 'https'
httpsUpdated = True
continue
httpsUpdated = False
if chk == 401: if chk == 401:
# Not yet authorized for Plex server # Not yet authorized for Plex server
# Please sign in to plex.tv # Please sign in to plex.tv
dialog.ok(self.addonName, dialog.ok(self.addonName,
string(39013) + str(server['name']), string(39013).encode('utf-8')
string(39014)) + server['name'].encode('utf-8'),
string(39014).encode('utf-8'))
result = self.plx.PlexTvSignInWithPin() result = self.plx.PlexTvSignInWithPin()
if result: if result:
plexLogin = result['username'] plexLogin = result['username']
@ -154,7 +160,8 @@ class InitialSetup():
# Problems connecting # Problems connecting
elif chk >= 400 or chk is False: elif chk >= 400 or chk is False:
# Problems connecting to server. Pick another server? # Problems connecting to server. Pick another server?
resp = dialog.yesno(self.addonName, string(39015)) resp = dialog.yesno(self.addonName,
string(39015).encode('utf-8'))
# Exit while loop if user chooses No # Exit while loop if user chooses No
if not resp: if not resp:
break break
@ -166,7 +173,7 @@ class InitialSetup():
# Enter Kodi settings instead # Enter Kodi settings instead
if dialog.yesno( if dialog.yesno(
heading=self.addonName, heading=self.addonName,
line1=string(39016)): line1=string(39016).encode('utf-8')):
self.logMsg("User opted to disable Plex music library.", 1) self.logMsg("User opted to disable Plex music library.", 1)
utils.settings('enableMusic', value="false") utils.settings('enableMusic', value="false")
xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId) xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId)
@ -202,12 +209,12 @@ class InitialSetup():
if dialog.yesno( if dialog.yesno(
heading=self.addonName, heading=self.addonName,
line1=string(39016)): line1=string(39016).encode('utf-8')):
self.logMsg("User opted to disable Plex music library.", 1) self.logMsg("User opted to disable Plex music library.", 1)
utils.settings('enableMusic', value="false") utils.settings('enableMusic', value="false")
if dialog.yesno( if dialog.yesno(
heading=self.addonName, heading=self.addonName,
line1=string(39017)): line1=string(39017).encode('utf-8')):
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')

View file

@ -164,10 +164,10 @@ class ThreadedShowSyncInfo(Thread):
threadStopped = self.threadStopped threadStopped = self.threadStopped
downloadLock = self.locks[0] downloadLock = self.locks[0]
processLock = self.locks[1] processLock = self.locks[1]
dialog.create("%s: Sync %s: %s items" dialog.create(("%s: Sync %s: %s items"
% (self.addonName.encode('utf-8'), % (self.addonName,
self.itemType.encode('utf-8'), self.itemType,
str(total)), str(total))).encode('utf-8'),
"Starting") "Starting")
global getMetadataCount global getMetadataCount
global processMetadataCount global processMetadataCount
@ -188,9 +188,9 @@ class ThreadedShowSyncInfo(Thread):
try: try:
dialog.update( dialog.update(
percentage, percentage,
message="Downloaded: %s. Processed: %s: %s" message=("Downloaded: %s. Processed: %s: %s"
% (getMetadataProgress, processMetadataProgress, % (getMetadataProgress, processMetadataProgress,
viewName.decode('utf-8'))) viewName))).encode('utf-8')
except: except:
# Wierd formating of the string viewName?!? # Wierd formating of the string viewName?!?
pass pass
@ -225,6 +225,9 @@ class LibrarySync(Thread):
utils.settings('dbSyncIndicator') == 'true' else False utils.settings('dbSyncIndicator') == 'true' else False
self.enableMusic = True if utils.settings('enableMusic') == "true" \ self.enableMusic = True if utils.settings('enableMusic') == "true" \
else False else False
self.enableBackgroundSync = True if utils.settings(
'enableBackgroundSync') == "true" \
else False
Thread.__init__(self) Thread.__init__(self)
@ -236,7 +239,7 @@ class LibrarySync(Thread):
return return
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=self.addonName, heading=self.addonName,
message=message, message=message.encode('utf-8'),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
sound=False) sound=False)
@ -1253,7 +1256,7 @@ class LibrarySync(Thread):
time=3000, time=3000,
sound=True) sound=True)
window('emby_dbScan', clear=True) window('emby_dbScan', clear=True)
else: elif self.enableBackgroundSync:
# Run full lib scan approx every 30min # Run full lib scan approx every 30min
if count >= 1800: if count >= 1800:
count = 0 count = 0

View file

@ -214,7 +214,7 @@ def getSongTags(file):
except Exception as e: except Exception as e:
#file in use ? #file in use ?
logMsg("Exception in getSongTags %s" %e,0) utils.logMsg("Exception in getSongTags", str(e),0)
rating = None rating = None
#remove tempfile if needed.... #remove tempfile if needed....

View file

@ -523,7 +523,10 @@ class Player(xbmc.Player):
# Plex: never delete # Plex: never delete
offerDelete = False offerDelete = False
if percentComplete >= markPlayedAt and offerDelete: if percentComplete >= markPlayedAt and offerDelete:
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000) resp = xbmcgui.Dialog().yesno(
lang(30091).encode('utf-8'),
lang(33015).encode('utf-8'),
autoclose=120000)
if not resp: if not resp:
log("User skipped deletion.", 1) log("User skipped deletion.", 1)
continue continue

View file

@ -8,7 +8,6 @@ from urllib import urlencode
import xbmc import xbmc
import xbmcgui import xbmcgui
import playbackutils
import embydb_functions as embydb import embydb_functions as embydb
import read_embyserver as embyserver import read_embyserver as embyserver
import utils import utils

View file

@ -343,7 +343,7 @@ class PlayUtils():
#audioStreamsChannelsList[audioNum] = stream.attrib['channels'] #audioStreamsChannelsList[audioNum] = stream.attrib['channels']
audioStreamsList.append(index) audioStreamsList.append(index)
audioStreams.append(track) audioStreams.append(track.encode('utf-8'))
audioNum += 1 audioNum += 1
# Subtitles # Subtitles
@ -367,11 +367,11 @@ class PlayUtils():
downloadableStreams.append(index) downloadableStreams.append(index)
subtitleStreamsList.append(index) subtitleStreamsList.append(index)
subtitleStreams.append(track) subtitleStreams.append(track.encode('utf-8'))
subNum += 1 subNum += 1
if audioNum > 1: if audioNum > 1:
resp = dialog.select(lang(33013), audioStreams) resp = dialog.select(lang(33013).encode('utf-8'), audioStreams)
if resp > -1: if resp > -1:
# User selected audio # User selected audio
playurlprefs['audioStreamID'] = audioStreamsList[resp] playurlprefs['audioStreamID'] = audioStreamsList[resp]
@ -384,7 +384,7 @@ class PlayUtils():
playurlprefs['audioBoost'] = utils.settings('audioBoost') playurlprefs['audioBoost'] = utils.settings('audioBoost')
if subNum > 1: if subNum > 1:
resp = dialog.select(lang(33014), subtitleStreams) resp = dialog.select(lang(33014).encode('utf-8'), subtitleStreams)
if resp == 0: if resp == 0:
# User selected no subtitles # User selected no subtitles
playurlprefs["skipSubtitles"] = 1 playurlprefs["skipSubtitles"] = 1

View file

@ -262,14 +262,14 @@ class Read_EmbyServer():
retry = 0 retry = 0
while utils.window('emby_online') != "true": while utils.window('emby_online') != "true":
# Wait server to come back online # Wait server to come back online
if retry == 3: if retry == 5:
log("Unable to reconnect to server. Abort process.", 1) log("Unable to reconnect to server. Abort process.", 1)
return return items
retry += 1 retry += 1
if xbmc.Monitor().waitForAbort(1): if xbmc.Monitor().waitForAbort(1):
# Abort was requested while waiting. # Abort was requested while waiting.
return return items
else: else:
# Request succeeded # Request succeeded
index += jump index += jump
@ -321,18 +321,18 @@ class Read_EmbyServer():
# Filter view types # Filter view types
continue continue
# 11/10/2015 Review key, when it's added to server. Currently unavailable. # 3/4/2016 OriginalCollectionType is added
itemtype = item.get('OriginalCollectionType', item.get('CollectionType')) itemtype = item.get('OriginalCollectionType', item.get('CollectionType', "mixed"))
# 11/29/2015 Remove this once OriginalCollectionType is added to stable server. # 11/29/2015 Remove this once OriginalCollectionType is added to stable server.
# Assumed missing is mixed then. # Assumed missing is mixed then.
if itemtype is None: '''if itemtype is None:
url = "{server}/emby/Library/MediaFolders?format=json" url = "{server}/emby/Library/MediaFolders?format=json"
result = doUtils(url) result = doUtils(url)
for folder in result['Items']: for folder in result['Items']:
if itemId == folder['Id']: if itemId == folder['Id']:
itemtype = folder.get('CollectionType', "mixed") itemtype = folder.get('CollectionType', "mixed")'''
if name not in ('Collections', 'Trailers'): if name not in ('Collections', 'Trailers'):

View file

@ -54,12 +54,15 @@ class UserClient(threading.Thread):
self.AdditionalUser = additionalUsers.split(',') self.AdditionalUser = additionalUsers.split(',')
def getUsername(self): def getUsername(self):
"""
Returns username as unicode
"""
username = utils.settings('username') username = utils.settings('username').decode('utf-8')
if not username: if not username:
self.logMsg("No username saved, trying to get Plex username", 0) self.logMsg("No username saved, trying to get Plex username", 0)
username = utils.settings('plexLogin') username = utils.settings('plexLogin').decode('utf-8')
if not username: if not username:
self.logMsg("Also no Plex username found", 0) self.logMsg("Also no Plex username found", 0)
return "" return ""
@ -84,33 +87,31 @@ class UserClient(threading.Thread):
if username is None: if username is None:
username = self.getUsername() username = self.getUsername()
w_userId = window('emby_currUser') w_userId = window('emby_currUser')
s_userId = settings('userId%s' % username) s_userId = settings('userId')
# Verify the window property # Verify the window property
if w_userId: if w_userId:
if not s_userId: if not s_userId:
# Save access token if it's missing from settings # Save access token if it's missing from settings
settings('userId%s' % username, value=w_userId) settings('userId', value=w_userId)
log("Returning userId from WINDOW for username: %s UserId: %s" log("Returning userId %s from WINDOW for username %s"
% (username, w_userId), 1) % (w_userId, username), 0)
return w_userId return w_userId
# Verify the settings # Verify the settings
elif s_userId: elif s_userId:
log("Returning userId from SETTINGS for username: %s userId: %s" log("Returning userId %s from SETTINGS for username %s"
% (username, s_userId), 1) % (w_userId, username), 0)
return s_userId return s_userId
# No userId found # No userId found
else: log("No userId saved. Trying to get Plex to use instead", 0)
log("No userId saved for username: %s. Trying to get Plex ID" plexId = settings('plexid')
% username, 0) if not plexId:
plexId = settings('plexid') log('Also no Plex ID found in settings', 0)
if not plexId: return ''
log('Also no Plex ID found in settings', 0) log('Using Plex ID %s as userid for username %s'
return '' % (plexId, username), 0)
log('Using Plex ID %s as userid for username: %s' settings('userId', value=plexId)
% (plexId, username)) return plexId
settings('userId%s' % username, value=plexId)
return plexId
def getServer(self, prefix=True): def getServer(self, prefix=True):
@ -157,14 +158,14 @@ class UserClient(threading.Thread):
if not s_token: if not s_token:
# Save access token if it's missing from settings # Save access token if it's missing from settings
settings('accessToken', value=w_token) settings('accessToken', value=w_token)
log("Returning accessToken from WINDOW for username: %s accessToken: %s" log("Returning accessToken from WINDOW for username: %s "
% (username, w_token), 2) "accessToken: xxxx" % username, 2)
return w_token return w_token
# Verify the settings # Verify the settings
elif s_token: elif s_token:
log("Returning accessToken from SETTINGS for username: %s accessToken: %s" log("Returning accessToken from SETTINGS for username: %s "
% (username, s_token), 2) "accessToken: xxxx" % username, 2)
window('emby_accessToken%s' % username, value=s_token) window('emby_accessToken%s' % userId, value=s_token)
return s_token return s_token
else: else:
log("No token found.", 1) log("No token found.", 1)
@ -244,7 +245,9 @@ class UserClient(threading.Thread):
log("Access is granted.", 1) log("Access is granted.", 1)
self.HasAccess = True self.HasAccess = True
window('emby_serverStatus', clear=True) window('emby_serverStatus', clear=True)
xbmcgui.Dialog().notification(self.addonName, utils.language(33007)) xbmcgui.Dialog().notification(
self.addonName,
utils.language(33007).encode('utf-8'))
def loadCurrUser(self, authenticated=False): def loadCurrUser(self, authenticated=False):
self.logMsg('Loading current user', 0) self.logMsg('Loading current user', 0)
@ -284,7 +287,6 @@ class UserClient(threading.Thread):
window('plex_username', value=username) window('plex_username', value=username)
window('emby_accessToken%s' % userId, value=self.currToken) window('emby_accessToken%s' % userId, value=self.currToken)
window('emby_server%s' % userId, value=self.currServer) window('emby_server%s' % userId, value=self.currServer)
window('emby_server_%s' % userId, value=self.getServer(prefix=False))
window('plex_machineIdentifier', value=self.machineIdentifier) window('plex_machineIdentifier', value=self.machineIdentifier)
window('emby_serverStatus', clear=True) window('emby_serverStatus', clear=True)
@ -359,9 +361,8 @@ class UserClient(threading.Thread):
# Check connection # Check connection
if plx.CheckConnection(server, accessToken) == 200: if plx.CheckConnection(server, accessToken) == 200:
self.currUser = username self.currUser = username
dialog = xbmcgui.Dialog()
settings('accessToken', value=accessToken) settings('accessToken', value=accessToken)
settings('userId%s' % username, value=userId) settings('userId', value=userId)
log("User authenticated with an access token", 1) log("User authenticated with an access token", 1)
if self.loadCurrUser(authenticated=True) is False: if self.loadCurrUser(authenticated=True) is False:
# Something went really wrong, return and try again # Something went really wrong, return and try again
@ -372,7 +373,7 @@ class UserClient(threading.Thread):
if username: if username:
dialog.notification( dialog.notification(
heading=self.addonName, heading=self.addonName,
message="Welcome %s" % username.decode('utf-8'), message=("Welcome " + username).encode('utf-8'),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png") icon="special://home/addons/plugin.video.plexkodiconnect/icon.png")
else: else:
dialog.notification( dialog.notification(
@ -384,13 +385,14 @@ class UserClient(threading.Thread):
else: else:
self.logMsg("Error: user authentication failed.", -1) self.logMsg("Error: user authentication failed.", -1)
settings('accessToken', value="") settings('accessToken', value="")
settings('userId%s' % username, value="") settings('userId', value="")
# Give attempts at entering password / selecting user # Give attempts at entering password / selecting user
if self.retry >= 5: if self.retry >= 5:
log("Too many retries.", 1) log("Too many retries.", 1)
window('emby_serverStatus', value="Stop") window('emby_serverStatus', value="Stop")
dialog.ok(lang(33001), lang(39023)) dialog.ok(lang(33001).encode('utf-8'),
lang(39023).encode('utf-8'))
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')

View file

@ -25,6 +25,27 @@ import xbmcvfs
addonName = xbmcaddon.Addon().getAddonInfo('name') addonName = xbmcaddon.Addon().getAddonInfo('name')
def IfExists(path):
"""
Kodi's xbmcvfs.exists is broken - it caches the results for directories.
path: path to a directory (with a slash at the end)
Returns True if path exists, else false
"""
dummyfile = os.path.join(path, 'dummyfile.txt').encode('utf-8')
try:
etree.ElementTree(etree.Element('test')).write(dummyfile)
except:
# folder does not exist yet
answer = False
else:
# Folder exists. Delete file again.
xbmcvfs.delete(dummyfile)
answer = True
return answer
def LogTime(func): def LogTime(func):
""" """
Decorator for functions and methods to log the time it took to run the code Decorator for functions and methods to log the time it took to run the code
@ -207,18 +228,26 @@ def window(property, value=None, clear=False, windowid=10000):
if clear: if clear:
WINDOW.clearProperty(property) WINDOW.clearProperty(property)
elif value is not None: elif value is not None:
# Takes unicode or string by default!
WINDOW.setProperty(property, value) WINDOW.setProperty(property, value)
else: #getproperty returns string so convert to unicode else: #getproperty returns string so convert to unicode
return WINDOW.getProperty(property)#.decode("utf-8") return WINDOW.getProperty(property)
def settings(setting, value=None): def settings(setting, value=None):
# Get or add addon setting """
Get or add addon setting.
Settings needs to be string
Value can either be unicode or string
"""
addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect') addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
if value is not None: if value is not None:
# Takes string or unicode by default!
addon.setSetting(setting, value) addon.setSetting(setting, value)
else: else:
return addon.getSetting(setting) #returns unicode object # Returns unicode by default!
return addon.getSetting(setting)
def language(stringid): def language(stringid):
# Central string retrieval # Central string retrieval
@ -410,8 +439,8 @@ def reset():
addon = xbmcaddon.Addon() addon = xbmcaddon.Addon()
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8') addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
dataPath = "%ssettings.xml" % addondir dataPath = "%ssettings.xml" % addondir
xbmcvfs.delete(dataPath) xbmcvfs.delete(dataPath.encode('utf-8'))
logMsg("EMBY", "Deleting: settings.xml", 1) logMsg("PLEX", "Deleting: settings.xml", 1)
dialog.ok( dialog.ok(
heading="Emby for Kodi", heading="Emby for Kodi",
@ -668,9 +697,9 @@ def passwordsXML():
sound=False) sound=False)
def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False): def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
# Tagname is in unicode - actions: add or delete """
tagname = tagname.encode('utf-8') Feed with tagname as unicode
"""
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8') path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
if viewtype == "mixed": if viewtype == "mixed":
plname = "%s - %s" % (tagname, mediatype) plname = "%s - %s" % (tagname, mediatype)
@ -680,15 +709,15 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
xsppath = "%sPlex %s.xsp" % (path, viewid) xsppath = "%sPlex %s.xsp" % (path, viewid)
# Create the playlist directory # Create the playlist directory
if not xbmcvfs.exists(path): if not xbmcvfs.exists(path.encode('utf-8')):
logMsg("PLEX", "Creating directory: %s" % path, 1) logMsg("PLEX", "Creating directory: %s" % path, 1)
xbmcvfs.mkdirs(path) xbmcvfs.mkdirs(path.encode('utf-8'))
# Only add the playlist if it doesn't already exists # Only add the playlist if it doesn't already exists
if xbmcvfs.exists(xsppath): if xbmcvfs.exists(xsppath.encode('utf-8')):
logMsg('Path %s does exist' % xsppath, 1)
if delete: if delete:
xbmcvfs.delete(xsppath) xbmcvfs.delete(xsppath.encode('utf-8'))
logMsg("PLEX", "Successfully removed playlist: %s." % tagname, 1) logMsg("PLEX", "Successfully removed playlist: %s." % tagname, 1)
return return
@ -699,21 +728,22 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
} }
logMsg("Plex", "Writing playlist file to: %s" % xsppath, 1) logMsg("Plex", "Writing playlist file to: %s" % xsppath, 1)
try: try:
f = xbmcvfs.File(xsppath, 'w') f = xbmcvfs.File(xsppath.encode('utf-8'), 'wb')
except: except:
logMsg("Plex", "Failed to create playlist: %s" % xsppath, -1) logMsg("Plex", "Failed to create playlist: %s" % xsppath, -1)
return return
else: else:
f.write( f.write((
'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n'
'<smartplaylist type="%s">\n\t' '<smartplaylist type="%s">\n\t'
'<name>Plex %s</name>\n\t' '<name>Plex %s</name>\n\t'
'<match>all</match>\n\t' '<match>all</match>\n\t'
'<rule field="tag" operator="is">\n\t\t' '<rule field="tag" operator="is">\n\t\t'
'<value>%s</value>\n\t' '<value>%s</value>\n\t'
'</rule>' '</rule>\n'
'</smartplaylist>' '</smartplaylist>'
% (itemtypes.get(mediatype, mediatype), plname, tagname)) % (itemtypes.get(mediatype, mediatype), plname, tagname))
.encode('utf-8'))
f.close() f.close()
logMsg("Plex", "Successfully added playlist: %s" % tagname) logMsg("Plex", "Successfully added playlist: %s" % tagname)
@ -721,26 +751,26 @@ def deletePlaylists():
# Clean up the playlists # Clean up the playlists
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8') path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
dirs, files = xbmcvfs.listdir(path) dirs, files = xbmcvfs.listdir(path.encode('utf-8'))
for file in files: for file in files:
if file.decode('utf-8').startswith('Emby'): if file.decode('utf-8').startswith('Plex'):
xbmcvfs.delete("%s%s" % (path, file)) xbmcvfs.delete(("%s%s" % (path, file.decode('utf-8'))).encode('utf-8'))
def deleteNodes(): def deleteNodes():
# Clean up video nodes # Clean up video nodes
import shutil import shutil
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8') path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
dirs, files = xbmcvfs.listdir(path) dirs, files = xbmcvfs.listdir(path.encode('utf-8'))
for dir in dirs: for dir in dirs:
if dir.decode('utf-8').startswith('Emby'): if dir.decode('utf-8').startswith('Plex'):
try: try:
shutil.rmtree("%s%s" % (path, dir.decode('utf-8'))) shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
except: except:
logMsg("EMBY", "Failed to delete directory: %s" % dir.decode('utf-8')) logMsg("PLEX", "Failed to delete directory: %s" % dir.decode('utf-8'))
for file in files: for file in files:
if file.decode('utf-8').startswith('emby'): if file.decode('utf-8').startswith('plex'):
try: try:
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8'))) xbmcvfs.delete(("%s%s" % (path, file.decode('utf-8'))).encode('utf-8'))
except: except:
logMsg("EMBY", "Failed to file: %s" % file.decode('utf-8')) logMsg("PLEX", "Failed to file: %s" % file.decode('utf-8'))

View file

@ -4,6 +4,7 @@
import shutil import shutil
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from os import path as ospath
import xbmc import xbmc
import xbmcvfs import xbmcvfs
@ -41,7 +42,6 @@ class VideoNodes(object):
return root return root
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False): def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False):
# Plex: reassign mediatype due to Kodi inner workings # Plex: reassign mediatype due to Kodi inner workings
mediatypes = { mediatypes = {
'movie': 'movies', 'movie': 'movies',
@ -65,24 +65,29 @@ class VideoNodes(object):
"special://profile/library/video/Plex-%s/" % dirname).decode('utf-8') "special://profile/library/video/Plex-%s/" % dirname).decode('utf-8')
# Verify the video directory # Verify the video directory
if not xbmcvfs.exists(path): # KODI BUG
# Kodi caches the result of exists for directories
# so try creating a file
if utils.IfExists(path) is False:
shutil.copytree( shutil.copytree(
src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'), src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
dst=xbmc.translatePath("special://profile/library/video").decode('utf-8')) dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
xbmcvfs.exists(path)
# Create the node directory # Create the node directory
if not xbmcvfs.exists(nodepath) and not mediatype == "photo": if mediatype != "photo":
# We need to copy over the default items if utils.IfExists(nodepath) is False:
xbmcvfs.mkdirs(nodepath) # folder does not exist yet
else: self.logMsg('Creating folder %s' % nodepath, 1)
if delete: xbmcvfs.mkdirs(nodepath.encode('utf-8'))
dirs, files = xbmcvfs.listdir(nodepath) if delete:
for file in files: dirs, files = xbmcvfs.listdir(nodepath.encode('utf-8'))
xbmcvfs.delete(nodepath + file) for file in files:
xbmcvfs.delete(
(nodepath + file.decode('utf-8')).encode('utf-8'))
self.logMsg("Sucessfully removed videonode: %s." % tagname, 1) self.logMsg("Sucessfully removed videonode: %s."
return % tagname, 1)
return
# Create index entry # Create index entry
nodeXML = "%sindex.xml" % nodepath nodeXML = "%sindex.xml" % nodepath
@ -239,7 +244,7 @@ class VideoNodes(object):
# To do: add our photos nodes to kodi picture sources somehow # To do: add our photos nodes to kodi picture sources somehow
continue continue
if xbmcvfs.exists(nodeXML): if xbmcvfs.exists(nodeXML.encode('utf-8')):
# Don't recreate xml if already exists # Don't recreate xml if already exists
continue continue

View file

@ -51,6 +51,7 @@
<setting id="syncEmptyShows" type="bool" label="30508" default="false" visible="false"/> <setting id="syncEmptyShows" type="bool" label="30508" default="false" visible="false"/>
<setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" visible="false"/> <!-- Playback mode --> <setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" visible="false"/> <!-- Playback mode -->
<setting id="enableMusic" type="bool" label="30509" default="true" /> <setting id="enableMusic" type="bool" label="30509" default="true" />
<setting id="enableBackgroundSync" type="bool" label="39026" default="true" visible="true"/>
<setting id="streamMusic" type="bool" label="30510" default="false" visible="false" subsetting="true"/> <!-- Direct stream Music library --> <setting id="streamMusic" type="bool" label="30510" default="false" visible="false" subsetting="true"/> <!-- Direct stream Music library -->
<setting type="lsep" label="30523" visible="false"/> <!-- Music metadata options --> <setting type="lsep" label="30523" visible="false"/> <!-- Music metadata options -->
<setting id="enableImportSongRating" type="bool" label="30524" default="true" visible="false"/> <setting id="enableImportSongRating" type="bool" label="30524" default="true" visible="false"/>

View file

@ -171,9 +171,8 @@ class Service():
self.welcome_msg = False self.welcome_msg = False
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=self.addonName, heading=self.addonName,
message=("%s %s" message=("%s %s" % (lang(33000), user.currUser)
% (lang(33000), ).encode('utf-8'),
user.currUser.decode('utf-8'))),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
time=2000, time=2000,
sound=False) sound=False)
@ -226,8 +225,8 @@ class Service():
window('emby_online', value="false") window('emby_online', value="false")
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=lang(33001), heading=lang(33001).encode('utf-8'),
message="%s %s" % (self.addonName, lang(33002)), message=("%s %s" % (self.addonName, lang(33002))).encode('utf-8'),
icon="special://home/addons/plugin.video." icon="special://home/addons/plugin.video."
"plexkodiconnect/icon.png", "plexkodiconnect/icon.png",
sound=False) sound=False)
@ -245,7 +244,7 @@ class Service():
# Alert the user that server is online. # Alert the user that server is online.
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=self.addonName, heading=self.addonName,
message=lang(33003), message=lang(33003).encode('utf-8'),
icon="special://home/addons/plugin.video." icon="special://home/addons/plugin.video."
"plexkodiconnect/icon.png", "plexkodiconnect/icon.png",
time=2000, time=2000,