Merge branch 'beta-version'

This commit is contained in:
croneter 2018-08-05 14:45:51 +02:00
commit d8386c4cc0
6 changed files with 83 additions and 71 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-2.3.3-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) [![stable version](https://img.shields.io/badge/stable_version-2.3.3-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.3.3-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![beta version](https://img.shields.io/badge/beta_version-2.3.4-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) [![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="2.3.3" provider-name="croneter"> <addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.3.4" 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.9.1" /> <import addon="script.module.requests" version="2.9.1" />
@ -73,7 +73,10 @@
<summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary> <summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary>
<description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description> <description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description>
<disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer> <disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer>
<news>version 2.3.3: <news>version 2.3.4 (beta only):
- Fix playback sometimes not starting and UnicodeEncodeError for logging
version 2.3.3:
- Choose trailer if several are present (DB reset required) - Choose trailer if several are present (DB reset required)
version 2.3.2: version 2.3.2:

View file

@ -1,3 +1,6 @@
version 2.3.4 (beta only):
- Fix playback sometimes not starting and UnicodeEncodeError for logging
version 2.3.3: version 2.3.3:
- Choose trailer if several are present (DB reset required) - Choose trailer if several are present (DB reset required)

View file

@ -38,9 +38,11 @@ def config():
class LogHandler(logging.StreamHandler): class LogHandler(logging.StreamHandler):
def __init__(self): def __init__(self):
logging.StreamHandler.__init__(self) logging.StreamHandler.__init__(self)
self.setFormatter(logging.Formatter(fmt="%(name)s: %(message)s")) self.setFormatter(logging.Formatter(fmt=b"%(name)s: %(message)s"))
def emit(self, record): def emit(self, record):
if isinstance(record.msg, unicode):
record.msg = record.msg.encode('utf-8')
try: try:
xbmc.log(self.format(record), level=LEVELS[record.levelno]) xbmc.log(self.format(record), level=LEVELS[record.levelno])
except UnicodeEncodeError: except UnicodeEncodeError:

View file

@ -33,36 +33,7 @@ class PlaylistError(Exception):
pass pass
class PlaylistObjectBaseclase(object): class Playqueue_Object(object):
"""
Base class
"""
def __init__(self):
self.id = None
self.type = None
def __repr__(self):
"""
Print the playlist, e.g. to log. Returns unicode
"""
answ = '{\'%s\': {\'id\': %s, ' % (self.__class__.__name__, self.id)
# For some reason, can't use dir directly
for key in self.__dict__:
if key in ('id', 'kodi_pl'):
continue
if isinstance(getattr(self, key), str):
answ += '\'%s\': \'%s\', ' % (key,
utils.try_decode(getattr(self,
key)))
elif isinstance(getattr(self, key), unicode):
answ += '\'%s\': \'%s\', ' % (key, getattr(self, key))
else:
# e.g. int
answ += '\'%s\': %s, ' % (key, unicode(getattr(self, key)))
return answ + '}}'
class Playqueue_Object(PlaylistObjectBaseclase):
""" """
PKC object to represent PMS playQueues and Kodi playlist for queueing PKC object to represent PMS playQueues and Kodi playlist for queueing
@ -85,6 +56,8 @@ class Playqueue_Object(PlaylistObjectBaseclase):
kind = 'playQueue' kind = 'playQueue'
def __init__(self): def __init__(self):
self.id = None
self.type = None
self.playlistid = None self.playlistid = None
self.kodi_pl = None self.kodi_pl = None
self.items = [] self.items = []
@ -104,7 +77,25 @@ class Playqueue_Object(PlaylistObjectBaseclase):
# To keep track if Kodi playback was initiated from a Kodi playlist # To keep track if Kodi playback was initiated from a Kodi playlist
# There are a couple of pitfalls, unfortunately... # There are a couple of pitfalls, unfortunately...
self.kodi_playlist_playback = False self.kodi_playlist_playback = False
PlaylistObjectBaseclase.__init__(self)
def __repr__(self):
answ = ("{{"
"'playlistid': {self.playlistid}, "
"'id': {self.id}, "
"'version': {self.version}, "
"'type': '{self.type}', "
"'selectedItemID': {self.selectedItemID}, "
"'selectedItemOffset': {self.selectedItemOffset}, "
"'shuffled': {self.shuffled}, "
"'repeat': {self.repeat}, "
"'kodi_playlist_playback': {self.kodi_playlist_playback}, "
"'pkc_edit': {self.pkc_edit}, ".format(self=self))
answ = answ.encode('utf-8')
# Since list.__repr__ will return string, not unicode
return answ + b"'items': {self.items}}}".format(self=self)
def __str__(self):
return self.__repr__()
def is_pkc_clear(self): def is_pkc_clear(self):
""" """
@ -181,28 +172,27 @@ class Playlist_Item(object):
self.force_transcode = False self.force_transcode = False
def __repr__(self): def __repr__(self):
""" answ = ("{{"
Print the playlist item, e.g. to log. Returns unicode "'id': {self.id}, "
""" "'plex_id': {self.plex_id}, "
answ = ('{\'%s\': {\'id\': \'%s\', \'plex_id\': \'%s\', ' "'plex_type': '{self.plex_type}', "
% (self.__class__.__name__, self.id, self.plex_id)) "'plex_uuid': '{self.plex_uuid}', "
for key in self.__dict__: "'kodi_id': {self.kodi_id}, "
if key in ('id', 'plex_id', 'xml'): "'kodi_type': '{self.kodi_type}', "
continue "'file': '{self.file}', "
if isinstance(getattr(self, key), str): "'uri': '{self.uri}', "
answ += '\'%s\': \'%s\', ' % (key, "'guid': '{self.guid}', "
utils.try_decode(getattr(self, "'playmethod': '{self.playmethod}', "
key))) "'playcount': {self.playcount}, "
elif isinstance(getattr(self, key), unicode): "'offset': {self.offset}, "
answ += '\'%s\': \'%s\', ' % (key, getattr(self, key)) "'force_transcode': {self.force_transcode}, "
else: "'part': {self.part}, ".format(self=self))
# e.g. int answ = answ.encode('utf-8')
answ += '\'%s\': %s, ' % (key, unicode(getattr(self, key))) # etree xml.__repr__() could return string, not unicode
if self.xml is None: return answ + b"'xml': \"{self.xml}\"}}".format(self=self)
answ += '\'xml\': None}}'
else: def __str__(self):
answ += '\'xml\': \'%s\'}}' % self.xml.tag return self.__repr__()
return answ
def plex_stream_index(self, kodi_stream_index, stream_type): def plex_stream_index(self, kodi_stream_index, stream_type):
""" """
@ -412,12 +402,13 @@ def get_playlist_details_from_xml(playlist, xml):
Raises PlaylistError if something went wrong. Raises PlaylistError if something went wrong.
""" """
playlist.id = xml.get('%sID' % playlist.kind) playlist.id = xml.get('%sID' % playlist.kind).decode('utf-8')
playlist.version = xml.get('%sVersion' % playlist.kind) playlist.version = xml.get('%sVersion' % playlist.kind).decode('utf-8')
playlist.shuffled = xml.get('%sShuffled' % playlist.kind) playlist.shuffled = xml.get('%sShuffled' % playlist.kind).decode('utf-8')
playlist.selectedItemID = xml.get('%sSelectedItemID' % playlist.kind) playlist.selectedItemID = xml.get(
'%sSelectedItemID' % playlist.kind).decode('utf-8')
playlist.selectedItemOffset = xml.get( playlist.selectedItemOffset = xml.get(
'%sSelectedItemOffset' % playlist.kind) '%sSelectedItemOffset' % playlist.kind).decode('utf-8')
LOG.debug('Updated playlist from xml: %s', playlist) LOG.debug('Updated playlist from xml: %s', playlist)
@ -781,4 +772,4 @@ def get_plextype_from_xml(xml):
except (TypeError, IndexError, AttributeError): except (TypeError, IndexError, AttributeError):
LOG.error('Could not get plex metadata for plex id %s', plex_id) LOG.error('Could not get plex metadata for plex id %s', plex_id)
return return
return new_xml[0].attrib.get('type') return new_xml[0].attrib.get('type').decode('utf-8')

View file

@ -53,6 +53,19 @@ LOG = getLogger('PLEX.plex_api')
############################################################################### ###############################################################################
def _unicode_or_none(value):
"""
Tries to decode value to unicode. Returns None if this fails
"""
try:
return value.decode('utf-8')
except TypeError:
# e.g. Android TV's Python
return value.decode()
except AttributeError:
pass
class API(object): class API(object):
""" """
API(item) API(item)
@ -77,9 +90,10 @@ class API(object):
def plex_type(self): def plex_type(self):
""" """
Returns the type of media, e.g. 'movie' or 'clip' for trailers Returns the type of media, e.g. 'movie' or 'clip' for trailers as
Unicode or None.
""" """
return self.item.get('type') return _unicode_or_none(self.item.get('type'))
def playlist_type(self): def playlist_type(self):
""" """
@ -104,9 +118,9 @@ class API(object):
def plex_id(self): def plex_id(self):
""" """
Returns the Plex ratingKey such as '246922' as a string or None Returns the Plex ratingKey such as '246922' as Unicode or None
""" """
return self.item.get('ratingKey') return _unicode_or_none(self.item.get('ratingKey'))
def path(self, force_first_media=True, force_addon=False, def path(self, force_first_media=True, force_addon=False,
direct_paths=None): direct_paths=None):
@ -664,12 +678,11 @@ class API(object):
def item_id(self): def item_id(self):
""" """
Returns current playQueueItemID or if unsuccessful the playListItemID Returns current playQueueItemID or if unsuccessful the playListItemID
as Unicode.
If not found, None is returned If not found, None is returned
""" """
answ = self.item.get('playQueueItemID') return _unicode_or_none(self.item.get('playQueueItemID') or
if answ is None: self.item.get('playListItemID'))
answ = self.item.get('playListItemID')
return answ
def _data_from_part_or_media(self, key): def _data_from_part_or_media(self, key):
""" """