Merge branch 'develop'

This commit is contained in:
tomkat83 2016-03-08 18:35:52 +01:00
commit 8363c04ae3
21 changed files with 347 additions and 216 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="1.0.3" version="1.0.4"
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,14 @@
version 1.0.4
- Sleep for a while in loops - drastically reduces CPU load
- Connect to remote PMS!
- New Setting to reset all PMS and the plex.tv connection
- Correct encoding
- Much shorter download timeouts
- Improve sync resiliance and GDM discovery
- Reduce number of unsuccesful retries to 3 before telling user
- Clean-up library sync loop
- Language strings for library sync
version 1.0.3 version 1.0.3
- Hotfix database minimum version = 1.0.2 - Hotfix database minimum version = 1.0.2

View file

@ -66,7 +66,7 @@ class Main:
'companion': entrypoint.plexCompanion, 'companion': entrypoint.plexCompanion,
'switchuser': entrypoint.switchPlexUser, 'switchuser': entrypoint.switchPlexUser,
'deviceid': entrypoint.resetDeviceId, 'deviceid': entrypoint.resetDeviceId,
'doPlexTvLogin': entrypoint.doPlexTvLogin 'reConnect': entrypoint.reConnect
} }
if "/extrafanart" in sys.argv[0]: if "/extrafanart" in sys.argv[0]:

View file

@ -362,7 +362,7 @@
<string id="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</string> <string id="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</string>
<string id="39022"> (local)</string> <string id="39022"> (local)</string>
<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]Reset PMS and plex.tv connections to re-login[/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> <string id="39026">Enable constant background sync (restart Kodi!)</string>
@ -375,6 +375,9 @@
<string id="39204">Perform manual library sync</string> <string id="39204">Perform manual library sync</string>
<string id="39205">Unable to run the sync, the add-on is not connected to a Plex server.</string> <string id="39205">Unable to run the sync, the add-on is not connected to a Plex server.</string>
<string id="39206">Plex might lock your account if you fail to log in too many times. Proceed anyway?</string> <string id="39206">Plex might lock your account if you fail to log in too many times. Proceed anyway?</string>
<string id="39207">Reseting PMS connections, please wait</string>
<string id="39208">Failed to reset PMS and plex.tv connects. Try to restart Kodi.</string>
<string id="39209">[COLOR yellow]Log-in to plex.tv[/COLOR]</string>
<!-- Plex Artwork.py --> <!-- Plex Artwork.py -->
@ -393,4 +396,13 @@
<string id="39308">Could not log in user </string> <string id="39308">Could not log in user </string>
<string id="39309">Please try again.</string> <string id="39309">Please try again.</string>
<!-- Plex Librarysync.py -->
<string id="39400">Library sync thread has crashed. You should restart Kodi now. Please report this on the forum</string>
<string id="39401">Detected Kodi database needs to be recreated for this version. This might take a while. Proceed?</string>
<string id="39402"> may not work correctly until the database is reset.</string>
<string id="39403">Cancelling the database syncing process. Current Kodi version is unsupported. Please verify your logs for more info.</string>
<string id="39404">Startup syncing process failed repeatedly. Try restarting Kodi. Stopping Sync for now.</string>
<string id="39405">Plex playlists/nodes refreshed</string>
<string id="39406">Plex playlists/nodes refresh failed</string>
</strings> </strings>

View file

@ -293,7 +293,7 @@
<string id="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</string> <string id="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</string>
<string id="39022"> (lokal)</string> <string id="39022"> (lokal)</string>
<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]PMS und plex.tv Verbindungen zurücksetzen für erneuten Login[/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> <string id="39026">Laufende Synchronisierung im Hintergrund aktivieren (Neustart!)</string>
@ -305,6 +305,11 @@
<string id="39204">Manuellen Scan der Plex Bibliotheken starten</string> <string id="39204">Manuellen Scan der Plex Bibliotheken starten</string>
<string id="39205">Plex Bibliothek kann nicht gescannt werden, da keine Verbindung mit einem Plex Server besteht.</string> <string id="39205">Plex Bibliothek kann nicht gescannt werden, da keine Verbindung mit einem Plex Server besteht.</string>
<string id="39206">Plex könnte möglicherweise Ihren Account sperren, wenn Sie zu oft versuchen, sich erfolglos anzumelden. Trotzdem fortfahren?</string> <string id="39206">Plex könnte möglicherweise Ihren Account sperren, wenn Sie zu oft versuchen, sich erfolglos anzumelden. Trotzdem fortfahren?</string>
<string id="39207">PMS Verbindungen werden zurückgesetzt</string>
<string id="39208">PMS und plex.tv Verbindungen konnten nicht zurückgesetzt werden. Bitte versuchen Sie, Kodi neu zu starten, um das Problem zu beheben.</string>
<string id="39209">[COLOR yellow]Bei plex.tv einloggen[/COLOR]</string>
<!-- Plex Artwork.py --> <!-- Plex Artwork.py -->
<string id="39250">Alle Plex Bilder in Kodi zwischenzuspeichern kann sehr lange dauern. Möchten Sie wirklich fortfahren?</string> <string id="39250">Alle Plex Bilder in Kodi zwischenzuspeichern kann sehr lange dauern. Möchten Sie wirklich fortfahren?</string>
@ -322,4 +327,13 @@
<string id="39308">Anmeldung fehlgeschlagen für Benutzer </string> <string id="39308">Anmeldung fehlgeschlagen für Benutzer </string>
<string id="39309">Bitte erneut versuchen.</string> <string id="39309">Bitte erneut versuchen.</string>
<!-- Plex Librarysync.py -->
<string id="39400">Die Synchronisierung der Plex Bibliotheken ist abgestürzt. Bitte Kodi neu starten. Danke, wenn Sie sich die Zeit nehmen und im Plex Forum vom Absturz berichten.</string>
<string id="39401">Die Kodi Datenbank muss neu kreiert werden für diese Version. Das kann eine Weile dauern. Fortfahren?</string>
<string id="39402"> funktioniert möglicherweise nicht richtig, bis die Kodi Datenbank zurückgesetzt worden ist.</string>
<string id="39403">Synchronisierung der Plex Bibliotheken wird abgebrochen. Die momentane Kodi Version wird nicht unterstützt. Für weitere Informationen bitte das Kodi Log konsultieren.</string>
<string id="39404">Der Startup Synchronisations-Prozess der Plex Bibliotheken ist mehrmals fehlgeschlagen. Bitte Kodi neu starten. Synch wird jetzt gestoppt.</string>
<string id="39405">Plex Playlisten/Nodes aktualisiert</string>
<string id="39406">Plex Playlisten/Nodes Aktualisierung fehlgeschlagen</string>
</strings> </strings>

View file

@ -55,7 +55,7 @@ import re
import json import json
from urllib import urlencode, quote_plus from urllib import urlencode, quote_plus
from PlexFunctions import PlexToKodiTimefactor from PlexFunctions import PlexToKodiTimefactor, PMSHttpsEnabled
try: try:
import xml.etree.cElementTree as etree import xml.etree.cElementTree as etree
@ -131,17 +131,14 @@ class PlexAPI():
dialog = xbmcgui.Dialog() dialog = xbmcgui.Dialog()
while retrievedPlexLogin == '' and plexLogin != '': while retrievedPlexLogin == '' and plexLogin != '':
# 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 != "":
# Enter password for plex.tv user # Enter password for plex.tv user
plexPassword = dialog.input( plexPassword = dialog.input(
string(39301).encode('utf-8') + plexLogin.encode('utf-8'), string(39301) + plexLogin,
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,
@ -151,8 +148,7 @@ class PlexAPI():
if plexLogin == '': if plexLogin == '':
# Could not sign in user # Could not sign in user
dialog.ok(self.addonName, dialog.ok(self.addonName,
string(39302).encode('utf-8') string(39302) + plexLogin)
+ 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 +173,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).encode('utf-8')) dialog.ok(self.addonName, string(39303))
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").encode('utf-8'), string(39304) + "\n\n",
code.encode('utf-8')) code)
if not answer: if not answer:
return False return False
count = 0 count = 0
@ -196,7 +192,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).encode('utf-8')) dialog.ok(self.addonName, string(39305))
return False return False
# Parse xml # Parse xml
userid = xml.attrib.get('id') userid = xml.attrib.get('id')
@ -300,12 +296,12 @@ class PlexAPI():
timeout=timeout) timeout=timeout)
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
self.logMsg("Server is offline or cannot be reached. Url: %s. " self.logMsg("Server is offline or cannot be reached. Url: %s. "
"Header: %s. Error message: %s" "Error message: %s"
% (url, header, e), -1) % (url, e), -1)
return False return False
except requests.exceptions.ReadTimeout: except requests.exceptions.ReadTimeout:
self.logMsg("Server timeout reached for Url %s with header %s" self.logMsg("Server timeout reached for Url %s"
% (url, header), -1) % url, -1)
return False return False
# We received an answer from the server, but not as expected. # We received an answer from the server, but not as expected.
if answer.status_code >= 400: if answer.status_code >= 400:
@ -634,6 +630,12 @@ class PlexAPI():
self.getPMSListFromMyPlex(ATV_udid, authtoken) self.getPMSListFromMyPlex(ATV_udid, authtoken)
# all servers - update enableGzip # all servers - update enableGzip
for uuid_id in self.g_PMS.get(ATV_udid, {}): for uuid_id in self.g_PMS.get(ATV_udid, {}):
# Ping to check whether we need HTTPs or HTTP
url = (self.getPMSProperty(ATV_udid, uuid_id, 'ip') + ':'
+ self.getPMSProperty(ATV_udid, uuid_id, 'port'))
if PMSHttpsEnabled(url):
self.logMsg('PMS %s talks HTTPS' % uuid_id, 1)
self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'https')
# enable Gzip if not on same host, local&remote PMS depending # enable Gzip if not on same host, local&remote PMS depending
# on setting # on setting
enableGzip = (not self.getPMSProperty(ATV_udid, uuid_id, 'ip') == IP_self) \ enableGzip = (not self.getPMSProperty(ATV_udid, uuid_id, 'ip') == IP_self) \
@ -1089,7 +1091,7 @@ class PlexAPI():
if usernumber > 1: if usernumber > 1:
# Select user # Select user
user_select = dialog.select( user_select = dialog.select(
(self.addonName + string(39306)).encode('utf-8'), self.addonName + string(39306),
userlistCoded) userlistCoded)
if user_select == -1: if user_select == -1:
self.logMsg("No user selected.", 1) self.logMsg("No user selected.", 1)
@ -1108,7 +1110,7 @@ class PlexAPI():
# 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).encode('utf-8'), string(39307) + selected_user,
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
@ -1129,11 +1131,9 @@ class PlexAPI():
# Couldn't get user auth # Couldn't get user auth
if not username: if not username:
# 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
# Successfully retrieved: break out of while loop # Successfully retrieved: break out of while loop

