Merge pull request #778 from croneter/beta-version

Bump master
This commit is contained in:
croneter 2019-03-17 18:01:07 +01:00 committed by GitHub
commit 34bc708d7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 100 additions and 60 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-2.7.6-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
[![beta version](https://img.shields.io/badge/beta_version-2.7.6-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
[![stable version](https://img.shields.io/badge/stable_version-2.7.7-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
[![beta version](https://img.shields.io/badge/beta_version-2.7.7-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.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)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.7.6" provider-name="croneter">
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.7.7" provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.9.1" />
@ -77,7 +77,15 @@
<summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary>
<description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description>
<disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer>
<news>version 2.7.6:
<news>version 2.7.7:
- Fix sync not working due to non-ASCII Plex library names
- Fix PKC synching playstate to wrong user on profile switch. Be aware that Kodi profile switches are error-prone
- Fix playback sometimes not being reported for direct paths
- Fix float() argument must be a string or a number
- Fix nodes for skin use
- Fix 'NoneType' object has no attribute 'kodi_path'
version 2.7.6:
- Make 2.7.5 available for everyone
version 2.7.5:

View file

@ -1,3 +1,11 @@
version 2.7.7:
- Fix sync not working due to non-ASCII Plex library names
- Fix PKC synching playstate to wrong user on profile switch. Be aware that Kodi profile switches are error-prone
- Fix playback sometimes not being reported for direct paths
- Fix float() argument must be a string or a number
- Fix nodes for skin use
- Fix 'NoneType' object has no attribute 'kodi_path'
version 2.7.6:
- Make 2.7.5 available for everyone

View file

@ -84,7 +84,7 @@ def show_main_menu(content_type=None):
# Get nodes from the window props
totalnodes = int(utils.window('Plex.nodes.total') or 0)
for i in range(totalnodes):
path = utils.window('Plex.nodes.%s.content' % i)
path = utils.window('Plex.nodes.%s.index' % i)
if not path:
continue
label = utils.window('Plex.nodes.%s.title' % i)
@ -114,9 +114,9 @@ def show_main_menu(content_type=None):
continue
# Add ANOTHER menu item that uses add-on paths instead of direct
# paths in order to let the user navigate into all submenus
addon_path = utils.window('Plex.nodes.%s.addon_path' % i)
addon_index = utils.window('Plex.nodes.%s.addon_index' % i)
# Append "(More...)" to the label
directory_item('%s (%s)' % (label, utils.lang(22082)), addon_path)
directory_item('%s (%s)' % (label, utils.lang(22082)), addon_index)
# Playlists
if content_type != 'image':
path = 'plugin://%s?mode=playlists' % v.ADDON_ID

View file

@ -22,12 +22,9 @@ def kodiid_from_filename(path, kodi_type=None, db_type=None):
"""
kodi_id = None
path = utils.try_decode(path)
try:
filename = path.rsplit('/', 1)[1]
path = path.rsplit('/', 1)[0] + '/'
except IndexError:
filename = path.rsplit('\\', 1)[1]
path = path.rsplit('\\', 1)[0] + '\\'
path, filename = path_ops.path.split(path)
# Make sure path ends in either '/' or '\'
path = path_ops.path.join(path, '')
if kodi_type == v.KODI_TYPE_SONG or db_type == 'music':
with KodiMusicDB(lock=False) as kodidb:
try:

View file

@ -258,6 +258,7 @@ class FullSync(common.fullsync_mixin):
for section in (x for x in sections.SECTIONS
if x.section_type == kind[1]):
if self.isCanceled():
LOG.debug('Need to exit now')
return
if not section.sync_to_kodi:
LOG.info('User chose to not sync section %s', section)
@ -283,6 +284,8 @@ class FullSync(common.fullsync_mixin):
self.section_success = False
else:
queue.put(element)
except Exception:
utils.ERROR(notify=True)
finally:
queue.put(None)
@ -410,7 +413,7 @@ class FullSync(common.fullsync_mixin):
def _run(self):
self.current_sync = timing.plex_now()
# Get latest Plex libraries and build playlist and video node files
if not sections.sync_from_pms(self):
if self.isCanceled() or not sections.sync_from_pms(self):
return
self.successful = True
try:
@ -422,10 +425,7 @@ class FullSync(common.fullsync_mixin):
# Actual syncing - do only new items first
LOG.info('Running full_library_sync with repair=%s',
self.repair)
if not self.full_library_sync():
self.successful = False
return
if self.isCanceled():
if self.isCanceled() or not self.full_library_sync():
self.successful = False
return
if common.PLAYLIST_SYNC_ENABLED and not playlists.full_sync():
@ -437,6 +437,7 @@ class FullSync(common.fullsync_mixin):
self.dialog.close()
if self.threader:
self.threader.shutdown()
self.threader = None
if not self.successful and not self.isCanceled():
# "ERROR in library sync"
utils.dialog('notification',

View file

@ -99,7 +99,7 @@ class Section(object):
"'section_type': '{self.section_type}', "
"'sync_to_kodi': {self.sync_to_kodi}, "
"'last_sync': {self.last_sync}"
"}}").format(self=self)
"}}").format(self=self).encode('utf-8')
__str__ = __repr__
def __nonzero__(self):
@ -246,20 +246,27 @@ class Section(object):
}
if not self.sync_to_kodi:
args['synched'] = 'false'
addon_path = self.addon_path(args)
addon_index = self.addon_path(args)
if self.sync_to_kodi and self.section_type in v.PLEX_VIDEOTYPES:
path = 'library://video/Plex-%s' % self.section_id
path = 'library://video/Plex-{0}/{0}_all.xml'
path = path.format(self.section_id)
index = 'library://video/Plex-%s' % self.section_id
else:
# No xmls to link to - let's show the listings on the fly
path = addon_path
index = addon_index
args['key'] = '/library/sections/%s/all' % self.section_id
path = self.addon_path(args)
# .index will list all possible nodes for this library
utils.window('%s.index' % self.node, value=index)
utils.window('%s.title' % self.node, value=self.name)
utils.window('%s.type' % self.node, value=self.content)
utils.window('%s.content' % self.node, value=path)
# .path leads to all elements of this library
utils.window('%s.path' % self.node,
value='ActivateWindow(Videos,%s,return)' % path)
utils.window('%s.id' % self.node, value=str(self.section_id))
# To let the user navigate into this node when selecting widgets
utils.window('%s.addon_path' % self.node, value=addon_path)
utils.window('%s.addon_index' % self.node, value=addon_index)
if not self.sync_to_kodi:
self.remove_files_from_kodi()
return
@ -307,7 +314,7 @@ class Section(object):
xml = getattr(nodes, 'node_%s' % node_type)(self, node_name)
self._write_xml(xml, xml_name)
self.order += 1
self._window_node(path, node_name, node_type)
self._window_node(path, node_name, node_type, pms_node)
def _write_xml(self, xml, xml_name):
LOG.debug('Creating xml for section %s: %s', self.name, xml_name)
@ -329,17 +336,17 @@ class Section(object):
utils.indent(xml)
etree.ElementTree(xml).write(self.playlist_path, encoding='utf-8')
def _window_node(self, path, node_name, node_type):
def _window_node(self, path, node_name, node_type, pms_node):
"""
Will save this section's node to the Kodi window variables
Uses the same conventions/logic as Emby for Kodi does
"""
if self.section_type == v.PLEX_TYPE_ARTIST:
window_path = 'ActivateWindow(Music,%s,return)' % path
elif self.section_type == v.PLEX_TYPE_PHOTO:
if pms_node or not self.sync_to_kodi:
# Check: elif node_type in ('browse', 'homevideos', 'photos'):
window_path = path
elif self.section_type == v.PLEX_TYPE_ARTIST:
window_path = 'ActivateWindow(Music,%s,return)' % path
else:
window_path = 'ActivateWindow(Videos,%s,return)' % path
# if node_type == 'all':

View file

@ -90,9 +90,9 @@ def sync_pms_time():
# Toggle watched state back
PF.scrobble(plex_id, 'unwatched')
try:
plextime = xml[0].get('lastViewedAt')
except (IndexError, TypeError, AttributeError):
LOG.error('Could not get lastViewedAt - aborting')
plextime = xml[0].attrib['lastViewedAt']
except (IndexError, TypeError, AttributeError, KeyError):
LOG.warn('Could not get lastViewedAt - aborting')
return False
# Calculate time offset Kodi-PMS

View file

@ -372,19 +372,15 @@ def verify_kodi_item(plex_id, kodi_item):
raise PlaylistError
LOG.debug('Starting research for Kodi id since we didnt get one: %s',
kodi_item)
kodi_id, _ = kodiid_from_filename(kodi_item['file'],
v.KODI_TYPE_MOVIE)
kodi_item['type'] = v.KODI_TYPE_MOVIE
if kodi_id is None:
kodi_id, _ = kodiid_from_filename(kodi_item['file'],
v.KODI_TYPE_EPISODE)
kodi_item['type'] = v.KODI_TYPE_EPISODE
if kodi_id is None:
kodi_id, _ = kodiid_from_filename(kodi_item['file'],
v.KODI_TYPE_SONG)
kodi_item['type'] = v.KODI_TYPE_SONG
# Try the VIDEO DB first - will find both movies and episodes
kodi_id, kodi_type = kodiid_from_filename(kodi_item['file'],
db_type='video')
if not kodi_id:
# No movie or episode found - try MUSIC DB now for songs
kodi_id, kodi_type = kodiid_from_filename(kodi_item['file'],
db_type='music')
kodi_item['id'] = kodi_id
kodi_item['type'] = None if kodi_id is None else kodi_item['type']
kodi_item['type'] = None if kodi_id is None else kodi_type
LOG.debug('Research results for kodi_item: %s', kodi_item)
return kodi_item

View file

@ -202,6 +202,8 @@ def _full_sync():
return False
playlist = db.get_playlist(plex_id=plex_id)
LOG.debug('Removing outdated Plex playlist from Kodi: %s', playlist)
if playlist is None:
continue
try:
kodi_pl.delete(playlist)
except PlaylistError:

View file

@ -289,7 +289,9 @@ class SubscriptionMgr(object):
# To avoid RuntimeError, don't use self.lastplayers
for playerid in (0, 1, 2):
self.last_params['state'] = 'stopped'
self._send_pms_notification(playerid, self.last_params)
self._send_pms_notification(playerid,
self.last_params,
timeout=0.0001)
def _plex_stream_index(self, playerid, stream_type):
"""
@ -399,7 +401,11 @@ class SubscriptionMgr(object):
self.last_params = params
return params
def _send_pms_notification(self, playerid, params):
def _send_pms_notification(self, playerid, params, timeout=None):
"""
Pass a really low timeout in seconds if shutting down Kodi and we don't
need the PMS' response
"""
serv = self._server_by_host(self.server)
playqueue = PQ.PLAYQUEUES[playerid]
xargs = params_pms()
@ -416,7 +422,8 @@ class SubscriptionMgr(object):
DU().downloadUrl(url,
authenticate=False,
parameters=xargs,
headerOverride=HEADERS_PMS)
headerOverride=HEADERS_PMS,
timeout=timeout)
LOG.debug("Sent server notification with parameters: %s to %s",
xargs, url)

View file

@ -451,12 +451,15 @@ class Service(object):
self.choose_plex_libraries()
elif plex_command == 'RESET-PKC':
utils.reset()
elif plex_command == 'EXIT-PKC':
LOG.info('Received command from another instance to quit')
app.APP.stop_pkc = True
if task:
backgroundthread.BGThreader.addTasksToFront([task])
continue
if app.APP.suspend:
app.APP.monitor.waitForAbort(0.1)
xbmc.sleep(100)
continue
# Before proceeding, need to make sure:
@ -495,7 +498,7 @@ class Service(object):
if utils.settings('enable_alexa') == 'true':
self.alexa.start()
app.APP.monitor.waitForAbort(0.1)
xbmc.sleep(100)
# EXITING PKC
# Tell all threads to terminate (e.g. several lib sync threads)
@ -506,26 +509,37 @@ class Service(object):
library_sync.clear_window_vars()
# Will block until threads have quit
app.APP.stop_threads()
utils.window('plex_service_started', clear=True)
LOG.info("======== STOP %s ========", v.ADDON_NAME)
def start():
# Safety net - Kody starts PKC twice upon first installation!
if utils.window('plex_service_started') == 'true':
EXIT = True
else:
LOG.info('Another service.py instance is already running - shutting '
'it down now')
# Telling the other Python instance of PKC to shut down now
i = 0
while utils.window('plexkodiconnect.command'):
xbmc.sleep(20)
i += 1
if i > 300:
LOG.error('Could not tell other PKC instance to shut down')
return
utils.window('plexkodiconnect.command', value='EXIT-PKC')
# Telling successful - now wait for actual shut-down
i = 0
while utils.window('plex_service_started'):
xbmc.sleep(20)
i += 1
if i > 300:
LOG.error('Could not shut down other PKC instance')
return
utils.window('plex_service_started', value='true')
EXIT = False
# Delay option
DELAY = int(utils.settings('startupDelay'))
LOG.info("Delaying Plex startup by: %s sec...", DELAY)
if EXIT:
LOG.error('PKC service.py already started - exiting this instance')
elif DELAY and xbmc.Monitor().waitForAbort(DELAY):
if DELAY and xbmc.Monitor().waitForAbort(DELAY):
# Start the service
LOG.info("Abort requested while waiting. PKC not started.")
else:
Service().ServiceEntryPoint()
utils.window('plex_service_started', clear=True)
LOG.info("======== STOP PlexKodiConnect service ========")