Merge branch 'develop' into translations
This commit is contained in:
commit
64c6afad92
16 changed files with 115 additions and 84 deletions
|
@ -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)
|
||||
[![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)
|
||||
[![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.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)
|
||||
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
||||
|
|
15
addon.xml
15
addon.xml
|
@ -1,5 +1,5 @@
|
|||
<?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>
|
||||
<import addon="xbmc.python" version="2.1.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="zh_TW">使用風險由您自己承擔</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 PKC syncing progress to wrong account
|
||||
- Warn user if a xml cannot be parsed
|
||||
|
|
|
@ -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)
|
||||
- Fix Playback and watched status not syncing
|
||||
- Fix PKC syncing progress to wrong account
|
||||
|
|
|
@ -1705,6 +1705,21 @@ msgctxt "#39225"
|
|||
msgid "Missing only"
|
||||
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
|
||||
|
||||
msgctxt "#39250"
|
||||
|
|
|
@ -208,7 +208,7 @@ class PlexAPI():
|
|||
settings('plexHomeSize', homeSize)
|
||||
# Let Kodi log into plex.tv on startup from now on
|
||||
settings('myplexlogin', 'true')
|
||||
settings('plex_status', value='Logged in to plex.tv')
|
||||
settings('plex_status', value=lang(39227))
|
||||
return result
|
||||
|
||||
def CheckPlexTvSignin(self, identifier):
|
||||
|
|
|
@ -78,7 +78,7 @@ class PlexCompanion(Thread):
|
|||
data = task['data']
|
||||
|
||||
# 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':
|
||||
# e.g. Alexa
|
||||
xml = GetPlexMetadata(data['key'])
|
||||
|
@ -90,9 +90,11 @@ class PlexCompanion(Thread):
|
|||
api = API(xml[0])
|
||||
if api.getType() == v.PLEX_TYPE_ALBUM:
|
||||
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())
|
||||
queue.plex_transient_token = token
|
||||
else:
|
||||
state.PLEX_TRANSIENT_TOKEN = token
|
||||
params = {
|
||||
'mode': 'plex_node',
|
||||
'key': '{server}%s' % data.get('key'),
|
||||
|
@ -106,6 +108,7 @@ class PlexCompanion(Thread):
|
|||
elif (task['action'] == 'playlist' and
|
||||
data.get('address') == 'node.plexapp.com'):
|
||||
# E.g. watch later initiated by Companion
|
||||
state.PLEX_TRANSIENT_TOKEN = token
|
||||
params = {
|
||||
'mode': 'plex_node',
|
||||
'key': '{server}%s' % data.get('key'),
|
||||
|
@ -144,6 +147,7 @@ class PlexCompanion(Thread):
|
|||
ID,
|
||||
repeat=query.get('repeat'),
|
||||
offset=data.get('offset'))
|
||||
playqueue.plex_transient_token = token
|
||||
|
||||
def run(self):
|
||||
# Ensure that sockets will be closed no matter what
|
||||
|
|
|
@ -78,7 +78,7 @@ def togglePlexTV():
|
|||
settings('plexid', value="")
|
||||
settings('plexHomeSize', value="1")
|
||||
settings('plexAvatar', value="")
|
||||
settings('plex_status', value="Not logged in to plex.tv")
|
||||
settings('plex_status', value=lang(39226))
|
||||
|
||||
window('plex_token', clear=True)
|
||||
plex_command('PLEX_TOKEN', '')
|
||||
|
|
|
@ -82,7 +82,7 @@ class InitialSetup():
|
|||
answer = False
|
||||
else:
|
||||
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
|
||||
xml = self.doUtils('https://plex.tv/users/account',
|
||||
authenticate=False,
|
||||
|
@ -413,7 +413,7 @@ class InitialSetup():
|
|||
|
||||
# Optionally sign into plex.tv. Will not be called on very first run
|
||||
# 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:
|
||||
self.CheckPlexTVSignIn()
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@ def set_excludefromscan_music_folders():
|
|||
path = api.validatePlayurl(location.attrib['path'],
|
||||
typus=v.PLEX_TYPE_ARTIST,
|
||||
omitCheck=True)
|
||||
path = tryEncode(path)
|
||||
paths.append(__turn_to_regex(path))
|
||||
# Get existing advancedsettings
|
||||
root, tree = advancedsettings_xml(['audio', 'excludefromscan'],
|
||||
|
@ -98,7 +97,7 @@ def set_excludefromscan_music_folders():
|
|||
|
||||
if write_xml is True:
|
||||
indent(tree.getroot())
|
||||
tree.write('%sadvancedsettings.xml' % v.KODI_PROFILE)
|
||||
tree.write('%sadvancedsettings.xml' % v.KODI_PROFILE, encoding="UTF-8")
|
||||
return changed
|
||||
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ class Player(xbmc.Player):
|
|||
'plex_forcetranscode'):
|
||||
window(item, clear=True)
|
||||
# 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
|
||||
log.debug("Cleared playlist properties.")
|
||||
|
||||
|
@ -326,68 +326,45 @@ class Player(xbmc.Player):
|
|||
# Process each items
|
||||
for item in self.played_info:
|
||||
data = self.played_info.get(item)
|
||||
if data:
|
||||
log.debug("Item path: %s" % item)
|
||||
log.debug("Item data: %s" % data)
|
||||
if not data:
|
||||
continue
|
||||
log.debug("Item path: %s" % item)
|
||||
log.debug("Item data: %s" % data)
|
||||
|
||||
runtime = data['runtime']
|
||||
currentPosition = data['currentPosition']
|
||||
itemid = data['item_id']
|
||||
refresh_id = data['refresh_id']
|
||||
currentFile = data['currentfile']
|
||||
media_type = data['Type']
|
||||
playMethod = data['playmethod']
|
||||
runtime = data['runtime']
|
||||
currentPosition = data['currentPosition']
|
||||
itemid = data['item_id']
|
||||
refresh_id = data['refresh_id']
|
||||
currentFile = data['currentfile']
|
||||
media_type = data['Type']
|
||||
playMethod = data['playmethod']
|
||||
|
||||
# Prevent manually mark as watched in Kodi monitor
|
||||
window('plex_skipWatched%s' % itemid, value="true")
|
||||
# Prevent manually mark as watched in Kodi monitor
|
||||
window('plex_skipWatched%s' % itemid, value="true")
|
||||
|
||||
if currentPosition and runtime:
|
||||
try:
|
||||
percentComplete = float(currentPosition) / float(runtime)
|
||||
except ZeroDivisionError:
|
||||
# Runtime is 0.
|
||||
percentComplete = 0
|
||||
if not currentPosition or not runtime:
|
||||
continue
|
||||
try:
|
||||
percentComplete = float(currentPosition) / float(runtime)
|
||||
except ZeroDivisionError:
|
||||
# Runtime is 0.
|
||||
percentComplete = 0
|
||||
|
||||
markPlayed = 0.90
|
||||
log.info("Percent complete: %s Mark played at: %s"
|
||||
% (percentComplete, markPlayed))
|
||||
if percentComplete >= markPlayed:
|
||||
# Tell Kodi that we've finished watching (Plex knows)
|
||||
if (data['fileid'] is not None and
|
||||
data['itemType'] in (v.KODI_TYPE_MOVIE, v.KODI_TYPE_EPISODE)):
|
||||
with kodidb.GetKodiDB('video') as kodi_db:
|
||||
kodi_db.addPlaystate(
|
||||
data['fileid'],
|
||||
None,
|
||||
None,
|
||||
data['playcount'] + 1,
|
||||
DateToKodi(getUnixTimestamp()))
|
||||
# Send the delete action to the server.
|
||||
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")
|
||||
markPlayed = 0.90
|
||||
log.info("Percent complete: %s Mark played at: %s"
|
||||
% (percentComplete, markPlayed))
|
||||
if percentComplete >= markPlayed:
|
||||
# Tell Kodi that we've finished watching (Plex knows)
|
||||
if (data['fileid'] is not None and
|
||||
data['itemType'] in (v.KODI_TYPE_MOVIE,
|
||||
v.KODI_TYPE_EPISODE)):
|
||||
with kodidb.GetKodiDB('video') as kodi_db:
|
||||
kodi_db.addPlaystate(
|
||||
data['fileid'],
|
||||
None,
|
||||
None,
|
||||
data['playcount'] + 1,
|
||||
DateToKodi(getUnixTimestamp()))
|
||||
|
||||
# Clean the WINDOW properties
|
||||
for filename in self.played_info:
|
||||
|
|
|
@ -29,6 +29,8 @@ class Playlist_Object_Baseclase(object):
|
|||
selectedItemOffset = None
|
||||
shuffled = 0 # [int], 0: not shuffled, 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):
|
||||
answ = "<%s: " % (self.__class__.__name__)
|
||||
|
@ -58,6 +60,7 @@ class Playlist_Object_Baseclase(object):
|
|||
self.selectedItemOffset = None
|
||||
self.shuffled = 0
|
||||
self.repeat = 0
|
||||
self.plex_transient_token = None
|
||||
log.debug('Playlist cleared: %s' % self)
|
||||
|
||||
def log_Kodi_playlist(self):
|
||||
|
|
|
@ -78,6 +78,8 @@ class Playqueue(Thread):
|
|||
def init_playqueue_from_plex_children(self, plex_id):
|
||||
"""
|
||||
Init a new playqueue e.g. from an album. Alexa does this
|
||||
|
||||
Returns the Playlist_Object
|
||||
"""
|
||||
xml = GetAllPlexChildren(plex_id)
|
||||
try:
|
||||
|
@ -93,6 +95,7 @@ class Playqueue(Thread):
|
|||
PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey())
|
||||
log.debug('Firing up Kodi player')
|
||||
Player().play(playqueue.kodi_pl, None, False, 0)
|
||||
return playqueue
|
||||
|
||||
def update_playqueue_from_PMS(self,
|
||||
playqueue,
|
||||
|
|
|
@ -123,6 +123,8 @@ class SubscriptionManager:
|
|||
ret += ' itemType="%s"' % info['itemType']
|
||||
if 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
|
||||
if ptype == 'video':
|
||||
ret += ' subtitleStreamID="-1"'
|
||||
|
@ -157,7 +159,7 @@ class SubscriptionManager:
|
|||
def notifyServer(self, players):
|
||||
for typus, p in players.iteritems():
|
||||
info = self.playerprops[p.get('playerid')]
|
||||
self._sendNotification(info)
|
||||
self._sendNotification(info, int(p['playerid']))
|
||||
self.lastinfo[typus] = info
|
||||
# Cross the one of the list
|
||||
try:
|
||||
|
@ -167,9 +169,10 @@ class SubscriptionManager:
|
|||
# Process the players we have left (to signal a stop)
|
||||
for typus, p in self.lastplayers.iteritems():
|
||||
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()
|
||||
params = {
|
||||
'containerKey': self.containerKey or "/library/metadata/900000",
|
||||
|
@ -181,6 +184,8 @@ class SubscriptionManager:
|
|||
}
|
||||
if 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'):
|
||||
params['containerKey'] = '/playQueues/%s' % info['playQueueID']
|
||||
params['playQueueVersion'] = info['playQueueVersion']
|
||||
|
@ -272,6 +277,8 @@ class SubscriptionManager:
|
|||
info['volume'] = self.volume
|
||||
info['mute'] = self.mute
|
||||
|
||||
info['plex_transient_token'] = playqueue.plex_transient_token
|
||||
|
||||
return info
|
||||
|
||||
|
||||
|
|
|
@ -557,8 +557,8 @@ def __setXMLTag(element, tag, value, attrib=None):
|
|||
If "subelement" does not exist, create it using attrib and value.
|
||||
|
||||
element : etree element
|
||||
tag : string/unicode for subelement
|
||||
value : string/unicode
|
||||
tag : unicode for subelement
|
||||
value : unicode
|
||||
attrib : dict; will use etree attrib method
|
||||
|
||||
Returns the subelement
|
||||
|
@ -657,7 +657,7 @@ def advancedsettings_xml(node_list, new_value=None, attrib=None,
|
|||
# Indent and make readable
|
||||
indent(root)
|
||||
# Safe the changed xml
|
||||
tree.write(path)
|
||||
tree.write(path, encoding="UTF-8")
|
||||
return element, tree
|
||||
|
||||
|
||||
|
@ -705,7 +705,7 @@ def sourcesXML():
|
|||
try:
|
||||
indent(root)
|
||||
except: pass
|
||||
etree.ElementTree(root).write(xmlpath)
|
||||
etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
|
||||
|
||||
|
||||
def passwordsXML():
|
||||
|
@ -749,7 +749,8 @@ def passwordsXML():
|
|||
paths.remove(path)
|
||||
log.info("Successfully removed credentials for: %s"
|
||||
% credentials)
|
||||
etree.ElementTree(root).write(xmlpath)
|
||||
etree.ElementTree(root).write(xmlpath,
|
||||
encoding="UTF-8")
|
||||
break
|
||||
else:
|
||||
log.error("Failed to find saved server: %s in passwords.xml"
|
||||
|
@ -817,7 +818,7 @@ def passwordsXML():
|
|||
indent(root)
|
||||
except:
|
||||
pass
|
||||
etree.ElementTree(root).write(xmlpath)
|
||||
etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
|
||||
|
||||
# dialog.notification(
|
||||
# heading="PlexKodiConnect",
|
||||
|
|
|
@ -120,7 +120,7 @@ class VideoNodes(object):
|
|||
indent(root)
|
||||
except:
|
||||
pass
|
||||
etree.ElementTree(root).write(nodeXML)
|
||||
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
|
||||
|
||||
nodetypes = {
|
||||
'1': "all",
|
||||
|
@ -375,7 +375,7 @@ class VideoNodes(object):
|
|||
indent(root)
|
||||
except:
|
||||
pass
|
||||
etree.ElementTree(root).write(nodeXML)
|
||||
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
|
||||
|
||||
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
|
||||
tagname = tryEncode(tagname)
|
||||
|
@ -431,7 +431,7 @@ class VideoNodes(object):
|
|||
indent(root)
|
||||
except:
|
||||
pass
|
||||
etree.ElementTree(root).write(nodeXML)
|
||||
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
|
||||
|
||||
def clearProperties(self):
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<category label="Plex">
|
||||
<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="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 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" />
|
||||
|
|
Loading…
Reference in a new issue