View file

@ -87,9 +87,11 @@ class PlexCompanion(threading.Thread):
subscribers.subMgr.notify() subscribers.subMgr.notify()
settings['serverList'] = self.client.getServerList() settings['serverList'] = self.client.getServerList()
xbmc.sleep(50)
except: except:
self.logMsg("Error in loop, continuing anyway", 1) self.logMsg("Error in loop, continuing anyway", 1)
self.logMsg(traceback.print_exc(), 1) self.logMsg(traceback.print_exc(), 1)
xbmc.sleep(50)
self.client.stop_all() self.client.stop_all()
try: try:

View file

@ -347,3 +347,24 @@ def getPlexRepeat(kodiRepeat):
'all': '2' # does this work?!? 'all': '2' # does this work?!?
} }
return plexRepeat.get(kodiRepeat) return plexRepeat.get(kodiRepeat)
def PMSHttpsEnabled(url):
"""
Returns True if the PMS wants to talk https, False otherwise
With with e.g. url=192.168.0.1:32400 (NO http/https)
This is done by GET /identity (returns an error if https is enabled and we
are trying to use http)
"""
xml = downloadutils.DownloadUtils().downloadUrl('http://%s/identity' % url)
try:
# received a valid XML - http connection is possible
xml.attrib
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTP' % url, 1)
return False
except:
# couldn't get an xml - switch to https traffic
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1)
return True

View file

@ -37,7 +37,7 @@ class DownloadUtils():
# Requests session # Requests session
s = None s = None
timeout = 30 timeout = 3
def __init__(self): def __init__(self):
@ -359,7 +359,7 @@ class DownloadUtils():
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
# Make the addon aware of status # Make the addon aware of status
if utils.window('emby_online') != "false": if utils.window('emby_online') != "false":
self.logMsg("Server unreachable at: %s" % url, 0) self.logMsg("Server unreachable at: %s" % url, -1)
self.logMsg(e, 2) self.logMsg(e, 2)
utils.window('emby_online', value="false") utils.window('emby_online', value="false")

View file

@ -72,18 +72,48 @@ def plexCompanion(fullurl, params):
title, "Not knowing what to do for now - no playQueue sent", -1) title, "Not knowing what to do for now - no playQueue sent", -1)
def doPlexTvLogin(): def reConnect():
""" """
Triggers login to plex.tv Triggers login to plex.tv and re-authorization
""" """
string = xbmcaddon.Addon().getLocalizedString
utils.logMsg("entrypoint reConnect",
"Connection resets requested", 0)
# Pause library sync thread - user needs to be auth in order to sync
utils.window('suspend_LibraryThread', value='true')
# Suspend the user client during procedure # Suspend the user client during procedure
utils.window('suspend_Userclient', value='true') utils.window('suspend_Userclient', value='true')
dialog = xbmcgui.Dialog()
dialog.notification(
heading=addonName,
message=string(39207),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
sound=False)
# Wait max for 20 seconds for all lib scans to finish
counter = 0
while utils.window('emby_dbScan') == 'true':
xbmc.sleep(1000)
counter += 1
if counter > 20:
dialog.ok(
heading=addonName,
message=string(39208),
)
# Resuming threads, just in case
utils.window('suspend_LibraryThread', clear=True)
utils.window('suspend_Userclient', clear=True)
# Abort reConnection
return
import initialsetup import initialsetup
initialsetup.InitialSetup().setup(forcePlexTV=True) initialsetup.InitialSetup().setup(forcePlexTV=True)
utils.logMsg("PLEX", "Reset login attempts.", 1) # Log out currently signed in user:
utils.window('emby_serverStatus', value="Auth") utils.window('emby_serverStatus', value="401")
# Restart user client # Restart user client
utils.window('suspend_Userclient', clear=True) utils.window('suspend_Userclient', clear=True)
# Request lib sync to get user view data (e.g. watched/unwatched)
utils.window('plex_runLibScan', value='full')
def PassPlaylist(xml, resume=None): def PassPlaylist(xml, resume=None):
@ -155,7 +185,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).encode('utf-8')) line1=string(39206))
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")
@ -225,14 +255,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).encode('utf-8')) line1=language(33032))
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).encode('utf-8')) line1=language(33033))
xbmc.executebuiltin('RestartApp') xbmc.executebuiltin('RestartApp')
##### ADD ADDITIONAL USERS ##### ##### ADD ADDITIONAL USERS #####
@ -1159,6 +1189,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).encode('utf-8')) line1=string(39205))
else: else:
utils.window('plex_runLibScan', value='full') utils.window('plex_runLibScan', value='full')

View file

@ -40,7 +40,7 @@ class image_cache_thread(threading.Thread):
"http://%s:%s/image/image://%s" "http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, self.urlToProcess)), % (self.xbmc_host, self.xbmc_port, self.urlToProcess)),
auth=(self.xbmc_username, self.xbmc_password), auth=(self.xbmc_username, self.xbmc_password),
timeout=(35.1, 35.1)) timeout=(0.1, 0.1))
# We don't need the result # We don't need the result
except: pass except: pass

View file

@ -44,7 +44,7 @@ class InitialSetup():
plexLogin = plexdict['plexLogin'] plexLogin = plexdict['plexLogin']
plexToken = plexdict['plexToken'] plexToken = plexdict['plexToken']
plexid = plexdict['plexid'] plexid = plexdict['plexid']
self.logMsg('Plex info retrieved from settings: %s' % plexdict, 1) self.logMsg('Plex info retrieved from settings', 1)
dialog = xbmcgui.Dialog() dialog = xbmcgui.Dialog()
@ -57,10 +57,8 @@ class InitialSetup():
# Delete token in the settings # Delete token in the settings
utils.settings('plexToken', value='') utils.settings('plexToken', value='')
# 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:
plexLogin = result['username'] plexLogin = result['username']
@ -68,10 +66,8 @@ class InitialSetup():
plexid = result['plexid'] plexid = result['plexid']
elif chk is False or chk >= 400: elif chk is False or chk >= 400:
# 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:
self.logMsg("Server is already set.", 0) self.logMsg("Server is already set.", 0)
@ -100,8 +96,8 @@ class InitialSetup():
self.logMsg("Result of setting g_PMS variable: %s" self.logMsg("Result of setting g_PMS variable: %s"
% self.plx.g_PMS, 1) % self.plx.g_PMS, 1)
isconnected = False isconnected = False
serverlist = self.plx.returnServerList( serverlist = self.plx.returnServerList(clientId,
clientId, self.plx.g_PMS) self.plx.g_PMS)
self.logMsg('PMS serverlist: %s' % serverlist) self.logMsg('PMS serverlist: %s' % serverlist)
# Let user pick server from a list # Let user pick server from a list
# Get a nicer list # Get a nicer list
@ -110,22 +106,27 @@ class InitialSetup():
if len(serverlist) == 0: if len(serverlist) == 0:
dialog.ok( dialog.ok(
self.addonName, self.addonName,
string(39011).encode('utf-8') string(39011)
) )
break break
for server in serverlist: for server in serverlist:
if server['local'] == '1': if server['local'] == '1':
# server is in the same network as client. Add "local" # server is in the same network as client. Add "local"
dialoglist.append( dialoglist.append(
server['name'].encode('utf-8') server['name']
+ string(39022).encode('utf-8')) + string(39022))
else: else:
dialoglist.append(server['name'].encode('utf-8')) dialoglist.append(server['name'])
resp = dialog.select(string(39012).encode('utf-8'), dialoglist) resp = dialog.select(string(39012), dialoglist)
server = serverlist[resp] server = serverlist[resp]
activeServer = server['machineIdentifier'] activeServer = server['machineIdentifier']
url = server['scheme'] + '://' + server['ip'] + ':' + \ # Re-direct via plex if remote - will lead to the correct SSL
server['port'] # certificate
if server['local'] == '1':
url = server['scheme'] + '://' + server['ip'] + ':' \
+ server['port']
else:
url = server['baseURL']
# Deactive SSL verification if the server is local! # Deactive SSL verification if the server is local!
if server['local'] == '1': if server['local'] == '1':
utils.settings('sslverify', 'false') utils.settings('sslverify', 'false')
@ -146,9 +147,8 @@ class InitialSetup():
# 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).encode('utf-8') string(39013) + server['name'],
+ server['name'].encode('utf-8'), string(39014))
string(39014).encode('utf-8'))
result = self.plx.PlexTvSignInWithPin() result = self.plx.PlexTvSignInWithPin()
if result: if result:
plexLogin = result['username'] plexLogin = result['username']
@ -161,7 +161,7 @@ class InitialSetup():
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, resp = dialog.yesno(self.addonName,
string(39015).encode('utf-8')) string(39015))
# Exit while loop if user chooses No # Exit while loop if user chooses No
if not resp: if not resp:
break break
@ -171,18 +171,21 @@ class InitialSetup():
break break
if not isconnected: if not isconnected:
# Enter Kodi settings instead # Enter Kodi settings instead
if dialog.yesno(
heading=self.addonName,
line1=string(39016).encode('utf-8')):
self.logMsg("User opted to disable Plex music library.", 1)
utils.settings('enableMusic', value="false")
xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId) xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId)
return return
# Write to Kodi settings file # Write to Kodi settings file
utils.settings('plex_machineIdentifier', activeServer) utils.settings('plex_machineIdentifier', activeServer)
if server['local'] == '1':
scheme = server['scheme']
utils.settings('ipaddress', server['ip']) utils.settings('ipaddress', server['ip'])
utils.settings('port', server['port']) utils.settings('port', server['port'])
if server['scheme'] == 'https': else:
baseURL = server['baseURL'].split(':')
scheme = baseURL[0]
utils.settings('ipaddress', baseURL[1].replace('//', ''))
utils.settings('port', baseURL[2])
if scheme == 'https':
utils.settings('https', 'true') utils.settings('https', 'true')
else: else:
utils.settings('https', 'false') utils.settings('https', 'false')
@ -207,14 +210,14 @@ class InitialSetup():
if forcePlexTV: if forcePlexTV:
return return
if dialog.yesno( # Disable Plex music?
heading=self.addonName, if dialog.yesno(heading=self.addonName,
line1=string(39016).encode('utf-8')): line1=string(39016)):
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( # Open Settings page now?
heading=self.addonName, if dialog.yesno(heading=self.addonName,
line1=string(39017).encode('utf-8')): line1=string(39017)):
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')

View file

@ -8,6 +8,7 @@ import Queue
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcvfs import xbmcvfs
import xbmcaddon
import utils import utils
import clientinfo import clientinfo
@ -24,6 +25,7 @@ import PlexFunctions
############################################################################### ###############################################################################
@utils.logging
@utils.ThreadMethodsAdditionalStop('emby_shouldStop') @utils.ThreadMethodsAdditionalStop('emby_shouldStop')
@utils.ThreadMethods @utils.ThreadMethods
class ThreadedGetMetadata(Thread): class ThreadedGetMetadata(Thread):
@ -38,10 +40,11 @@ class ThreadedGetMetadata(Thread):
the downloaded metadata XMLs as etree objects the downloaded metadata XMLs as etree objects
lock Lock(), used for counting where we are lock Lock(), used for counting where we are
""" """
def __init__(self, queue, out_queue, lock): def __init__(self, queue, out_queue, lock, processlock):
self.queue = queue self.queue = queue
self.out_queue = out_queue self.out_queue = out_queue
self.lock = lock self.lock = lock
self.processlock = processlock
Thread.__init__(self) Thread.__init__(self)
def run(self): def run(self):
@ -49,8 +52,10 @@ class ThreadedGetMetadata(Thread):
queue = self.queue queue = self.queue
out_queue = self.out_queue out_queue = self.out_queue
lock = self.lock lock = self.lock
processlock = self.processlock
threadStopped = self.threadStopped threadStopped = self.threadStopped
global getMetadataCount global getMetadataCount
global processMetadataCount
while threadStopped() is False: while threadStopped() is False:
# grabs Plex item from queue # grabs Plex item from queue
try: try:
@ -63,6 +68,14 @@ class ThreadedGetMetadata(Thread):
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId']) plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
if plexXML is None: if plexXML is None:
# Did not receive a valid XML - skip that item for now # Did not receive a valid XML - skip that item for now
self.logMsg("Could not get metadata for %s. "
"Skipping that item for now"
% updateItem['itemId'], -1)
# Increase BOTH counters - since metadata won't be processed
with lock:
getMetadataCount += 1
with processlock:
processMetadataCount += 1
queue.task_done() queue.task_done()
continue continue
@ -113,25 +126,20 @@ class ThreadedProcessMetadata(Thread):
except Queue.Empty: except Queue.Empty:
xbmc.sleep(100) xbmc.sleep(100)
continue continue
# Do the work; lock to be sure we've only got 1 Thread # Do the work
plexitem = updateItem['XML'] plexitem = updateItem['XML']
method = updateItem['method'] method = updateItem['method']
viewName = updateItem['viewName'] viewName = updateItem['viewName']
viewId = updateItem['viewId'] viewId = updateItem['viewId']
title = updateItem['title'] title = updateItem['title']
itemSubFkt = getattr(item, method) itemSubFkt = getattr(item, method)
with lock:
# Get the one child entry in the xml and process # Get the one child entry in the xml and process
for child in plexitem: for child in plexitem:
if method == 'add_updateAlbum':
item.add_updateAlbum(child,
viewtag=viewName,
viewid=viewId)
else:
itemSubFkt(child, itemSubFkt(child,
viewtag=viewName, viewtag=viewName,
viewid=viewId) viewid=viewId)
# Keep track of where we are at # Keep track of where we are at
with lock:
processMetadataCount += 1 processMetadataCount += 1
processingViewName = title processingViewName = title
# signals to queue job is done # signals to queue job is done
@ -164,10 +172,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, % (self.addonName,
self.itemType, self.itemType,
str(total))).encode('utf-8'), str(total)),
"Starting") "Starting")
global getMetadataCount global getMetadataCount
global processMetadataCount global processMetadataCount
@ -185,15 +193,11 @@ class ThreadedShowSyncInfo(Thread):
percentage = int(float(totalProgress) / float(total)*100.0) percentage = int(float(totalProgress) / float(total)*100.0)
except ZeroDivisionError: except ZeroDivisionError:
percentage = 0 percentage = 0
try: dialog.update(percentage,
dialog.update( message="Downloaded: %s. Processed: %s: %s"
percentage, % (getMetadataProgress,
message=("Downloaded: %s. Processed: %s: %s" processMetadataProgress,
% (getMetadataProgress, processMetadataProgress, viewName))
viewName))).encode('utf-8')
except:
# Wierd formating of the string viewName?!?
pass
# Sleep for x milliseconds # Sleep for x milliseconds
xbmc.sleep(500) xbmc.sleep(500)
dialog.close() dialog.close()
@ -213,6 +217,8 @@ class LibrarySync(Thread):
self.__dict__ = self._shared_state self.__dict__ = self._shared_state
self.__language__ = xbmcaddon.Addon().getLocalizedString
self.clientInfo = clientinfo.ClientInfo() self.clientInfo = clientinfo.ClientInfo()
self.user = userclient.UserClient() self.user = userclient.UserClient()
self.emby = embyserver.Read_EmbyServer() self.emby = embyserver.Read_EmbyServer()
@ -226,33 +232,23 @@ class LibrarySync(Thread):
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( self.enableBackgroundSync = True if utils.settings(
'enableBackgroundSync') == "true" \ 'enableBackgroundSync') == "true" else False
else False
Thread.__init__(self) Thread.__init__(self)
def showKodiNote(self, message, forced=False): def showKodiNote(self, message, forced=False):
""" """
Shows a Kodi popup, if user selected to do so Shows a Kodi popup, if user selected to do so. Pass message in unicode
or string
""" """
if not (self.showDbSync or forced): if not (self.showDbSync or forced):
return return
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=self.addonName, heading=self.addonName,
message=message.encode('utf-8'), message=message,
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
sound=False) sound=False)
def startSync(self):
utils.window('emby_dbScan', value="true")
completed = self.fastSync()
if not completed:
# Fast sync failed or server plugin is not found
self.logMsg("Something went wrong, starting full sync", -1)
completed = self.fullSync(manualrun=True)
utils.window('emby_dbScan', clear=True)
return completed
def fastSync(self): def fastSync(self):
""" """
Fast incremential lib sync Fast incremential lib sync
@ -291,8 +287,8 @@ class LibrarySync(Thread):
if self.threadStopped(): if self.threadStopped():
return True return True
# Get items per view # Get items per view
items = PlexFunctions.GetAllPlexLeaves( items = PlexFunctions.GetAllPlexLeaves(view['id'],
view['id'], updatedAt=lastSync) updatedAt=lastSync)
# Just skip item if something went wrong # Just skip item if something went wrong
if not items: if not items:
continue continue
@ -456,7 +452,7 @@ class LibrarySync(Thread):
"Found viewid: %s" % folderid, "Found viewid: %s" % folderid,
"viewname: %s" % current_viewname, "viewname: %s" % current_viewname,
"viewtype: %s" % current_viewtype, "viewtype: %s" % current_viewtype,
"tagid: %s" % current_tagid)), 2) "tagid: %s" % current_tagid)), 1)
# Remove views that are still valid to delete rest later # Remove views that are still valid to delete rest later
try: try:
@ -739,7 +735,8 @@ class LibrarySync(Thread):
for i in range(min(self.syncThreadNumber, itemNumber)): for i in range(min(self.syncThreadNumber, itemNumber)):
thread = ThreadedGetMetadata(getMetadataQueue, thread = ThreadedGetMetadata(getMetadataQueue,
processMetadataQueue, processMetadataQueue,
getMetadataLock) getMetadataLock,
processMetadataLock)
thread.setDaemon(True) thread.setDaemon(True)
thread.start() thread.start()
threads.append(thread) threads.append(thread)
@ -1121,37 +1118,46 @@ class LibrarySync(Thread):
self.run_internal() self.run_internal()
except Exception as e: except Exception as e:
utils.window('emby_dbScan', clear=True) utils.window('emby_dbScan', clear=True)
self.logMsg('LibrarySync thread crashed', -1)
# Library sync thread has crashed
xbmcgui.Dialog().ok( xbmcgui.Dialog().ok(
heading=self.addonName, heading=self.addonName,
line1=("Library sync thread has crashed. " line1=self.__language__(39400))
"You should restart Kodi now. "
"Please report this on the forum."))
raise raise
def run_internal(self): def run_internal(self):
# Re-assign handles to have faster calls
window = utils.window window = utils.window
settings = utils.settings settings = utils.settings
log = self.logMsg log = self.logMsg
threadStopped = self.threadStopped
threadSuspended = self.threadSuspended
installSyncDone = self.installSyncDone
enableBackgroundSync = self.enableBackgroundSync
fullSync = self.fullSync
fastSync = self.fastSync
string = self.__language__
dialog = xbmcgui.Dialog()
startupComplete = False startupComplete = False
self.views = [] self.views = []
count = 0 count = 0
errorcount = 0 errorcount = 0
self.logMsg("---===### Starting LibrarySync ###===---", 0) log("---===### Starting LibrarySync ###===---", 0)
while not self.threadStopped(): while not threadStopped():
# In the event the server goes offline, or an item is playing # In the event the server goes offline, or an item is playing
while self.threadSuspended(): while threadSuspended():
# Set in service.py # Set in service.py
if self.threadStopped(): if threadStopped():
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
log("###===--- LibrarySync Stopped ---===###", 0) log("###===--- LibrarySync Stopped ---===###", 0)
return return
xbmc.sleep(1000) xbmc.sleep(1000)
if (window('emby_dbCheck') != "true" and if (window('emby_dbCheck') != "true" and installSyncDone):
self.installSyncDone):
# Verify the validity of the database # Verify the validity of the database
currentVersion = settings('dbCreatedWithVersion') currentVersion = settings('dbCreatedWithVersion')
minVersion = window('emby_minDBVersion') minVersion = window('emby_minDBVersion')
@ -1160,17 +1166,14 @@ class LibrarySync(Thread):
if not uptoDate: if not uptoDate:
log("Db version out of date: %s minimum version required: " log("Db version out of date: %s minimum version required: "
"%s" % (currentVersion, minVersion), 0) "%s" % (currentVersion, minVersion), 0)
resp = xbmcgui.Dialog().yesno( # DB out of date. Proceed to recreate?
heading="Db Version", resp = dialog.yesno(heading=self.addonName,
line1=("Detected the database needs to be recreated " line1=string(39401))
"for this version of " + self.addonName +
"Proceed?"))
if not resp: if not resp:
log("Db version out of date! USER IGNORED!", 0) log("Db version out of date! USER IGNORED!", 0)
xbmcgui.Dialog().ok( # PKC may not work correctly until reset
heading=self.addonName, dialog.ok(heading=self.addonName,
line1=(self.addonName + " may not work correctly " line1=(self.addonName + string(39402)))
"until the database is reset."))
else: else:
utils.reset() utils.reset()
break break
@ -1184,20 +1187,19 @@ class LibrarySync(Thread):
if not xbmcvfs.exists(videoDb): if not xbmcvfs.exists(videoDb):
# Database does not exists # Database does not exists
log("The current Kodi version is incompatible " log("The current Kodi version is incompatible "
"to know which Kodi versions are supported.", 0) "to know which Kodi versions are supported.", -1)
xbmcgui.Dialog().ok( log('Current Kodi version: %s' % xbmc.getInfoLabel(
heading=self.addonName, 'System.BuildVersion').decode('utf-8'))
line1=("Cancelling the database syncing process. " # "Current Kodi version is unsupported, cancel lib sync"
"Current Kodi version: %s is unsupported. " dialog.ok(heading=self.addonName,
"Please verify your logs for more info." line1=string(39403))
% xbmc.getInfoLabel('System.BuildVersion')))
break break
# Run start up sync # Run start up sync
window('emby_dbScan', value="true") window('emby_dbScan', value="true")
log("Db version: %s" % settings('dbCreatedWithVersion'), 0) log("Db version: %s" % settings('dbCreatedWithVersion'), 0)
log("Initial start-up full sync starting", 0) log("Initial start-up full sync starting", 0)
librarySync = self.fullSync(manualrun=True) librarySync = fullSync(manualrun=True)
window('emby_dbScan', clear=True) window('emby_dbScan', clear=True)
if librarySync: if librarySync:
log("Initial start-up full sync successful", 0) log("Initial start-up full sync successful", 0)
@ -1205,17 +1207,16 @@ class LibrarySync(Thread):
settings('SyncInstallRunDone', value="true") settings('SyncInstallRunDone', value="true")
settings("dbCreatedWithVersion", settings("dbCreatedWithVersion",
self.clientInfo.getVersion()) self.clientInfo.getVersion())
self.installSyncDone = True installSyncDone = True
else: else:
log("Initial start-up full sync unsuccessful", -1) log("Initial start-up full sync unsuccessful", -1)
errorcount += 1 errorcount += 1
if errorcount > 2: if errorcount > 2:
log("Startup full sync failed. Stopping sync", -1) log("Startup full sync failed. Stopping sync", -1)
xbmcgui.Dialog().ok( # "Startup syncing process failed repeatedly"
heading=self.addonName, # "Please restart"
line1=("Startup syncing process failed repeatedly." dialog.ok(heading=self.addonName,
" Try restarting Kodi. Stopping Sync for " line1=string(39404))
"now."))
break break
# Currently no db scan, so we can start a new scan # Currently no db scan, so we can start a new scan
@ -1225,7 +1226,7 @@ class LibrarySync(Thread):
log('Full library scan requested, starting', 0) log('Full library scan requested, starting', 0)
window('emby_dbScan', value="true") window('emby_dbScan', value="true")
window('plex_runLibScan', clear=True) window('plex_runLibScan', clear=True)
self.fullSync(manualrun=True) fullSync(manualrun=True)
window('emby_dbScan', clear=True) window('emby_dbScan', clear=True)
count = 0 count = 0
# Reset views was requested from somewhere else # Reset views was requested from somewhere else
@ -1239,36 +1240,45 @@ class LibrarySync(Thread):
# Remove video nodes # Remove video nodes
utils.deleteNodes() utils.deleteNodes()
# Kick off refresh # Kick off refresh
dialog = xbmcgui.Dialog()
if self.maintainViews(): if self.maintainViews():
# Ran successfully
log("Refresh playlists/nodes completed", 0)
# "Plex playlists/nodes refreshed"
dialog.notification( dialog.notification(
heading=self.addonName, heading=self.addonName,
message="Plex playlists/nodes refreshed", message=string(39405),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
time=3000, time=3000,
sound=True) sound=True)
else: else:
self.logMsg("Refresh playlists/nodes failed", -1) # Failed
log("Refresh playlists/nodes failed", -1)
# "Plex playlists/nodes refresh failed"
dialog.notification( dialog.notification(
heading=self.addonName, heading=self.addonName,
message="Plex playlists/nodes refresh failed", message=string(39406),
icon=xbmcgui.NOTIFICATION_ERROR, icon=xbmcgui.NOTIFICATION_ERROR,
time=3000, time=3000,
sound=True) sound=True)
window('emby_dbScan', clear=True) window('emby_dbScan', clear=True)
elif self.enableBackgroundSync: elif 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
window('emby_dbScan', value="true") window('emby_dbScan', value="true")
log('Running automatic full lib scan', 0) log('Running background full lib scan', 0)
self.fullSync(manualrun=True) fullSync(manualrun=True)
window('emby_dbScan', clear=True) window('emby_dbScan', clear=True)
# Run fast sync otherwise (ever 2 seconds or so) # Run fast sync otherwise (ever second or so)
else: else:
self.startSync() window('emby_dbScan', value="true")
if not fastSync():
# Fast sync failed or server plugin is not found
log("Something went wrong, starting full sync", -1)
fullSync(manualrun=True)
window('emby_dbScan', clear=True)
xbmc.sleep(2000) xbmc.sleep(1000)
count += 1 count += 1
log("###===--- LibrarySync Stopped ---===###", 0) log("###===--- LibrarySync Stopped ---===###", 0)

View file

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

View file

@ -34,6 +34,8 @@ class PlayUtils():
""" """
Returns the playurl for the part with number partNumber Returns the playurl for the part with number partNumber
(movie might consist of several files) (movie might consist of several files)
playurl is utf-8 encoded!
""" """
log = self.logMsg log = self.logMsg
window = utils.window window = utils.window
@ -64,6 +66,7 @@ class PlayUtils():
} }
playurl = self.API.getTranscodeVideoPath('Transcode', playurl = self.API.getTranscodeVideoPath('Transcode',
quality=quality) quality=quality)
playurl = playurl.encode('utf-8')
# Set playmethod property # Set playmethod property
window('emby_%s.playmethod' % playurl, value="Transcode") window('emby_%s.playmethod' % playurl, value="Transcode")
@ -371,7 +374,7 @@ class PlayUtils():
subNum += 1 subNum += 1
if audioNum > 1: if audioNum > 1:
resp = dialog.select(lang(33013).encode('utf-8'), audioStreams) resp = dialog.select(lang(33013), audioStreams)
if resp > -1: if resp > -1:
# User selected audio # User selected audio
playurlprefs['audioStreamID'] = audioStreamsList[resp] playurlprefs['audioStreamID'] = audioStreamsList[resp]
@ -384,7 +387,7 @@ class PlayUtils():
playurlprefs['audioBoost'] = utils.settings('audioBoost') playurlprefs['audioBoost'] = utils.settings('audioBoost')
if subNum > 1: if subNum > 1:
resp = dialog.select(lang(33014).encode('utf-8'), subtitleStreams) resp = dialog.select(lang(33014), subtitleStreams)
if resp == 0: if resp == 0:
# User selected no subtitles # User selected no subtitles
playurlprefs["skipSubtitles"] = 1 playurlprefs["skipSubtitles"] = 1

View file

@ -31,7 +31,10 @@ import re
import threading import threading
import time import time
import urllib2 import urllib2
import downloadutils import downloadutils
from PlexFunctions import PMSHttpsEnabled
class plexgdm: class plexgdm:
@ -56,6 +59,7 @@ class plexgdm:
self.discovery_complete = False self.discovery_complete = False
self.client_registered = False self.client_registered = False
self.debug = debug self.debug = debug
self.download = downloadutils.DownloadUtils().downloadUrl
def __printDebug(self, message, level=1): def __printDebug(self, message, level=1):
if self.debug: if self.debug:
@ -139,13 +143,19 @@ class plexgdm:
try: try:
media_server=self.server_list[0]['server'] media_server=self.server_list[0]['server']
media_port=self.server_list[0]['port'] media_port=self.server_list[0]['port']
scheme = self.server_list[0]['protocol']
self.__printDebug("Checking server [%s] on port [%s]" % (media_server, media_port) ,2) self.__printDebug("Checking server [%s] on port [%s]" % (media_server, media_port) ,2)
client_result = downloadutils.DownloadUtils().downloadUrl( client_result = self.download(
'http://%s:%s/clients' % (media_server, media_port)) '%s://%s:%s/clients' % (scheme, media_server, media_port))
# f = urllib2.urlopen('http://%s:%s/clients' % (media_server, media_port)) # f = urllib2.urlopen('http://%s:%s/clients' % (media_server, media_port))
# client_result = f.read() # client_result = f.read()
if self.client_id in str(client_result): registered = False
for client in client_result:
if (client.attrib.get('machineIdentifier') ==
self.client_id):
registered = True
if registered:
self.__printDebug("Client registration successful",1) self.__printDebug("Client registration successful",1)
self.__printDebug("Client data is: %s" % client_result, 3) self.__printDebug("Client data is: %s" % client_result, 3)
return True return True
@ -208,7 +218,6 @@ class plexgdm:
if "200 OK" in response.get('data'): if "200 OK" in response.get('data'):
for each in response.get('data').split('\r\n'): for each in response.get('data').split('\r\n'):
update['discovery'] = "auto" update['discovery'] = "auto"
update['owned']='1' update['owned']='1'
update['master']= 1 update['master']= 1
@ -230,6 +239,12 @@ class plexgdm:
elif "Server-Class:" in each: elif "Server-Class:" in each:
update['class'] = each.split(':')[1].strip() update['class'] = each.split(':')[1].strip()
# Quickly test if we need https
if PMSHttpsEnabled(
'%s:%s' % (update['server'], update['port'])):
update['protocol'] = 'https'
else:
update['protocol'] = 'http'
discovered_servers.append(update) discovered_servers.append(update)
self.server_list = discovered_servers self.server_list = discovered_servers
@ -239,7 +254,7 @@ class plexgdm:
else: else:
self.__printDebug("Number of servers Discovered: %s" % len(self.server_list),1) self.__printDebug("Number of servers Discovered: %s" % len(self.server_list),1)
for items in self.server_list: for items in self.server_list:
self.__printDebug("Server Discovered: %s" % items['serverName'] ,2) self.__printDebug("Server Discovered: %s" % items, 2)
def setInterval(self, interval): def setInterval(self, interval):

View file

@ -46,23 +46,16 @@ class UserClient(threading.Thread):
threading.Thread.__init__(self) threading.Thread.__init__(self)
def getAdditionalUsers(self):
additionalUsers = utils.settings('additionalUsers')
if additionalUsers:
self.AdditionalUser = additionalUsers.split(',')
def getUsername(self): def getUsername(self):
""" """
Returns username as unicode Returns username as unicode
""" """
username = utils.settings('username').decode('utf-8') username = utils.settings('username')
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').decode('utf-8') username = utils.settings('plexLogin')
if not username: if not username:
self.logMsg("Also no Plex username found", 0) self.logMsg("Also no Plex username found", 0)
return "" return ""
@ -245,9 +238,8 @@ 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( xbmcgui.Dialog().notification(self.addonName,
self.addonName, utils.language(33007))
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)
@ -318,7 +310,8 @@ class UserClient(threading.Thread):
dialog = xbmcgui.Dialog() dialog = xbmcgui.Dialog()
# Get /profile/addon_data # Get /profile/addon_data
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8') addondir = xbmc.translatePath(
self.addon.getAddonInfo('profile')).decode('utf-8')
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir) hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
# If there's no settings.xml # If there's no settings.xml
@ -373,7 +366,7 @@ class UserClient(threading.Thread):
if username: if username:
dialog.notification( dialog.notification(
heading=self.addonName, heading=self.addonName,
message=("Welcome " + username).encode('utf-8'), message="Welcome " + username,
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png") icon="special://home/addons/plugin.video.plexkodiconnect/icon.png")
else: else:
dialog.notification( dialog.notification(
@ -388,11 +381,11 @@ class UserClient(threading.Thread):
settings('userId', 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 >= 2:
log("Too many retries.", 1) log("Too many retries to login.", -1)
window('emby_serverStatus', value="Stop") window('emby_serverStatus', value="Stop")
dialog.ok(lang(33001).encode('utf-8'), dialog.ok(lang(33001),
lang(39023).encode('utf-8')) lang(39023))
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')
@ -423,7 +416,7 @@ class UserClient(threading.Thread):
while self.threadSuspended(): while self.threadSuspended():
if self.threadStopped(): if self.threadStopped():
break break
xbmc.sleep(3000) xbmc.sleep(1000)
status = window('emby_serverStatus') status = window('emby_serverStatus')
if status: if status:
@ -455,5 +448,8 @@ class UserClient(threading.Thread):
log("Server found: %s" % server, 2) log("Server found: %s" % server, 2)
self.auth = True self.auth = True
# Minimize CPU load
xbmc.sleep(500)
self.doUtils.stopSession() self.doUtils.stopSession()
log("##===---- UserClient Stopped ----===##", 0) log("##===---- UserClient Stopped ----===##", 0)

View file

@ -192,32 +192,47 @@ def logMsg(title, msg, level=1):
logLevel = int(window('emby_logLevel')) logLevel = int(window('emby_logLevel'))
except ValueError: except ValueError:
logLevel = 0 logLevel = 0
kodiLevel = {
-1: xbmc.LOGERROR,
0: xbmc.LOGNOTICE,
1: xbmc.LOGNOTICE,
2: xbmc.LOGNOTICE
}
if logLevel >= level: if logLevel >= level:
if logLevel == 2: # inspect is expensive if logLevel == 2: # inspect is expensive
func = inspect.currentframe().f_back.f_back.f_code func = inspect.currentframe().f_back.f_back.f_code
try: try:
xbmc.log("%s -> %s : %s" % ( xbmc.log("%s -> %s : %s" % (
title, func.co_name, msg)) title, func.co_name, msg), level=kodiLevel[level])
except UnicodeEncodeError: except UnicodeEncodeError:
try: try:
xbmc.log("%s -> %s : %s" % ( xbmc.log("%s -> %s : %s" % (
title, func.co_name, msg.encode('utf-8'))) title, func.co_name, msg.encode('utf-8')),
level=kodiLevel[level])
except: except:
xbmc.log("%s -> %s : %s" % (title, func.co_name, 'COULDNT LOG')) xbmc.log("%s -> %s : %s" % (
title, func.co_name, 'COULDNT LOG'),
level=kodiLevel[level])
else: else:
try: try:
xbmc.log("%s -> %s" % (title, msg)) xbmc.log("%s -> %s" % (title, msg), level=kodiLevel[level])
except UnicodeEncodeError: except UnicodeEncodeError:
try: try:
xbmc.log("%s -> %s" % (title, msg.encode('utf-8'))) xbmc.log("%s -> %s" % (title, msg.encode('utf-8')),
level=kodiLevel[level])
except: except:
xbmc.log("%s -> %s " % (title, 'COULDNT LOG')) xbmc.log("%s -> %s " % (title, 'COULDNT LOG'),
level=kodiLevel[level])
def window(property, value=None, clear=False, windowid=10000): def window(property, value=None, clear=False, windowid=10000):
# Get or set window property """
Get or set window property - thread safe!
Returns unicode.
Property needs to be string; value may be string or unicode
"""
WINDOW = xbmcgui.Window(windowid) WINDOW = xbmcgui.Window(windowid)
#setproperty accepts both string and unicode but utf-8 strings are adviced by kodi devs because some unicode can give issues #setproperty accepts both string and unicode but utf-8 strings are adviced by kodi devs because some unicode can give issues
@ -228,14 +243,13 @@ 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.encode('utf-8'))
WINDOW.setProperty(property, value) else:
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. Returns unicode
Settings needs to be string Settings needs to be string
Value can either be unicode or string Value can either be unicode or string
@ -244,10 +258,10 @@ def settings(setting, value=None):
if value is not None: if value is not None:
# Takes string or unicode by default! # Takes string or unicode by default!
addon.setSetting(setting, value) addon.setSetting(setting, value.encode('utf-8'))
else: else:
# Returns unicode by default! # Should return unicode by default, but just in case
return addon.getSetting(setting) return addon.getSetting(setting).decode('utf-8')
def language(stringid): def language(stringid):
# Central string retrieval # Central string retrieval
@ -443,7 +457,7 @@ def reset():
logMsg("PLEX", "Deleting: settings.xml", 1) logMsg("PLEX", "Deleting: settings.xml", 1)
dialog.ok( dialog.ok(
heading="Emby for Kodi", heading=addonName,
line1="Database reset has completed, Kodi will now restart to apply the changes.") line1="Database reset has completed, Kodi will now restart to apply the changes.")
xbmc.executebuiltin('RestartApp') xbmc.executebuiltin('RestartApp')

View file

@ -4,7 +4,6 @@
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

View file

@ -21,12 +21,13 @@
<setting label="30517" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Network credentials --> <setting label="30517" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Network credentials -->
<setting id="accessToken" type="text" visible="false" default="" /> <setting id="accessToken" type="text" visible="false" default="" />
<setting id="pathsub" type="bool" visible="false" default="false" /> <setting id="pathsub" type="bool" visible="false" default="false" />
<setting label="39024" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=reConnect)" option="close" />
</category> </category>
<category label="plex.tv"><!-- plex.tv --> <category label="plex.tv"><!-- plex.tv -->
<!-- Primary address --> <!-- Primary address -->
<setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup --> <setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup -->
<setting id="doPlexTvLogin" label="39024" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=doPlexTvLogin)" option="close" /> <setting label="39209" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=reConnect)" option="close" />
<setting id="plexLogin" label="plex.tv username" type="text" default="" visible="false" /> <setting id="plexLogin" label="plex.tv username" type="text" default="" visible="false" />
<setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" /> <setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" />

View file

@ -171,8 +171,7 @@ 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" % (lang(33000), user.currUser) message="%s %s" % (lang(33000), user.currUser),
).encode('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)
@ -225,8 +224,9 @@ class Service():
window('emby_online', value="false") window('emby_online', value="false")
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=lang(33001).encode('utf-8'), heading=lang(33001),
message=("%s %s" % (self.addonName, lang(33002))).encode('utf-8'), message="%s %s"
% (self.addonName, lang(33002)),
icon="special://home/addons/plugin.video." icon="special://home/addons/plugin.video."
"plexkodiconnect/icon.png", "plexkodiconnect/icon.png",
sound=False) sound=False)
@ -244,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).encode('utf-8'), message=lang(33003),
icon="special://home/addons/plugin.video." icon="special://home/addons/plugin.video."
"plexkodiconnect/icon.png", "plexkodiconnect/icon.png",
time=2000, time=2000,