Merge branch 'develop' into translations

This commit is contained in:
tomkat83 2017-05-29 15:43:10 +02:00
commit 64c6afad92
16 changed files with 115 additions and 84 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-1.7.7-blue.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip) [![stable version](https://img.shields.io/badge/stable_version-1.8.0-blue.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip)
[![beta version](https://img.shields.io/badge/beta_version-1.7.21-red.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect_BETA/bin-BETA/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.0.zip) [![beta version](https://img.shields.io/badge/beta_version-1.8.0-red.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect_BETA/bin-BETA/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.0.zip)
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.21" provider-name="croneter"> <addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.8.0" provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.3.0" /> <import addon="script.module.requests" version="2.3.0" />
@ -44,7 +44,18 @@
<disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer> <disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer>
<disclaimer lang="zh_TW">使用風險由您自己承擔</disclaimer> <disclaimer lang="zh_TW">使用風險由您自己承擔</disclaimer>
<disclaimer lang="es_ES">Usar a su propio riesgo</disclaimer> <disclaimer lang="es_ES">Usar a su propio riesgo</disclaimer>
<news>version 1.7.21 (beta only) <news>version 1.8.0
Featuring:
- Major music overhaul: Direct Paths should now work! Many thanks @Memesa for the pointers! Don't forget to reset your database
- Big transcoding overhaul
- Many Plex Companion fixes
- Add support to Kodi 18.0-alpha1 (thanks @CotzaDev)
version 1.7.22 (beta only)
- Fix playback stop not being recognized by the PMS
- Better way to sync progress to another account
version 1.7.21 (beta only)
- Fix Playback and watched status not syncing - Fix Playback and watched status not syncing
- Fix PKC syncing progress to wrong account - Fix PKC syncing progress to wrong account
- Warn user if a xml cannot be parsed - Warn user if a xml cannot be parsed

View file

@ -1,3 +1,14 @@
version 1.8.0
Featuring:
- Major music overhaul: Direct Paths should now work! Many thanks @Memesa for the pointers! Don't forget to reset your database
- Big transcoding overhaul
- Many Plex Companion fixes
- Add support to Kodi 18.0-alpha1 (thanks @CotzaDev)
version 1.7.22 (beta only)
- Fix playback stop not being recognized by the PMS
- Better way to sync progress to another account
version 1.7.21 (beta only) version 1.7.21 (beta only)
- Fix Playback and watched status not syncing - Fix Playback and watched status not syncing
- Fix PKC syncing progress to wrong account - Fix PKC syncing progress to wrong account

View file

@ -1705,6 +1705,21 @@ msgctxt "#39225"
msgid "Missing only" msgid "Missing only"
msgstr "" msgstr ""
# Message in the PKC settings if user has not logged in to plex.tv
msgctxt "#39226"
msgid "Not logged in to plex.tv"
msgstr ""
# Message in the PKC settings if user is logged in to plex.tv
msgctxt "#39227"
msgid "Logged in to plex.tv"
msgstr ""
# Message in the PKC settings to display the plex.tv username. Leave the colon :
msgctxt "#39228"
msgid "Plex user:"
msgstr ""
# Plex Artwork.py # Plex Artwork.py
msgctxt "#39250" msgctxt "#39250"

View file

@ -208,7 +208,7 @@ class PlexAPI():
settings('plexHomeSize', homeSize) settings('plexHomeSize', homeSize)
# Let Kodi log into plex.tv on startup from now on # Let Kodi log into plex.tv on startup from now on
settings('myplexlogin', 'true') settings('myplexlogin', 'true')
settings('plex_status', value='Logged in to plex.tv') settings('plex_status', value=lang(39227))
return result return result
def CheckPlexTvSignin(self, identifier): def CheckPlexTvSignin(self, identifier):

View file

@ -78,7 +78,7 @@ class PlexCompanion(Thread):
data = task['data'] data = task['data']
# Get the token of the user flinging media (might be different one) # Get the token of the user flinging media (might be different one)
state.PLEX_TRANSIENT_TOKEN = data.get('token') token = data.get('token')
if task['action'] == 'alexa': if task['action'] == 'alexa':
# e.g. Alexa # e.g. Alexa
xml = GetPlexMetadata(data['key']) xml = GetPlexMetadata(data['key'])
@ -90,9 +90,11 @@ class PlexCompanion(Thread):
api = API(xml[0]) api = API(xml[0])
if api.getType() == v.PLEX_TYPE_ALBUM: if api.getType() == v.PLEX_TYPE_ALBUM:
log.debug('Plex music album detected') log.debug('Plex music album detected')
self.mgr.playqueue.init_playqueue_from_plex_children( queue = self.mgr.playqueue.init_playqueue_from_plex_children(
api.getRatingKey()) api.getRatingKey())
queue.plex_transient_token = token
else: else:
state.PLEX_TRANSIENT_TOKEN = token
params = { params = {
'mode': 'plex_node', 'mode': 'plex_node',
'key': '{server}%s' % data.get('key'), 'key': '{server}%s' % data.get('key'),
@ -106,6 +108,7 @@ class PlexCompanion(Thread):
elif (task['action'] == 'playlist' and elif (task['action'] == 'playlist' and
data.get('address') == 'node.plexapp.com'): data.get('address') == 'node.plexapp.com'):
# E.g. watch later initiated by Companion # E.g. watch later initiated by Companion
state.PLEX_TRANSIENT_TOKEN = token
params = { params = {
'mode': 'plex_node', 'mode': 'plex_node',
'key': '{server}%s' % data.get('key'), 'key': '{server}%s' % data.get('key'),
@ -144,6 +147,7 @@ class PlexCompanion(Thread):
ID, ID,
repeat=query.get('repeat'), repeat=query.get('repeat'),
offset=data.get('offset')) offset=data.get('offset'))
playqueue.plex_transient_token = token
def run(self): def run(self):
# Ensure that sockets will be closed no matter what # Ensure that sockets will be closed no matter what

View file

@ -78,7 +78,7 @@ def togglePlexTV():
settings('plexid', value="") settings('plexid', value="")
settings('plexHomeSize', value="1") settings('plexHomeSize', value="1")
settings('plexAvatar', value="") settings('plexAvatar', value="")
settings('plex_status', value="Not logged in to plex.tv") settings('plex_status', value=lang(39226))
window('plex_token', clear=True) window('plex_token', clear=True)
plex_command('PLEX_TOKEN', '') plex_command('PLEX_TOKEN', '')

View file

@ -82,7 +82,7 @@ class InitialSetup():
answer = False answer = False
else: else:
log.info('plex.tv connection with token successful') log.info('plex.tv connection with token successful')
settings('plex_status', value='Logged in to plex.tv') settings('plex_status', value=lang(39227))
# Refresh the info from Plex.tv # Refresh the info from Plex.tv
xml = self.doUtils('https://plex.tv/users/account', xml = self.doUtils('https://plex.tv/users/account',
authenticate=False, authenticate=False,
@ -413,7 +413,7 @@ class InitialSetup():
# Optionally sign into plex.tv. Will not be called on very first run # Optionally sign into plex.tv. Will not be called on very first run
# as plexToken will be '' # as plexToken will be ''
settings('plex_status', value='Not logged in to plex.tv') settings('plex_status', value=lang(39226))
if self.plexToken and self.myplexlogin: if self.plexToken and self.myplexlogin:
self.CheckPlexTVSignIn() self.CheckPlexTVSignIn()

View file

@ -65,7 +65,6 @@ def set_excludefromscan_music_folders():
path = api.validatePlayurl(location.attrib['path'], path = api.validatePlayurl(location.attrib['path'],
typus=v.PLEX_TYPE_ARTIST, typus=v.PLEX_TYPE_ARTIST,
omitCheck=True) omitCheck=True)
path = tryEncode(path)
paths.append(__turn_to_regex(path)) paths.append(__turn_to_regex(path))
# Get existing advancedsettings # Get existing advancedsettings
root, tree = advancedsettings_xml(['audio', 'excludefromscan'], root, tree = advancedsettings_xml(['audio', 'excludefromscan'],
@ -98,7 +97,7 @@ def set_excludefromscan_music_folders():
if write_xml is True: if write_xml is True:
indent(tree.getroot()) indent(tree.getroot())
tree.write('%sadvancedsettings.xml' % v.KODI_PROFILE) tree.write('%sadvancedsettings.xml' % v.KODI_PROFILE, encoding="UTF-8")
return changed return changed

View file

@ -310,7 +310,7 @@ class Player(xbmc.Player):
'plex_forcetranscode'): 'plex_forcetranscode'):
window(item, clear=True) window(item, clear=True)
# We might have saved a transient token from a user flinging media via # We might have saved a transient token from a user flinging media via
# Companion # Companion (if we could not use the playqueue to store the token)
state.PLEX_TRANSIENT_TOKEN = None state.PLEX_TRANSIENT_TOKEN = None
log.debug("Cleared playlist properties.") log.debug("Cleared playlist properties.")
@ -326,68 +326,45 @@ class Player(xbmc.Player):
# Process each items # Process each items
for item in self.played_info: for item in self.played_info:
data = self.played_info.get(item) data = self.played_info.get(item)
if data: if not data:
log.debug("Item path: %s" % item) continue
log.debug("Item data: %s" % data) log.debug("Item path: %s" % item)
log.debug("Item data: %s" % data)
runtime = data['runtime'] runtime = data['runtime']
currentPosition = data['currentPosition'] currentPosition = data['currentPosition']
itemid = data['item_id'] itemid = data['item_id']
refresh_id = data['refresh_id'] refresh_id = data['refresh_id']
currentFile = data['currentfile'] currentFile = data['currentfile']
media_type = data['Type'] media_type = data['Type']
playMethod = data['playmethod'] playMethod = data['playmethod']
# Prevent manually mark as watched in Kodi monitor # Prevent manually mark as watched in Kodi monitor
window('plex_skipWatched%s' % itemid, value="true") window('plex_skipWatched%s' % itemid, value="true")
if currentPosition and runtime: if not currentPosition or not runtime:
try: continue
percentComplete = float(currentPosition) / float(runtime) try:
except ZeroDivisionError: percentComplete = float(currentPosition) / float(runtime)
# Runtime is 0. except ZeroDivisionError:
percentComplete = 0 # Runtime is 0.
percentComplete = 0
markPlayed = 0.90 markPlayed = 0.90
log.info("Percent complete: %s Mark played at: %s" log.info("Percent complete: %s Mark played at: %s"
% (percentComplete, markPlayed)) % (percentComplete, markPlayed))
if percentComplete >= markPlayed: if percentComplete >= markPlayed:
# Tell Kodi that we've finished watching (Plex knows) # Tell Kodi that we've finished watching (Plex knows)
if (data['fileid'] is not None and if (data['fileid'] is not None and
data['itemType'] in (v.KODI_TYPE_MOVIE, v.KODI_TYPE_EPISODE)): data['itemType'] in (v.KODI_TYPE_MOVIE,
with kodidb.GetKodiDB('video') as kodi_db: v.KODI_TYPE_EPISODE)):
kodi_db.addPlaystate( with kodidb.GetKodiDB('video') as kodi_db:
data['fileid'], kodi_db.addPlaystate(
None, data['fileid'],
None, None,
data['playcount'] + 1, None,
DateToKodi(getUnixTimestamp())) data['playcount'] + 1,
# Send the delete action to the server. DateToKodi(getUnixTimestamp()))
offerDelete = False
if media_type == "Episode" and settings('deleteTV') == "true":
offerDelete = True
elif media_type == "Movie" and settings('deleteMovies') == "true":
offerDelete = True
if settings('offerDelete') != "true":
# Delete could be disabled, even if the subsetting is enabled.
offerDelete = False
# Plex: never delete
offerDelete = False
if percentComplete >= markPlayed and offerDelete:
resp = xbmcgui.Dialog().yesno(
lang(30091),
lang(33015),
autoclose=120000)
if not resp:
log.info("User skipped deletion.")
continue
url = "{server}/emby/Items/%s?format=json" % itemid
log.info("Deleting request: %s" % itemid)
self.doUtils(url, action_type="DELETE")
# Clean the WINDOW properties # Clean the WINDOW properties
for filename in self.played_info: for filename in self.played_info:

View file

@ -29,6 +29,8 @@ class Playlist_Object_Baseclase(object):
selectedItemOffset = None selectedItemOffset = None
shuffled = 0 # [int], 0: not shuffled, 1: ??? 2: ??? shuffled = 0 # [int], 0: not shuffled, 1: ??? 2: ???
repeat = 0 # [int], 0: not repeated, 1: ??? 2: ??? repeat = 0 # [int], 0: not repeated, 1: ??? 2: ???
# If Companion playback is initiated by another user
plex_transient_token = None
def __repr__(self): def __repr__(self):
answ = "<%s: " % (self.__class__.__name__) answ = "<%s: " % (self.__class__.__name__)
@ -58,6 +60,7 @@ class Playlist_Object_Baseclase(object):
self.selectedItemOffset = None self.selectedItemOffset = None
self.shuffled = 0 self.shuffled = 0
self.repeat = 0 self.repeat = 0
self.plex_transient_token = None
log.debug('Playlist cleared: %s' % self) log.debug('Playlist cleared: %s' % self)
def log_Kodi_playlist(self): def log_Kodi_playlist(self):

View file

@ -78,6 +78,8 @@ class Playqueue(Thread):
def init_playqueue_from_plex_children(self, plex_id): def init_playqueue_from_plex_children(self, plex_id):
""" """
Init a new playqueue e.g. from an album. Alexa does this Init a new playqueue e.g. from an album. Alexa does this
Returns the Playlist_Object
""" """
xml = GetAllPlexChildren(plex_id) xml = GetAllPlexChildren(plex_id)
try: try:
@ -93,6 +95,7 @@ class Playqueue(Thread):
PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey()) PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey())
log.debug('Firing up Kodi player') log.debug('Firing up Kodi player')
Player().play(playqueue.kodi_pl, None, False, 0) Player().play(playqueue.kodi_pl, None, False, 0)
return playqueue
def update_playqueue_from_PMS(self, def update_playqueue_from_PMS(self,
playqueue, playqueue,

View file

@ -123,6 +123,8 @@ class SubscriptionManager:
ret += ' itemType="%s"' % info['itemType'] ret += ' itemType="%s"' % info['itemType']
if state.PLEX_TRANSIENT_TOKEN: if state.PLEX_TRANSIENT_TOKEN:
ret += ' token="%s"' % state.PLEX_TRANSIENT_TOKEN ret += ' token="%s"' % state.PLEX_TRANSIENT_TOKEN
elif info['plex_transient_token']:
ret += ' token="%s"' % info['plex_transient_token']
# Might need an update in the future # Might need an update in the future
if ptype == 'video': if ptype == 'video':
ret += ' subtitleStreamID="-1"' ret += ' subtitleStreamID="-1"'
@ -157,7 +159,7 @@ class SubscriptionManager:
def notifyServer(self, players): def notifyServer(self, players):
for typus, p in players.iteritems(): for typus, p in players.iteritems():
info = self.playerprops[p.get('playerid')] info = self.playerprops[p.get('playerid')]
self._sendNotification(info) self._sendNotification(info, int(p['playerid']))
self.lastinfo[typus] = info self.lastinfo[typus] = info
# Cross the one of the list # Cross the one of the list
try: try:
@ -167,9 +169,10 @@ class SubscriptionManager:
# Process the players we have left (to signal a stop) # Process the players we have left (to signal a stop)
for typus, p in self.lastplayers.iteritems(): for typus, p in self.lastplayers.iteritems():
self.lastinfo[typus]['state'] = 'stopped' self.lastinfo[typus]['state'] = 'stopped'
# self._sendNotification(self.lastinfo[typus]) self._sendNotification(self.lastinfo[typus], int(p['playerid']))
def _sendNotification(self, info): def _sendNotification(self, info, playerid):
playqueue = self.playqueue.playqueues[playerid]
xargs = getXArgsDeviceInfo() xargs = getXArgsDeviceInfo()
params = { params = {
'containerKey': self.containerKey or "/library/metadata/900000", 'containerKey': self.containerKey or "/library/metadata/900000",
@ -181,6 +184,8 @@ class SubscriptionManager:
} }
if state.PLEX_TRANSIENT_TOKEN: if state.PLEX_TRANSIENT_TOKEN:
xargs['X-Plex-Token'] = state.PLEX_TRANSIENT_TOKEN xargs['X-Plex-Token'] = state.PLEX_TRANSIENT_TOKEN
elif playqueue.plex_transient_token:
xargs['X-Plex-Token'] = playqueue.plex_transient_token
if info.get('playQueueID'): if info.get('playQueueID'):
params['containerKey'] = '/playQueues/%s' % info['playQueueID'] params['containerKey'] = '/playQueues/%s' % info['playQueueID']
params['playQueueVersion'] = info['playQueueVersion'] params['playQueueVersion'] = info['playQueueVersion']
@ -272,6 +277,8 @@ class SubscriptionManager:
info['volume'] = self.volume info['volume'] = self.volume
info['mute'] = self.mute info['mute'] = self.mute
info['plex_transient_token'] = playqueue.plex_transient_token
return info return info

View file

@ -557,8 +557,8 @@ def __setXMLTag(element, tag, value, attrib=None):
If "subelement" does not exist, create it using attrib and value. If "subelement" does not exist, create it using attrib and value.
element : etree element element : etree element
tag : string/unicode for subelement tag : unicode for subelement
value : string/unicode value : unicode
attrib : dict; will use etree attrib method attrib : dict; will use etree attrib method
Returns the subelement Returns the subelement
@ -657,7 +657,7 @@ def advancedsettings_xml(node_list, new_value=None, attrib=None,
# Indent and make readable # Indent and make readable
indent(root) indent(root)
# Safe the changed xml # Safe the changed xml
tree.write(path) tree.write(path, encoding="UTF-8")
return element, tree return element, tree
@ -705,7 +705,7 @@ def sourcesXML():
try: try:
indent(root) indent(root)
except: pass except: pass
etree.ElementTree(root).write(xmlpath) etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
def passwordsXML(): def passwordsXML():
@ -749,7 +749,8 @@ def passwordsXML():
paths.remove(path) paths.remove(path)
log.info("Successfully removed credentials for: %s" log.info("Successfully removed credentials for: %s"
% credentials) % credentials)
etree.ElementTree(root).write(xmlpath) etree.ElementTree(root).write(xmlpath,
encoding="UTF-8")
break break
else: else:
log.error("Failed to find saved server: %s in passwords.xml" log.error("Failed to find saved server: %s in passwords.xml"
@ -817,7 +818,7 @@ def passwordsXML():
indent(root) indent(root)
except: except:
pass pass
etree.ElementTree(root).write(xmlpath) etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
# dialog.notification( # dialog.notification(
# heading="PlexKodiConnect", # heading="PlexKodiConnect",

View file

@ -120,7 +120,7 @@ class VideoNodes(object):
indent(root) indent(root)
except: except:
pass pass
etree.ElementTree(root).write(nodeXML) etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
nodetypes = { nodetypes = {
'1': "all", '1': "all",
@ -375,7 +375,7 @@ class VideoNodes(object):
indent(root) indent(root)
except: except:
pass pass
etree.ElementTree(root).write(nodeXML) etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
def singleNode(self, indexnumber, tagname, mediatype, itemtype): def singleNode(self, indexnumber, tagname, mediatype, itemtype):
tagname = tryEncode(tagname) tagname = tryEncode(tagname)
@ -431,7 +431,7 @@ class VideoNodes(object):
indent(root) indent(root)
except: except:
pass pass
etree.ElementTree(root).write(nodeXML) etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
def clearProperties(self): def clearProperties(self):

View file

@ -23,7 +23,7 @@
<category label="Plex"> <category label="Plex">
<setting type="lsep" label="plex.tv"/> <setting type="lsep" label="plex.tv"/>
<setting id="plex_status" label="39071" type="text" default="Not logged in to plex.tv" enable="false" /><!-- Current plex.tv status: --> <setting id="plex_status" label="39071" type="text" default="Not logged in to plex.tv" enable="false" /><!-- Current plex.tv status: -->
<setting id="plexLogin" label="Plex user:" type="text" default="" enable="false" /> <setting id="plexLogin" label="39228" type="text" default="" enable="false" />
<setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup --> <setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup -->
<setting label="39209" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=togglePlexTV)" option="close" /> <setting label="39209" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=togglePlexTV)" option="close" />
<setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" /> <setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" />