Merge branch 'master' into develop
This commit is contained in:
commit
98d11ceb2d
25 changed files with 483 additions and 152 deletions
18
README.md
18
README.md
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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'],
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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>
|
||||||
|
|
49
service.py
49
service.py
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue