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.
**Known 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.
**Known "Larger" Issues:**
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.
- **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
**What could be in the pipeline?**
**What could be in the pipeline for future development?**
- Watch Later
- Playlists
- Homevideos
- Pictures
- 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)

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect"
name="PlexKodiConnect"
version="1.0.14"
version="1.0.16"
provider-name="croneter">
<requires>
<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
- Fix TV shows rating not showing up
- Fix music libraries being scanned twice

View file

@ -357,11 +357,10 @@
<string id="39014">Please sign in to plex.tv.</string>
<string id="39015">Problems connecting to server. Pick another server?</string>
<string id="39016">Disable Plex music library?</string>
<string id="39017">Would you now like to go to the plugin's settings?
(This is hopefully unneccessary ;-))</string>
<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>
<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="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</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="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 -->
<string id="39200">Switch Plex Home User</string>
<string id="39200">Log-out Plex Home User: </string>
<string id="39201">Settings</string>
<string id="39202">Network credentials</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="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="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="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="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</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="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 -->
<string id="39200">Plex Home Benutzer wechseln</string>
<string id="39200">Plex Home Benutzer abmelden: </string>
<string id="39201">Einstellungen</string>
<string id="39202">Netzwerk Credentials</string>
<string id="39203">Plex Playlisten und Nodes zurücksetzen</string>

View file

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

View file

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

View file

@ -4,12 +4,17 @@ from ast import literal_eval
from urlparse import urlparse, parse_qs
import re
from copy import deepcopy
import requests
from xbmcaddon import Addon
import downloadutils
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')
title = "%s %s" % (addonName, __name__)
@ -394,7 +399,8 @@ def getPlexRepeat(kodiRepeat):
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)
@ -403,17 +409,37 @@ def PMSHttpsEnabled(url):
Prefers HTTPS over HTTP
"""
xml = downloadutils.DownloadUtils().downloadUrl(
'https://%s/identity' % url)
# True if https, False if http
answer = True
try:
# received a valid XML - https connection is possible
xml.attrib
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1)
return True
except:
# couldn't get an xml - switch to http traffic
logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1)
return False
# Don't use downloadutils here, otherwise we may get un-authorized!
res = requests.get('https://%s/identity' % url,
headers={},
verify=False,
timeout=(3, 10))
# Don't verify SSL since we can connect for sure then!
except requests.exceptions.ConnectionError as e:
# 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):

View file

@ -15,6 +15,10 @@ import xbmcvfs
import utils
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 requests
import xml.etree.ElementTree as etree
# import logging
# import xbmc
@ -13,10 +14,6 @@ import utils
import clientinfo
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"):
# 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")
self.logMsg("HTTP Error: %s" % e, 0)
xbmcgui.Dialog().notification(

View file

@ -81,13 +81,28 @@ def reConnect():
utils.logMsg("entrypoint reConnect",
"Connection resets requested", 0)
dialog = xbmcgui.Dialog()
# Resetting, please wait
dialog.notification(
heading=addonName,
message=string(39207),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
time=2000,
sound=False)
# Pause library sync thread - user needs to be auth in order to sync
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
utils.settings('myplexlogin', value="true")
@ -97,18 +112,13 @@ def reConnect():
utils.settings('plexHomeSize', value="1")
utils.settings('plexAvatar', value="")
# Wait max for 5 seconds for all lib scans to finish
counter = 0
while utils.window('emby_dbScan') == 'true':
if counter > 100:
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)
# Reset connection details
utils.settings('plex_machineIdentifier', value="")
utils.settings('plex_servername', value="")
utils.settings('https', value="")
utils.settings('ipaddress', value="")
utils.settings('port', value="")
# Log out currently signed in user:
utils.window('emby_serverStatus', value="401")
@ -255,7 +265,7 @@ def doMainListing():
addDirectoryItem(label, path)
# Plex user switch
addDirectoryItem(string(39200),
addDirectoryItem(string(39200) + utils.window('plex_username'),
"plugin://plugin.video.plexkodiconnect/"
"?mode=switchuser")
@ -1446,6 +1456,19 @@ def getOnDeck(viewid, mediatype, tagname, limit):
for episode in episodes:
# There will always be only 1 episode ('limit=1')
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(
handle=int(sys.argv[1]),
url=episode['file'],

View file

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

View file

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

View file

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

View file

@ -47,7 +47,7 @@ class Kodidb_Functions():
self.clientInfo = clientinfo.ClientInfo()
self.artwork = artwork.Artwork()
def addPath(self, path):
def addPath(self, path, strHash=None):
# SQL won't return existing paths otherwise
if path is None:
path = ""
@ -64,15 +64,26 @@ class Kodidb_Functions():
except TypeError:
cursor.execute("select coalesce(max(idPath),0) from path")
pathid = cursor.fetchone()[0] + 1
query = (
'''
INSERT INTO path(
idPath, strPath)
if strHash is None:
query = (
'''
INSERT INTO path(
idPath, strPath)
VALUES (?, ?)
'''
)
cursor.execute(query, (pathid, path))
VALUES (?, ?)
'''
)
cursor.execute(query, (pathid, path))
else:
query = (
'''
INSERT INTO path(
idPath, strPath, strHash)
VALUES (?, ?, ?)
'''
)
cursor.execute(query, (pathid, path, strHash))
return pathid
@ -744,6 +755,88 @@ class Kodidb_Functions():
ids.append(row[0])
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):
"""
VIDEOS

View file

@ -10,6 +10,7 @@ import xbmcgui
import downloadutils
import embydb_functions as embydb
import kodidb_functions as kodidb
import playbackutils as pbutils
import utils
from PlexFunctions import scrobble
@ -154,11 +155,21 @@ class KodiMonitor(xbmc.Monitor):
# Try to get a Kodi ID
item = data.get('item')
try:
kodiid = item['id']
type = item['type']
except (KeyError, TypeError):
log("Item is invalid for Plex playstate update.", 0)
except:
log("Item is invalid for PMS playstate update.", 0)
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
with embydb.GetEmbyDB() as emby_db:

View file

@ -4,10 +4,7 @@
from threading import Thread, Lock
import Queue
try:
import xml.etree.cElementTree as etree
except ImportError:
import xml.etree.ElementTree as etree
import xml.etree.ElementTree as etree
import xbmc
import xbmcgui
@ -239,6 +236,9 @@ class LibrarySync(Thread):
'enableBackgroundSync') == "true" else False
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)
self.timeoffset = 0
# 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
xml = PlexFunctions.GetPlexMetadata(plexId)
if not xml:
if xml is None:
self.logMsg("Could not download metadata, aborting time sync", -1)
return
libraryId = xml[0].attrib['librarySectionID']
@ -493,7 +493,7 @@ class LibrarySync(Thread):
updatedAt=self.getPMSfromKodiTime(lastSync),
containerSize=self.limitindex)
# Just skip if something went wrong
if not items:
if items is None:
continue
# Get one itemtype, because they're the same in the PMS section
try:
@ -528,7 +528,7 @@ class LibrarySync(Thread):
view['id'],
lastViewedAt=self.getPMSfromKodiTime(lastSync),
containerSize=self.limitindex)
if not items:
if items is None:
continue
for item in items:
itemId = item.attrib.get('ratingKey')
@ -635,6 +635,12 @@ class LibrarySync(Thread):
# Add sources
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
self.saveLastSync()
@ -1075,7 +1081,7 @@ class LibrarySync(Thread):
viewName = view['name']
all_plexmovies = PlexFunctions.GetPlexSectionResults(
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)
continue
# Populate self.updatelist and self.allPlexElementsId
@ -1209,7 +1215,7 @@ class LibrarySync(Thread):
viewName = view['name']
allPlexTvShows = PlexFunctions.GetPlexSectionResults(
viewId, containerSize=self.limitindex)
if not allPlexTvShows:
if allPlexTvShows is None:
self.logMsg(
"Error downloading show view xml for view %s" % viewId, -1)
continue
@ -1236,7 +1242,7 @@ class LibrarySync(Thread):
# Grab all seasons to tvshow from PMS
seasons = PlexFunctions.GetAllPlexChildren(
tvShowId, containerSize=self.limitindex)
if not seasons:
if seasons is None:
self.logMsg(
"Error downloading season xml for show %s" % tvShowId, -1)
continue
@ -1261,7 +1267,7 @@ class LibrarySync(Thread):
# Grab all episodes to tvshow from PMS
episodes = PlexFunctions.GetAllPlexLeaves(
view['id'], containerSize=self.limitindex)
if not episodes:
if episodes is None:
self.logMsg(
"Error downloading episod xml for view %s"
% view.get('name'), -1)
@ -1359,7 +1365,7 @@ class LibrarySync(Thread):
viewName = view['name']
itemsXML = PlexFunctions.GetPlexSectionResults(
viewId, args=urlArgs, containerSize=self.limitindex)
if not itemsXML:
if itemsXML is None:
self.logMsg("Error downloading xml for view %s"
% viewId, -1)
continue
@ -1401,6 +1407,7 @@ class LibrarySync(Thread):
except Exception as e:
utils.window('emby_dbScan', clear=True)
self.logMsg('LibrarySync thread crashed', -1)
self.logMsg('Error message: %s' % e, -1)
# Library sync thread has crashed
xbmcgui.Dialog().ok(
heading=self.addonName,

View file

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

View file

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

View file

@ -32,8 +32,11 @@ import threading
import time
import urllib2
import xbmc
import downloadutils
from PlexFunctions import PMSHttpsEnabled
from utils import window
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.client_registered = True
time.sleep(0.5)
xbmc.sleep(500)
self.__printDebug("Client Update loop stopped",1)
@ -141,9 +144,15 @@ class plexgdm:
return False
try:
media_server=self.server_list[0]['server']
media_port=self.server_list[0]['port']
scheme = self.server_list[0]['protocol']
for server in self.server_list:
if server['uuid'] == window('plex_machineIdentifier'):
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)
client_result = self.download(
@ -240,15 +249,47 @@ class plexgdm:
update['class'] = each.split(':')[1].strip()
# Quickly test if we need https
if PMSHttpsEnabled(
'%s:%s' % (update['server'], update['port'])):
https = PMSHttpsEnabled(
'%s:%s' % (update['server'], update['port']))
if https is None:
# Error contacting server
continue
elif https:
update['protocol'] = 'https'
else:
update['protocol'] = 'http'
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
if not self.server_list:
self.__printDebug("No servers have been discovered",1)
else:
@ -292,7 +333,7 @@ class plexgdm:
if discovery_count > self.discovery_interval:
self.discover()
discovery_count=0
time.sleep(1)
xbmc.sleep(1000)
def start_discovery(self, daemon = False):
if not self._discovery_is_running:
@ -326,8 +367,8 @@ if __name__ == '__main__':
client.start_all()
while not client.discovery_complete:
print "Waiting for results"
time.sleep(1)
time.sleep(20)
xbmc.sleep(1000)
xbmc.sleep(20000)
print client.getServerList()
if client.check_client_registration():
print "Successfully registered"

View file

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

View file

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

View file

@ -148,7 +148,7 @@ def ThreadMethodsAdditionalStop(windowAttribute):
def wrapper(cls):
def threadStopped(self):
return (self._threadStopped or
(window('terminateNow') == "true") or
(window('plex_terminateNow') == "true") or
window(windowAttribute) == "true")
cls.threadStopped = threadStopped
return cls
@ -209,7 +209,7 @@ def ThreadMethods(cls):
cls.threadSuspended = threadSuspended
def threadStopped(self):
return self._threadStopped or (window('terminateNow') == 'true')
return self._threadStopped or (window('plex_terminateNow') == 'true')
cls.threadStopped = threadStopped
# 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()):
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():
# To make Master lock compatible
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="enableExportSongRating" type="bool" label="30525" 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 label="30516"><!-- Playback -->
@ -108,13 +109,21 @@
<setting id="newmusictime" type="number" label="30533" visible="false" default="2" option="int" subsetting="true" />
</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 -->
<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="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="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="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>
</settings>

View file

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