Merge branch 'master' into develop

This commit is contained in:
tomkat83 2016-03-23 17:37:01 +01:00
commit 98d11ceb2d
25 changed files with 483 additions and 152 deletions

View file

@ -55,18 +55,28 @@ Currently these features are working:
- Play directly from network paths (e.g. "\\\\server\\Plex\\movie.mkv" on Windows or SMB paths "smb://server/Plex/movie.mkv") instead of slow HTTP (e.g. "192.168.1.1:32400"). You have to setup all your Plex libraries to point to such network paths. - Play directly from network paths (e.g. "\\\\server\\Plex\\movie.mkv" on Windows or SMB paths "smb://server/Plex/movie.mkv") instead of slow HTTP (e.g. "192.168.1.1:32400"). You have to setup all your Plex libraries to point to such network paths.
**Known Issues:** **Known "Larger" Issues:**
- **Plex Music:** You must have a static IP address for your Plex media server if you plan to use Plex Music features. This is due to the way Kodi works and cannot be helped. Solutions are unlikely due to the nature of these issues
- **Plex Music:** You must have a static IP address for your Plex media server if you plan to use Plex Music features. This is due to the way Kodi works and cannot be helped.
- **Plex Music:** Kodi tries to scan every(!) single Plex song on startup. This leads to errors in the Kodi log file and potentially even crashes. (Plex puts each song in a "dedicated folder", e.g. 'http://192.168.1.1:32400/library/parts/749450/'. Kodi unsuccessfully tries to scan these folders)
- **Plex updates:** PlexKodiConnect continuously polls the Plex Media Server for changes. If something on the PMS has changed, this change is synced to Kodi. Hence if you rescan your entire library, a long PlexKodiConnect re-sync is triggered. - **Plex updates:** PlexKodiConnect continuously polls the Plex Media Server for changes. If something on the PMS has changed, this change is synced to Kodi. Hence if you rescan your entire library, a long PlexKodiConnect re-sync is triggered.
- **Direct Paths:** If you use direct paths, your sync will be slower - **Subtitles**: external Plex subtitles (separate file, e.g. mymovie.srt) can be used, but it is impossible to label them correctly/tell what language they are in
- **Direct Paths:** If you use direct paths, your (initial) sync will be slower
**Known Bugs:**
- **Plex Music:** Plex Music for direct paths does not work yet.
- **Video Nodes**: some nodes, e.g. "On Deck", are customized/hacked. Hence no access to movie metadata is possible, because Kodi does not know it's a library item - **Video Nodes**: some nodes, e.g. "On Deck", are customized/hacked. Hence no access to movie metadata is possible, because Kodi does not know it's a library item
**What could be in the pipeline?**
**What could be in the pipeline for future development?**
- Watch Later - Watch Later
- Playlists - Playlists
- Homevideos - Homevideos
- Pictures - Pictures
- Music Videos - Music Videos
- Automatic updates
- Redesigned background sync process that puts less strain on the PMS
- Simultaneously connecting to several PMS
- TV Shows Theme Music (ultra-low prio) - TV Shows Theme Music (ultra-low prio)

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.14" version="1.0.16"
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,20 @@
version 1.0.16
- Kodi profiles up and running; try assigning different Plex users per profile!
- Change "Switch User" to "Log Out Plex User: <username>"
- TV shows On Deck: append season and episode number in the settings
- Shut down PKC correctly (useful for Kodi profiles)
- Don't de-authorize if several PMS are present
- Relabel to "Full PKC reset" in settings
version 1.0.15
- Enable external Plex subtitles if available
- TV On Deck: option to include show name
- Playback updates now if an item is resumed
- Fix PMS not being informed of playback stop
- Fix playback updates for remote PMS
- Deactivate info "Gathering information from files"
- Updated readme
version 1.0.14 version 1.0.14
- Fix TV shows rating not showing up - Fix TV shows rating not showing up
- Fix music libraries being scanned twice - Fix music libraries being scanned twice

View file

@ -357,11 +357,10 @@
<string id="39014">Please sign in to plex.tv.</string> <string id="39014">Please sign in to plex.tv.</string>
<string id="39015">Problems connecting to server. Pick another server?</string> <string id="39015">Problems connecting to server. Pick another server?</string>
<string id="39016">Disable Plex music library?</string> <string id="39016">Disable Plex music library?</string>
<string id="39017">Would you now like to go to the plugin's settings? <string id="39017">Would you now like to go to the plugin's settings to fine-tune PKC? You will need to RESTART Kodi!</string>
(This is hopefully unneccessary ;-))</string>
<string id="39018">[COLOR yellow]Repair local database (force update all content)[/COLOR]</string> <string id="39018">[COLOR yellow]Repair local database (force update all content)[/COLOR]</string>
<string id="39019">[COLOR yellow]Perform local database reset (full resync)[/COLOR]</string> <string id="39019">[COLOR red]Partial or full reset of Database and PKC[/COLOR]</string>
<string id="39020">[COLOR yellow]Cache all images to Kodi texture cache[/COLOR]</string> <string id="39020">[COLOR yellow]Cache all images to Kodi texture cache[/COLOR]</string>
<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>
@ -388,8 +387,15 @@
<string id="39043">Go a step further and complete replace all original Plex library paths (/volume1/media) with custom SMB paths (smb://NAS/MyStuff)?</string> <string id="39043">Go a step further and complete replace all original Plex library paths (/volume1/media) with custom SMB paths (smb://NAS/MyStuff)?</string>
<string id="39044">Please enter your custom smb paths in the settings under "Sync Options" and then restart Kodi</string> <string id="39044">Please enter your custom smb paths in the settings under "Sync Options" and then restart Kodi</string>
<string id="39045">Appearance Tweaks</string>
<string id="39046">TV Shows</string>
<string id="39047">On Deck: Append show title to episode</string>
<string id="39048">On Deck: Append season- and episode-number (e.g. S3E2)</string>
<string id="39049">Nothing works? Try a full reset!</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->
<string id="39200">Switch Plex Home User</string> <string id="39200">Log-out Plex Home User: </string>
<string id="39201">Settings</string> <string id="39201">Settings</string>
<string id="39202">Network credentials</string> <string id="39202">Network credentials</string>
<string id="39203">Refresh Plex playlists/nodes</string> <string id="39203">Refresh Plex playlists/nodes</string>

View file

@ -294,10 +294,10 @@
<string id="39014">Bitte loggen Sie sich in plex.tv ein.</string> <string id="39014">Bitte loggen Sie sich in plex.tv ein.</string>
<string id="39015">Beim Verbinden mit dem Server sind Probleme aufgetreten. Mit einem anderen Server versuchen?</string> <string id="39015">Beim Verbinden mit dem Server sind Probleme aufgetreten. Mit einem anderen Server versuchen?</string>
<string id="39016">Plex Musik Bibliotheken deaktivieren?</string> <string id="39016">Plex Musik Bibliotheken deaktivieren?</string>
<string id="39017">Möchten Sie nun die Einstellungen des Plugins öffnen? (Was hoffentlich unnötig ist ;-))"</string> <string id="39017">Möchten Sie nun die Einstellungen des Plugins öffnen? Kodi muss anschliessend neu gestartet werden!</string>
<string id="39018">[COLOR yellow]Lokale Datenbank reparieren (allen Inhalt aktualisieren)[/COLOR]</string> <string id="39018">[COLOR yellow]Lokale Datenbank reparieren (allen Inhalt aktualisieren)[/COLOR]</string>
<string id="39019">[COLOR yellow]Lokale Datenbank zurücksetzen (kompletter Resync nötig)[/COLOR]</string> <string id="39019">[COLOR red]Datenbank und auf Wunsch PKC zurücksetzen[/COLOR]</string>
<string id="39020">[COLOR yellow]Alle Plex Bilder in Kodi zwischenspeichern[/COLOR]</string> <string id="39020">[COLOR yellow]Alle Plex Bilder in Kodi zwischenspeichern[/COLOR]</string>
<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>
@ -324,8 +324,14 @@
<string id="39043">Sollen sogar sämtliche Plex Pfade wie /volume1/Hans/medien durch benutzerdefinierte smb Pfade wie smb://NAS/Filme ersetzt werden?</string> <string id="39043">Sollen sogar sämtliche Plex Pfade wie /volume1/Hans/medien durch benutzerdefinierte smb Pfade wie smb://NAS/Filme ersetzt werden?</string>
<string id="39044">Bitte geben Sie Ihre benutzerdefinierten SMB Pfade nun in den Einstellungen unter Sync Optionen ein. Starten Sie dann Kodi neu.</string> <string id="39044">Bitte geben Sie Ihre benutzerdefinierten SMB Pfade nun in den Einstellungen unter Sync Optionen ein. Starten Sie dann Kodi neu.</string>
<string id="39045">Erscheinung</string>
<string id="39046">TV Serien</string>
<string id="39047">"Aktuell": Serien- an Episoden-Titel anfügen</string>
<string id="39048">"Aktuell": Staffel und Episode anfügen (z.B. S3E2)</string>
<string id="39049">Nichts funktioniert? Setze mal alles zurück!</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->
<string id="39200">Plex Home Benutzer wechseln</string> <string id="39200">Plex Home Benutzer abmelden: </string>
<string id="39201">Einstellungen</string> <string id="39201">Einstellungen</string>
<string id="39202">Netzwerk Credentials</string> <string id="39202">Netzwerk Credentials</string>
<string id="39203">Plex Playlisten und Nodes zurücksetzen</string> <string id="39203">Plex Playlisten und Nodes zurücksetzen</string>

View file

@ -51,6 +51,7 @@ from threading import Thread
import Queue import Queue
import traceback import traceback
import requests import requests
import xml.etree.ElementTree as etree
import re import re
import json import json
@ -58,10 +59,10 @@ from urllib import urlencode, quote_plus, unquote
from PlexFunctions import PlexToKodiTimefactor, PMSHttpsEnabled from PlexFunctions import PlexToKodiTimefactor, PMSHttpsEnabled
try:
import xml.etree.cElementTree as etree # Disable requests logging
except ImportError: from requests.packages.urllib3.exceptions import InsecureRequestWarning
import xml.etree.ElementTree as etree requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
@utils.logging @utils.logging
@ -194,12 +195,12 @@ class PlexAPI():
# Wait for approx 30 seconds (since the PIN is not visible anymore :-)) # Wait for approx 30 seconds (since the PIN is not visible anymore :-))
while count < 30: while count < 30:
xml = self.CheckPlexTvSignin(identifier) xml = self.CheckPlexTvSignin(identifier)
if xml: if xml is not False:
break break
# Wait for 1 seconds # Wait for 1 seconds
xbmc.sleep(1000) xbmc.sleep(1000)
count += 1 count += 1
if not xml: if xml is False:
# 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))
return False return False
@ -263,7 +264,7 @@ class PlexAPI():
identifier = None identifier = None
# Download # Download
xml = self.TalkToPlexServer(url, talkType="POST") xml = self.TalkToPlexServer(url, talkType="POST")
if not xml: if xml is False:
return code, identifier return code, identifier
try: try:
code = xml.find('code').text code = xml.find('code').text
@ -648,7 +649,11 @@ class PlexAPI():
# Ping to check whether we need HTTPs or HTTP # Ping to check whether we need HTTPs or HTTP
url = (self.getPMSProperty(ATV_udid, uuid_id, 'ip') + ':' url = (self.getPMSProperty(ATV_udid, uuid_id, 'ip') + ':'
+ self.getPMSProperty(ATV_udid, uuid_id, 'port')) + self.getPMSProperty(ATV_udid, uuid_id, 'port'))
if PMSHttpsEnabled(url): https = PMSHttpsEnabled(url)
if https is None:
# Error contacting url
continue
elif https:
self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'https') self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'https')
else: else:
self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'http') self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'http')
@ -2316,6 +2321,7 @@ class API():
utils.window('emby_shouldStop', value="true") utils.window('emby_shouldStop', value="true")
playurl = False playurl = False
utils.window('emby_pathverified', value='true') utils.window('emby_pathverified', value='true')
utils.settings('emby_pathverified', value='true')
return playurl return playurl
def askToValidate(self, url): def askToValidate(self, url):

View file

@ -2,7 +2,6 @@
import threading import threading
import traceback import traceback
import socket import socket
import requests
import xbmc import xbmc
@ -64,22 +63,19 @@ class PlexCompanion(threading.Thread):
message_count = 0 message_count = 0
is_running = False is_running = False
while not self.threadStopped(): while not self.threadStopped():
while self.threadSuspended():
if self.threadStopped():
break
xbmc.sleep(3000)
# If we are not authorized, sleep # If we are not authorized, sleep
# Otherwise, we trigger a download which leads to a # Otherwise, we trigger a download which leads to a
# re-authorizations # re-authorizations
if window('emby_serverStatus'): while self.threadSuspended() or window('emby_serverStatus'):
xbmc.sleep(3000) if self.threadStopped():
continue break
xbmc.sleep(1000)
try: try:
httpd.handle_request() httpd.handle_request()
message_count += 1 message_count += 1
if message_count > 30: if message_count > 100:
if self.client.check_client_registration(): if self.client.check_client_registration():
self.logMsg("Client is still registered", 1) self.logMsg("Client is still registered", 1)
else: else:
@ -97,13 +93,14 @@ class PlexCompanion(threading.Thread):
xbmc.sleep(50) 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.format_exc(), 1)
xbmc.sleep(50) xbmc.sleep(50)
self.client.stop_all() self.client.stop_all()
try: try:
httpd.socket.shutdown(socket.SHUT_RDWR) httpd.socket.shutdown(socket.SHUT_RDWR)
except:
pass
finally: finally:
httpd.socket.close() httpd.socket.close()
requests.dumpConnections()
self.logMsg("----===## STOP Plex Companion ##===----", 0) self.logMsg("----===## STOP Plex Companion ##===----", 0)

View file

@ -4,12 +4,17 @@ from ast import literal_eval
from urlparse import urlparse, parse_qs from urlparse import urlparse, parse_qs
import re import re
from copy import deepcopy from copy import deepcopy
import requests
from xbmcaddon import Addon from xbmcaddon import Addon
import downloadutils import downloadutils
from utils import logMsg, settings from utils import logMsg, settings
# Disable requests logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
addonName = Addon().getAddonInfo('name') addonName = Addon().getAddonInfo('name')
title = "%s %s" % (addonName, __name__) title = "%s %s" % (addonName, __name__)
@ -394,7 +399,8 @@ def getPlexRepeat(kodiRepeat):
def PMSHttpsEnabled(url): def PMSHttpsEnabled(url):
""" """
Returns True if the PMS wants to talk https, False otherwise Returns True if the PMS wants to talk https, False otherwise. None if error
occured, e.g. the connection timed out
With with e.g. url=192.168.0.1:32400 (NO http/https) With with e.g. url=192.168.0.1:32400 (NO http/https)
@ -403,17 +409,37 @@ def PMSHttpsEnabled(url):
Prefers HTTPS over HTTP Prefers HTTPS over HTTP
""" """
xml = downloadutils.DownloadUtils().downloadUrl( # True if https, False if http
'https://%s/identity' % url) answer = True
try: try:
# received a valid XML - https connection is possible # Don't use downloadutils here, otherwise we may get un-authorized!
xml.attrib res = requests.get('https://%s/identity' % url,
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1) headers={},
return True verify=False,
except: timeout=(3, 10))
# couldn't get an xml - switch to http traffic # Don't verify SSL since we can connect for sure then!
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1) except requests.exceptions.ConnectionError as e:
return False # Might have SSL deactivated. Try with http
try:
res = requests.get('http://%s/identity' % url,
headers={},
timeout=(3, 10))
except requests.exceptions.ConnectionError as e:
logMsg("Server is offline or cannot be reached. Url: %s, "
"Error message: %s" % (url, e), -1)
return None
except requests.exceptions.ReadTimeout:
logMsg("Server timeout reached for Url %s" % url, -1)
return None
else:
answer = False
except requests.exceptions.ReadTimeout:
logMsg("Server timeout reached for Url %s" % url, -1)
return None
if res.status_code == requests.codes.ok:
return answer
else:
return None
def scrobble(ratingKey, state): def scrobble(ratingKey, state):

View file

@ -15,6 +15,10 @@ import xbmcvfs
import utils import utils
import image_cache_thread import image_cache_thread
# Disable requests logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
############################################################################### ###############################################################################

View file

@ -4,6 +4,7 @@
# import json # import json
import requests import requests
import xml.etree.ElementTree as etree
# import logging # import logging
# import xbmc # import xbmc
@ -13,10 +14,6 @@ import utils
import clientinfo import clientinfo
import PlexAPI import PlexAPI
try:
import xml.etree.cElementTree as etree
except ImportError:
import xml.etree.ElementTree as etree
############################################################################### ###############################################################################
@ -392,7 +389,8 @@ class DownloadUtils():
elif status not in ("401", "Auth"): elif status not in ("401", "Auth"):
# Tell userclient token has been revoked. # Tell userclient token has been revoked.
self.logMsg('Setting emby_serverStatus to 401') self.logMsg('Error 401 contacting %s' % url, 0)
self.logMsg('Setting emby_serverStatus to 401', 0)
utils.window('emby_serverStatus', value="401") utils.window('emby_serverStatus', value="401")
self.logMsg("HTTP Error: %s" % e, 0) self.logMsg("HTTP Error: %s" % e, 0)
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(

View file

@ -81,13 +81,28 @@ def reConnect():
utils.logMsg("entrypoint reConnect", utils.logMsg("entrypoint reConnect",
"Connection resets requested", 0) "Connection resets requested", 0)
dialog = xbmcgui.Dialog() dialog = xbmcgui.Dialog()
# Resetting, please wait
dialog.notification( dialog.notification(
heading=addonName, heading=addonName,
message=string(39207), message=string(39207),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
time=2000,
sound=False) sound=False)
# Pause library sync thread - user needs to be auth in order to sync # Pause library sync thread - user needs to be auth in order to sync
utils.window('suspend_LibraryThread', value='true') utils.window('suspend_LibraryThread', value='true')
# Wait max for 5 seconds for all lib scans to finish
counter = 0
while utils.window('emby_dbScan') == 'true':
if counter > 500:
# Failed to reset PMS and plex.tv connects. Try to restart Kodi.
dialog.ok(heading=addonName,
message=string(39208))
# Resuming threads, just in case
utils.window('suspend_LibraryThread', clear=True)
# Abort reConnection
return
counter += 1
xbmc.sleep(50)
# Delete plex credentials in settings # Delete plex credentials in settings
utils.settings('myplexlogin', value="true") utils.settings('myplexlogin', value="true")
@ -97,18 +112,13 @@ def reConnect():
utils.settings('plexHomeSize', value="1") utils.settings('plexHomeSize', value="1")
utils.settings('plexAvatar', value="") utils.settings('plexAvatar', value="")
# Wait max for 5 seconds for all lib scans to finish # Reset connection details
counter = 0 utils.settings('plex_machineIdentifier', value="")
while utils.window('emby_dbScan') == 'true': utils.settings('plex_servername', value="")
if counter > 100: utils.settings('https', value="")
dialog.ok(heading=addonName, utils.settings('ipaddress', value="")
message=string(39208)) utils.settings('port', value="")
# Resuming threads, just in case
utils.window('suspend_LibraryThread', clear=True)
# Abort reConnection
return
counter += 1
xbmc.sleep(50)
# Log out currently signed in user: # Log out currently signed in user:
utils.window('emby_serverStatus', value="401") utils.window('emby_serverStatus', value="401")
@ -255,7 +265,7 @@ def doMainListing():
addDirectoryItem(label, path) addDirectoryItem(label, path)
# Plex user switch # Plex user switch
addDirectoryItem(string(39200), addDirectoryItem(string(39200) + utils.window('plex_username'),
"plugin://plugin.video.plexkodiconnect/" "plugin://plugin.video.plexkodiconnect/"
"?mode=switchuser") "?mode=switchuser")
@ -1446,6 +1456,19 @@ def getOnDeck(viewid, mediatype, tagname, limit):
for episode in episodes: for episode in episodes:
# There will always be only 1 episode ('limit=1') # There will always be only 1 episode ('limit=1')
li = createListItem(episode) li = createListItem(episode)
# Fix some skin shortcomings
title = episode.get('title', '')
if utils.settings('OnDeckTvAppendSeason') == 'true':
seasonid = episode.get('season')
episodeid = episode.get('episode')
if seasonid and episodeid:
title = ('S' + str(seasonid) + 'E' + str(episodeid)
+ ' - ' + title)
if utils.settings('OnDeckTvAppendShow') == 'true':
show = episode.get('showtitle')
if show:
title = show + ' - ' + title
li.setLabel(title)
xbmcplugin.addDirectoryItem( xbmcplugin.addDirectoryItem(
handle=int(sys.argv[1]), handle=int(sys.argv[1]),
url=episode['file'], url=episode['file'],

View file

@ -3,6 +3,10 @@ import utils
import xbmc import xbmc
import requests import requests
# Disable requests logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
@utils.logging @utils.logging
class image_cache_thread(threading.Thread): class image_cache_thread(threading.Thread):

View file

@ -261,8 +261,6 @@ class InitialSetup():
dialog.ok(heading=self.addonName, dialog.ok(heading=self.addonName,
line1=string(39044)) line1=string(39044))
goToSettings = True goToSettings = True
# Don't start anything because we need these paths first!
utils.window('emby_serverStatus', value="Stop")
# Go to network credentials? # Go to network credentials?
if dialog.yesno(heading=self.addonName, if dialog.yesno(heading=self.addonName,
@ -280,8 +278,9 @@ class InitialSetup():
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')
else: else:
# Open Settings page now? # Open Settings page now? You will need to restart!
if dialog.yesno(heading=self.addonName, if dialog.yesno(heading=self.addonName,
line1=string(39017)): line1=string(39017)):
utils.window('emby_serverStatus', value="Stop")
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')

View file

@ -2055,17 +2055,18 @@ class Music(Items):
if doIndirect: if doIndirect:
# Plex works a bit differently # Plex works a bit differently
path = "%s%s" % (self.server, item[0][0].attrib.get('key')) path = "%s%s" % (self.server, item[0][0].attrib.get('key'))
filename = API.addPlexCredentialsToUrl(path) path = API.addPlexCredentialsToUrl(path)
# Keep path empty to not let Kodi scan it filename = path.rsplit('/', 1)[1]
path = "" path = path.replace(filename, '')
# UPDATE THE SONG ##### # UPDATE THE SONG #####
if update_item: if update_item:
self.logMsg("UPDATE song itemid: %s - Title: %s with path: %s" self.logMsg("UPDATE song itemid: %s - Title: %s with path: %s"
% (itemid, title, path), 1) % (itemid, title, path), 1)
# Update path # Update path
query = "UPDATE path SET strPath = ? WHERE idPath = ?" # Use dummy strHash '123' for Kodi
kodicursor.execute(query, (path, pathid)) query = "UPDATE path SET strPath = ?, strHash = ? WHERE idPath = ?"
kodicursor.execute(query, (path, '123', pathid))
# Update the song entry # Update the song entry
query = ' '.join(( query = ' '.join((
@ -2087,7 +2088,7 @@ class Music(Items):
self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1) self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1)
# Add path # Add path
pathid = kodi_db.addPath(path) pathid = kodi_db.addPath(path, strHash="123")
try: try:
# Get the album # Get the album

View file

@ -47,7 +47,7 @@ class Kodidb_Functions():
self.clientInfo = clientinfo.ClientInfo() self.clientInfo = clientinfo.ClientInfo()
self.artwork = artwork.Artwork() self.artwork = artwork.Artwork()
def addPath(self, path): def addPath(self, path, strHash=None):
# SQL won't return existing paths otherwise # SQL won't return existing paths otherwise
if path is None: if path is None:
path = "" path = ""
@ -64,15 +64,26 @@ class Kodidb_Functions():
except TypeError: except TypeError:
cursor.execute("select coalesce(max(idPath),0) from path") cursor.execute("select coalesce(max(idPath),0) from path")
pathid = cursor.fetchone()[0] + 1 pathid = cursor.fetchone()[0] + 1
query = ( if strHash is None:
''' query = (
INSERT INTO path( '''
idPath, strPath) INSERT INTO path(
idPath, strPath)
VALUES (?, ?) VALUES (?, ?)
''' '''
) )
cursor.execute(query, (pathid, path)) cursor.execute(query, (pathid, path))
else:
query = (
'''
INSERT INTO path(
idPath, strPath, strHash)
VALUES (?, ?, ?)
'''
)
cursor.execute(query, (pathid, path, strHash))
return pathid return pathid
@ -744,6 +755,88 @@ class Kodidb_Functions():
ids.append(row[0]) ids.append(row[0])
return ids return ids
def getIdFromTitle(self, itemdetails):
"""
Returns the Kodi id (e.g. idMovie, idEpisode) from the item's
title (c00), if there is exactly ONE found for the itemtype.
(False otherwise)
itemdetails is the data['item'] response from Kodi
itemdetails for movies:
{
"title":"Kung Fu Panda",
"type":"movie",
"year":2008
}
itemdetails for episodes:
{
"episode":5
"season":5,
"showtitle":"Girls",
"title":"Queen for Two Days",
"type":"episode"
}
"""
try:
type = itemdetails['type']
except:
return False
if type == 'movie':
query = ' '.join((
"SELECT idMovie",
"FROM movie",
"WHERE c00 = ?"
))
try:
rows = self.cursor.execute(query, (itemdetails['title'],))
except:
return False
elif type == 'episode':
query = ' '.join((
"SELECT idShow",
"FROM tvshow",
"WHERE c00 = ?"
))
try:
rows = self.cursor.execute(query, (itemdetails['showtitle'],))
except:
return False
ids = []
for row in rows:
ids.append(row[0])
if len(ids) > 1:
# No unique match possible
return False
showid = ids[0]
query = ' '.join((
"SELECT idEpisode",
"FROM episode",
"WHERE c12 = ? AND c13 = ? AND idShow = ?"
))
try:
rows = self.cursor.execute(
query,
(itemdetails['season'],
itemdetails['episode'],
showid))
except:
return False
else:
return False
ids = []
for row in rows:
ids.append(row[0])
if len(ids) > 1:
# No unique match possible
return False
else:
return ids[0]
def getUnplayedItems(self): def getUnplayedItems(self):
""" """
VIDEOS VIDEOS

View file

@ -10,6 +10,7 @@ import xbmcgui
import downloadutils import downloadutils
import embydb_functions as embydb import embydb_functions as embydb
import kodidb_functions as kodidb
import playbackutils as pbutils import playbackutils as pbutils
import utils import utils
from PlexFunctions import scrobble from PlexFunctions import scrobble
@ -154,11 +155,21 @@ class KodiMonitor(xbmc.Monitor):
# Try to get a Kodi ID # Try to get a Kodi ID
item = data.get('item') item = data.get('item')
try: try:
kodiid = item['id']
type = item['type'] type = item['type']
except (KeyError, TypeError): except:
log("Item is invalid for Plex playstate update.", 0) log("Item is invalid for PMS playstate update.", 0)
return return
try:
kodiid = item['id']
except (KeyError, TypeError):
log('Kodi did not give us a Kodi item id, trying to get from item '
'title', 0)
# Try to get itemid with the element's title
with kodidb.GetKodiDB('video') as kodi_db:
kodiid = kodi_db.getIdFromTitle(item)
if kodiid is False:
log("Item is invalid for PMS playstate update.", 0)
return
# Get Plex' item id # Get Plex' item id
with embydb.GetEmbyDB() as emby_db: with embydb.GetEmbyDB() as emby_db:

View file

@ -4,10 +4,7 @@
from threading import Thread, Lock from threading import Thread, Lock
import Queue import Queue
try: import xml.etree.ElementTree as etree
import xml.etree.cElementTree as etree
except ImportError:
import xml.etree.ElementTree as etree
import xbmc import xbmc
import xbmcgui import xbmcgui
@ -239,6 +236,9 @@ class LibrarySync(Thread):
'enableBackgroundSync') == "true" else False 'enableBackgroundSync') == "true" else False
self.limitindex = int(utils.settings('limitindex')) self.limitindex = int(utils.settings('limitindex'))
if utils.settings('emby_pathverified') == 'true':
utils.window('emby_pathverified', value='true')
# Time offset between Kodi and PMS in seconds (=Koditime - PMStime) # Time offset between Kodi and PMS in seconds (=Koditime - PMStime)
self.timeoffset = 0 self.timeoffset = 0
# Time in seconds to look into the past when looking for PMS changes # Time in seconds to look into the past when looking for PMS changes
@ -326,7 +326,7 @@ class LibrarySync(Thread):
# Get the Plex item's metadata # Get the Plex item's metadata
xml = PlexFunctions.GetPlexMetadata(plexId) xml = PlexFunctions.GetPlexMetadata(plexId)
if not xml: if xml is None:
self.logMsg("Could not download metadata, aborting time sync", -1) self.logMsg("Could not download metadata, aborting time sync", -1)
return return
libraryId = xml[0].attrib['librarySectionID'] libraryId = xml[0].attrib['librarySectionID']
@ -493,7 +493,7 @@ class LibrarySync(Thread):
updatedAt=self.getPMSfromKodiTime(lastSync), updatedAt=self.getPMSfromKodiTime(lastSync),
containerSize=self.limitindex) containerSize=self.limitindex)
# Just skip if something went wrong # Just skip if something went wrong
if not items: if items is None:
continue continue
# Get one itemtype, because they're the same in the PMS section # Get one itemtype, because they're the same in the PMS section
try: try:
@ -528,7 +528,7 @@ class LibrarySync(Thread):
view['id'], view['id'],
lastViewedAt=self.getPMSfromKodiTime(lastSync), lastViewedAt=self.getPMSfromKodiTime(lastSync),
containerSize=self.limitindex) containerSize=self.limitindex)
if not items: if items is None:
continue continue
for item in items: for item in items:
itemId = item.attrib.get('ratingKey') itemId = item.attrib.get('ratingKey')
@ -635,6 +635,12 @@ class LibrarySync(Thread):
# Add sources # Add sources
utils.sourcesXML() utils.sourcesXML()
# Deactivate Kodi popup showing that it's (unsuccessfully) trying to
# scan music folders
if self.enableMusic:
utils.musiclibXML()
utils.advancedSettingsXML()
# Set new timestamp NOW because sync might take a while # Set new timestamp NOW because sync might take a while
self.saveLastSync() self.saveLastSync()
@ -1075,7 +1081,7 @@ class LibrarySync(Thread):
viewName = view['name'] viewName = view['name']
all_plexmovies = PlexFunctions.GetPlexSectionResults( all_plexmovies = PlexFunctions.GetPlexSectionResults(
viewId, args=None, containerSize=self.limitindex) viewId, args=None, containerSize=self.limitindex)
if not all_plexmovies: if all_plexmovies is None:
self.logMsg("Couldnt get section items, aborting for view.", 1) self.logMsg("Couldnt get section items, aborting for view.", 1)
continue continue
# Populate self.updatelist and self.allPlexElementsId # Populate self.updatelist and self.allPlexElementsId
@ -1209,7 +1215,7 @@ class LibrarySync(Thread):
viewName = view['name'] viewName = view['name']
allPlexTvShows = PlexFunctions.GetPlexSectionResults( allPlexTvShows = PlexFunctions.GetPlexSectionResults(
viewId, containerSize=self.limitindex) viewId, containerSize=self.limitindex)
if not allPlexTvShows: if allPlexTvShows is None:
self.logMsg( self.logMsg(
"Error downloading show view xml for view %s" % viewId, -1) "Error downloading show view xml for view %s" % viewId, -1)
continue continue
@ -1236,7 +1242,7 @@ class LibrarySync(Thread):
# Grab all seasons to tvshow from PMS # Grab all seasons to tvshow from PMS
seasons = PlexFunctions.GetAllPlexChildren( seasons = PlexFunctions.GetAllPlexChildren(
tvShowId, containerSize=self.limitindex) tvShowId, containerSize=self.limitindex)
if not seasons: if seasons is None:
self.logMsg( self.logMsg(
"Error downloading season xml for show %s" % tvShowId, -1) "Error downloading season xml for show %s" % tvShowId, -1)
continue continue
@ -1261,7 +1267,7 @@ class LibrarySync(Thread):
# Grab all episodes to tvshow from PMS # Grab all episodes to tvshow from PMS
episodes = PlexFunctions.GetAllPlexLeaves( episodes = PlexFunctions.GetAllPlexLeaves(
view['id'], containerSize=self.limitindex) view['id'], containerSize=self.limitindex)
if not episodes: if episodes is None:
self.logMsg( self.logMsg(
"Error downloading episod xml for view %s" "Error downloading episod xml for view %s"
% view.get('name'), -1) % view.get('name'), -1)
@ -1359,7 +1365,7 @@ class LibrarySync(Thread):
viewName = view['name'] viewName = view['name']
itemsXML = PlexFunctions.GetPlexSectionResults( itemsXML = PlexFunctions.GetPlexSectionResults(
viewId, args=urlArgs, containerSize=self.limitindex) viewId, args=urlArgs, containerSize=self.limitindex)
if not itemsXML: if itemsXML is None:
self.logMsg("Error downloading xml for view %s" self.logMsg("Error downloading xml for view %s"
% viewId, -1) % viewId, -1)
continue continue
@ -1401,6 +1407,7 @@ class LibrarySync(Thread):
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) self.logMsg('LibrarySync thread crashed', -1)
self.logMsg('Error message: %s' % e, -1)
# Library sync thread has crashed # Library sync thread has crashed
xbmcgui.Dialog().ok( xbmcgui.Dialog().ok(
heading=self.addonName, heading=self.addonName,

View file

@ -275,9 +275,7 @@ class PlaybackUtils():
# Append external subtitles to stream # Append external subtitles to stream
playmethod = utils.window('%s.playmethod' % embyitem) playmethod = utils.window('%s.playmethod' % embyitem)
# Only for direct stream if playmethod in ("DirectStream", "DirectPlay"):
if playmethod in ("DirectStream"):
# Direct play automatically appends external
subtitles = self.API.externalSubs(playurl) subtitles = self.API.externalSubs(playurl)
listitem.setSubtitles(subtitles) listitem.setSubtitles(subtitles)

View file

@ -537,6 +537,7 @@ class Player(xbmc.Player):
url = "{server}/emby/Items/%s?format=json" % itemid url = "{server}/emby/Items/%s?format=json" % itemid
log("Deleting request: %s" % itemid, 1) log("Deleting request: %s" % itemid, 1)
doUtils(url, type="DELETE") doUtils(url, type="DELETE")
self.stopPlayback(data)
# Clean the WINDOW properties # Clean the WINDOW properties
for filename in self.played_info: for filename in self.played_info:
@ -552,7 +553,6 @@ class Player(xbmc.Player):
) )
for item in cleanup: for item in cleanup:
utils.window(item, clear=True) utils.window(item, clear=True)
self.stopPlayback(data)
# Stop transcoding # Stop transcoding
if playMethod == "Transcode": if playMethod == "Transcode":
@ -564,7 +564,7 @@ class Player(xbmc.Player):
self.played_info.clear() self.played_info.clear()
def stopPlayback(self, data): def stopPlayback(self, data):
self.logMsg("stopPlayback called", 2) self.logMsg("stopPlayback called", 1)
itemId = data['item_id'] itemId = data['item_id']
playTime = data['currentPosition'] playTime = data['currentPosition']

View file

@ -32,8 +32,11 @@ import threading
import time import time
import urllib2 import urllib2
import xbmc
import downloadutils import downloadutils
from PlexFunctions import PMSHttpsEnabled from PlexFunctions import PMSHttpsEnabled
from utils import window
class plexgdm: class plexgdm:
@ -119,7 +122,7 @@ class plexgdm:
self.__printDebug("Sending registration data: HTTP/1.0 200 OK\r\n%s" % (self.client_data), 3) self.__printDebug("Sending registration data: HTTP/1.0 200 OK\r\n%s" % (self.client_data), 3)
self.client_registered = True self.client_registered = True
time.sleep(0.5) xbmc.sleep(500)
self.__printDebug("Client Update loop stopped",1) self.__printDebug("Client Update loop stopped",1)
@ -141,9 +144,15 @@ class plexgdm:
return False return False
try: try:
media_server=self.server_list[0]['server'] for server in self.server_list:
media_port=self.server_list[0]['port'] if server['uuid'] == window('plex_machineIdentifier'):
scheme = self.server_list[0]['protocol'] media_server = server['server']
media_port = server['port']
scheme = server['protocol']
break
else:
self.__printDebug("Did not find our server!", 2)
return False
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 = self.download( client_result = self.download(
@ -240,15 +249,47 @@ class plexgdm:
update['class'] = each.split(':')[1].strip() update['class'] = each.split(':')[1].strip()
# Quickly test if we need https # Quickly test if we need https
if PMSHttpsEnabled( https = PMSHttpsEnabled(
'%s:%s' % (update['server'], update['port'])): '%s:%s' % (update['server'], update['port']))
if https is None:
# Error contacting server
continue
elif https:
update['protocol'] = 'https' update['protocol'] = 'https'
else: else:
update['protocol'] = 'http' update['protocol'] = 'http'
discovered_servers.append(update) discovered_servers.append(update)
# Append REMOTE PMS that we haven't found yet; if necessary
currServer = window('pms_server')
if currServer:
currServerProt, currServerIP, currServerPort = \
currServer.split(':')
currServerIP = currServerIP.replace('/', '')
for server in discovered_servers:
if server['server'] == currServerIP:
break
else:
# Currently active server was not discovered via GDM; ADD
update = {
'port': currServerPort,
'protocol': currServerProt,
'class': None,
'content-type': 'plex/media-server',
'discovery': 'auto',
'master': 1,
'owned': '1',
'role': 'master',
'server': currServerIP,
'serverName': window('plex_servername'),
'updated': int(time.time()),
'uuid': window('plex_machineIdentifier'),
'version': 'irrelevant'
}
discovered_servers.append(update)
self.server_list = discovered_servers self.server_list = discovered_servers
if not self.server_list: if not self.server_list:
self.__printDebug("No servers have been discovered",1) self.__printDebug("No servers have been discovered",1)
else: else:
@ -292,7 +333,7 @@ class plexgdm:
if discovery_count > self.discovery_interval: if discovery_count > self.discovery_interval:
self.discover() self.discover()
discovery_count=0 discovery_count=0
time.sleep(1) xbmc.sleep(1000)
def start_discovery(self, daemon = False): def start_discovery(self, daemon = False):
if not self._discovery_is_running: if not self._discovery_is_running:
@ -326,8 +367,8 @@ if __name__ == '__main__':
client.start_all() client.start_all()
while not client.discovery_complete: while not client.discovery_complete:
print "Waiting for results" print "Waiting for results"
time.sleep(1) xbmc.sleep(1000)
time.sleep(20) xbmc.sleep(20000)
print client.getServerList() print client.getServerList()
if client.check_client_registration(): if client.check_client_registration():
print "Successfully registered" print "Successfully registered"

View file

@ -1,12 +1,12 @@
import re import re
import threading import threading
from xml.dom.minidom import parseString # from xml.dom.minidom import parseString
from functions import * from functions import *
from settings import settings from settings import settings
from httppersist import requests from httppersist import requests
from xbmc import Player from xbmc import Player
import xbmcgui # import xbmcgui
import downloadutils import downloadutils
from utils import window from utils import window
import PlexFunctions as pf import PlexFunctions as pf
@ -67,18 +67,19 @@ class SubscriptionManager:
ret += ' />' ret += ' />'
return ret return ret
WINDOW = xbmcgui.Window(10000)
# pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server')) # pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server'))
# userId = str(WINDOW.getProperty('currUserId')) # userId = str(WINDOW.getProperty('currUserId'))
# pbmc_server = str(WINDOW.getProperty('pms_server')) pbmc_server = window('pms_server')
pbmc_server = None if pbmc_server:
(self.protocol, self.server, self.port) = \
pbmc_server.split(':')
self.server = self.server.replace('/', '')
keyid = None keyid = None
count = 0 count = 0
while not keyid: while not keyid:
if count > 300: if count > 300:
break break
keyid = WINDOW.getProperty('Plex_currently_playing_itemid') keyid = window('Plex_currently_playing_itemid')
xbmc.sleep(100) xbmc.sleep(100)
count += 1 count += 1
if keyid: if keyid:
@ -87,8 +88,6 @@ class SubscriptionManager:
ret += ' location="%s"' % (self.mainlocation) ret += ' location="%s"' % (self.mainlocation)
ret += ' key="%s"' % (self.lastkey) ret += ' key="%s"' % (self.lastkey)
ret += ' ratingKey="%s"' % (self.lastratingkey) ret += ' ratingKey="%s"' % (self.lastratingkey)
if pbmc_server:
(self.server, self.port) = pbmc_server.split(':')
serv = getServerByHost(self.server) serv = getServerByHost(self.server)
if info.get('playQueueID'): if info.get('playQueueID'):
self.containerKey = "/playQueues/%s" % info.get('playQueueID') self.containerKey = "/playQueues/%s" % info.get('playQueueID')

View file

@ -76,8 +76,8 @@ class UserClient(threading.Thread):
settings = utils.settings settings = utils.settings
# Original host # Original host
self.machineIdentifier = utils.settings('plex_machineIdentifier') self.machineIdentifier = settings('plex_machineIdentifier')
self.servername = utils.settings('plex_servername') self.servername = settings('plex_servername')
HTTPS = settings('https') == "true" HTTPS = settings('https') == "true"
host = settings('ipaddress') host = settings('ipaddress')
port = settings('port') port = settings('port')
@ -91,14 +91,11 @@ class UserClient(threading.Thread):
# If https is true # If https is true
if prefix and HTTPS: if prefix and HTTPS:
server = "https://%s" % server server = "https://%s" % server
return server
# If https is false # If https is false
elif prefix and not HTTPS: elif prefix and not HTTPS:
server = "http://%s" % server server = "http://%s" % server
return server self.logMsg('Returning active server: %s' % server)
# If only the host:port is required return server
elif not prefix:
return server
def getSSLverify(self): def getSSLverify(self):
# Verify host certificate # Verify host certificate
@ -410,6 +407,7 @@ class UserClient(threading.Thread):
self.auth = False self.auth = False
if self.authenticate(): if self.authenticate():
# Successfully authenticated and loaded a user # Successfully authenticated and loaded a user
log("Successfully authenticated!", 1)
log("Current user: %s" % self.currUser, 1) log("Current user: %s" % self.currUser, 1)
log("Current userId: %s" % self.currUserId, 1) log("Current userId: %s" % self.currUserId, 1)
log("Current accessToken: xxxx", 1) log("Current accessToken: xxxx", 1)

View file

@ -148,7 +148,7 @@ def ThreadMethodsAdditionalStop(windowAttribute):
def wrapper(cls): def wrapper(cls):
def threadStopped(self): def threadStopped(self):
return (self._threadStopped or return (self._threadStopped or
(window('terminateNow') == "true") or (window('plex_terminateNow') == "true") or
window(windowAttribute) == "true") window(windowAttribute) == "true")
cls.threadStopped = threadStopped cls.threadStopped = threadStopped
return cls return cls
@ -209,7 +209,7 @@ def ThreadMethods(cls):
cls.threadSuspended = threadSuspended cls.threadSuspended = threadSuspended
def threadStopped(self): def threadStopped(self):
return self._threadStopped or (window('terminateNow') == 'true') return self._threadStopped or (window('plex_terminateNow') == 'true')
cls.threadStopped = threadStopped cls.threadStopped = threadStopped
# Return class to render this a decorator # Return class to render this a decorator
@ -625,6 +625,85 @@ def indent(elem, level=0):
if level and (not elem.tail or not elem.tail.strip()): if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i elem.tail = i
def musiclibXML():
"""
Deactivates Kodi trying to scan music library on startup
Changes guisettings.xml in Kodi userdata folder:
updateonstartup: set to "false"
"""
path = xbmc.translatePath("special://profile/").decode('utf-8')
xmlpath = "%sguisettings.xml" % path
try:
xmlparse = etree.parse(xmlpath)
except:
# Document is blank or missing
root = etree.Element('settings')
else:
root = xmlparse.getroot()
music = root.find('musiclibrary')
if music is None:
music = etree.SubElement(root, 'musiclibrary')
startup = music.find('updateonstartup')
if startup is None:
# Setting does not exist yet; create it
startup = etree.SubElement(music,
'updateonstartup',
attrib={'default': "true"}).text = "false"
else:
startup.text = "false"
# Prettify and write to file
try:
indent(root)
except:
pass
etree.ElementTree(root).write(xmlpath)
def advancedSettingsXML():
"""
Deactivates Kodi popup for scanning of music library
Changes advancedsettings.xml, musiclibrary:
backgroundupdate set to "true"
"""
path = xbmc.translatePath("special://profile/").decode('utf-8')
xmlpath = "%sadvancedsettings.xml" % path
try:
xmlparse = etree.parse(xmlpath)
except:
# Document is blank or missing
root = etree.Element('advancedsettings')
else:
root = xmlparse.getroot()
music = root.find('musiclibrary')
if music is None:
music = etree.SubElement(root, 'musiclibrary')
backgroundupdate = music.find('backgroundupdate')
if backgroundupdate is None:
# Setting does not exist yet; create it
backgroundupdate = etree.SubElement(
music,
'backgroundupdate').text = "true"
else:
backgroundupdate.text = "true"
# Prettify and write to file
try:
indent(root)
except:
pass
etree.ElementTree(root).write(xmlpath)
def sourcesXML(): def sourcesXML():
# To make Master lock compatible # To make Master lock compatible
path = xbmc.translatePath("special://profile/").decode('utf-8') path = xbmc.translatePath("special://profile/").decode('utf-8')

View file

@ -74,6 +74,7 @@
<setting id="enableImportSongRating" type="bool" label="30524" default="true" visible="false"/> <setting id="enableImportSongRating" type="bool" label="30524" default="true" visible="false"/>
<setting id="enableExportSongRating" type="bool" label="30525" default="false" visible="false" /> <setting id="enableExportSongRating" type="bool" label="30525" default="false" visible="false" />
<setting id="enableUpdateSongRating" type="bool" label="30526" default="false" visible="false" /> <setting id="enableUpdateSongRating" type="bool" label="30526" default="false" visible="false" />
<setting id="emby_pathverified" type="bool" default="false" visible="false" /> <!-- If 'false': one single warning message pops up if PKC cannot verify direct paths -->
</category> </category>
<category label="30516"><!-- Playback --> <category label="30516"><!-- Playback -->
@ -108,13 +109,21 @@
<setting id="newmusictime" type="number" label="30533" visible="false" default="2" option="int" subsetting="true" /> <setting id="newmusictime" type="number" label="30533" visible="false" default="2" option="int" subsetting="true" />
</category> </category>
--> -->
<category label="39045"><!-- Appearance Tweaks -->
<setting type="lsep" label="39046" />
<setting id="OnDeckTvAppendShow" type="bool" label="39047" default="false" /><!--On Deck view: Append show title to episode-->
<setting id="OnDeckTvAppendSeason" type="bool" label="39048" default="false" /><!--On Deck view: Append season number to episode-->
</category>
<category label="30022"><!-- Advanced --> <category label="30022"><!-- Advanced -->
<setting id="logLevel" type="enum" label="30004" values="Disabled|Info|Debug" default="1" /> <setting id="logLevel" type="enum" label="30004" values="Disabled|Info|Debug" default="1" />
<setting id="startupDelay" type="number" label="30529" default="0" option="int" /> <setting id="startupDelay" type="number" label="30529" default="0" option="int" />
<setting id="connectMsg" type="bool" label="30249" default="true" /> <setting id="connectMsg" type="bool" label="30249" default="true" />
<setting label="39018" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=repair)" option="close" /> <!-- Repair local database (force update all content) --> <setting label="39018" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=repair)" option="close" /> <!-- Repair local database (force update all content) -->
<setting label="39019" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=reset)" option="close" /> <!-- Perform local database reset (full resync) -->
<setting label="39020" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=texturecache)" option="close" /> <!-- Cache all images to Kodi texture cache --> <setting label="39020" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=texturecache)" option="close" /> <!-- Cache all images to Kodi texture cache -->
<setting label="39021" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=thememedia)" option="close" visible="false" /> <!-- Sync Plex Theme Media to Kodi --> <setting label="39021" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=thememedia)" option="close" visible="false" /> <!-- Sync Plex Theme Media to Kodi -->
<setting type="lsep" label="39049" /><!-- Nothing works? Try a full reset -->
<setting label="39019" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=reset)" option="close" /> <!-- Partial or full reset of Database and PKC -->
</category> </category>
</settings> </settings>

View file

@ -28,6 +28,7 @@ import player
import utils import utils
import videonodes import videonodes
import websocket_client as wsc import websocket_client as wsc
import downloadutils
import PlexAPI import PlexAPI
import PlexCompanion import PlexCompanion
@ -80,7 +81,10 @@ class Service():
"emby_initialScan", "emby_customplaylist", "emby_playbackProps", "emby_initialScan", "emby_customplaylist", "emby_playbackProps",
"plex_runLibScan", "plex_username", "pms_token", "plex_token", "plex_runLibScan", "plex_username", "pms_token", "plex_token",
"pms_server", "plex_machineIdentifier", "plex_servername", "pms_server", "plex_machineIdentifier", "plex_servername",
"plex_authenticated" "plex_authenticated", "EmbyUserImage", "useDirectPaths",
"replaceSMB", "remapSMB", "remapSMBmovieOrg", "remapSMBtvOrg",
"remapSMBmusicOrg", "remapSMBmovieNew", "remapSMBtvNew",
"remapSMBmusicNew", "suspend_LibraryThread", "plex_terminateNow"
] ]
for prop in properties: for prop in properties:
window(prop, clear=True) window(prop, clear=True)
@ -132,12 +136,10 @@ class Service():
# 3. User has access to the server # 3. User has access to the server
if window('emby_online') == "true": if window('emby_online') == "true":
# Emby server is online # Emby server is online
# Verify if user is set and has access to the server # Verify if user is set and has access to the server
if (user.currUser is not None) and user.HasAccess: if (user.currUser is not None) and user.HasAccess:
# If an item is playing
# If an item is playing
if xplayer.isPlaying(): if xplayer.isPlaying():
try: try:
# Update and report progress # Update and report progress
@ -190,10 +192,15 @@ class Service():
ws.start() ws.start()
# Start the syncing thread # Start the syncing thread
if not self.library_running: if not self.library_running:
log('Starting libary sync thread', 1)
self.library_running = True self.library_running = True
library.start() library.start()
# Start the Plex Companion thread
if not self.plexCompanion_running and \
self.runPlexCompanion == "true":
self.plexCompanion_running = True
plexCompanion.start()
else: else:
if (user.currUser is None) and self.warn_auth: if (user.currUser is None) and self.warn_auth:
# Alert user is not authenticated and suppress future warning # Alert user is not authenticated and suppress future warning
self.warn_auth = False self.warn_auth = False
@ -213,6 +220,7 @@ class Service():
if monitor.waitForAbort(5): if monitor.waitForAbort(5):
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
break break
xbmc.sleep(50)
else: else:
# Wait until Emby server is online # Wait until Emby server is online
# or Kodi is shut down. # or Kodi is shut down.
@ -235,9 +243,7 @@ class Service():
icon="special://home/addons/plugin.video." icon="special://home/addons/plugin.video."
"plexkodiconnect/icon.png", "plexkodiconnect/icon.png",
sound=False) sound=False)
self.server_online = False self.server_online = False
else: else:
# Server is online # Server is online
if not self.server_online: if not self.server_online:
@ -254,9 +260,8 @@ class Service():
"plexkodiconnect/icon.png", "plexkodiconnect/icon.png",
time=2000, time=2000,
sound=False) sound=False)
self.server_online = True self.server_online = True
log("Server is online and ready.", 1) log("Server %s is online and ready." % server, 1)
window('emby_online', value="true") window('emby_online', value="true")
# Start the userclient thread # Start the userclient thread
@ -264,36 +269,31 @@ class Service():
self.userclient_running = True self.userclient_running = True
user.start() user.start()
# Start the Plex Companion thread
if not self.plexCompanion_running and \
self.runPlexCompanion == "true":
self.plexCompanion_running = True
plexCompanion.start()
break break
if monitor.waitForAbort(1): if monitor.waitForAbort(1):
# Abort was requested while waiting. # Abort was requested while waiting.
break break
xbmc.sleep(50)
if monitor.waitForAbort(1): if monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
break break
##### Emby thread is terminating. ##### # Terminating PlexKodiConnect
# Tell all threads to terminate # Tell all threads to terminate (e.g. several lib sync threads)
utils.window('terminateNow', value='true') utils.window('plex_terminateNow', value='true')
try: try:
if self.plexCompanion_running: plexCompanion.stopThread()
plexCompanion.stopThread()
except: except:
xbmc.log('plexCompanion already shut down') xbmc.log('plexCompanion already shut down')
try: try:
if self.library_running: library.stopThread()
library.stopThread()
except: except:
xbmc.log('Library sync already shut down') xbmc.log('Library sync already shut down')
@ -303,8 +303,7 @@ class Service():
xbmc.log('Websocket client already shut down') xbmc.log('Websocket client already shut down')
try: try:
if self.userclient_running: user.stopThread()
user.stopThread()
except: except:
xbmc.log('User client already shut down') xbmc.log('User client already shut down')
@ -314,8 +313,8 @@ class Service():
delay = int(utils.settings('startupDelay')) delay = int(utils.settings('startupDelay'))
xbmc.log("Delaying Plex startup by: %s sec..." % delay) xbmc.log("Delaying Plex startup by: %s sec..." % delay)
# Plex: add 3 seconds just for good measure # Plex: add 5 seconds just for good measure
if delay and xbmc.Monitor().waitForAbort(delay+3): if delay and xbmc.Monitor().waitForAbort(delay+5):
# Start the service # Start the service
xbmc.log("Abort requested while waiting. Emby for kodi not started.") xbmc.log("Abort requested while waiting. Emby for kodi not started.")
else: else: