Merge branch 'beta-version'
This commit is contained in:
commit
d8386c4cc0
6 changed files with 83 additions and 71 deletions
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue