Merge branch 'hotfixes' into translations
This commit is contained in:
commit
fae3647407
17 changed files with 308 additions and 195 deletions
|
@ -1,5 +1,5 @@
|
||||||
[![stable version](https://img.shields.io/badge/stable_version-1.8.18-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-1.8.18-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.0.19-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.0.20-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)
|
||||||
|
|
15
addon.xml
15
addon.xml
|
@ -1,10 +1,10 @@
|
||||||
<?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.0.19" provider-name="croneter">
|
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.20" 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" />
|
||||||
<import addon="plugin.video.plexkodiconnect.movies" version="2.0.2" />
|
<import addon="plugin.video.plexkodiconnect.movies" version="2.0.4" />
|
||||||
<import addon="plugin.video.plexkodiconnect.tvshows" version="2.0.3" />
|
<import addon="plugin.video.plexkodiconnect.tvshows" version="2.0.4" />
|
||||||
</requires>
|
</requires>
|
||||||
<extension point="xbmc.python.pluginsource" library="default.py">
|
<extension point="xbmc.python.pluginsource" library="default.py">
|
||||||
<provides>video audio image</provides>
|
<provides>video audio image</provides>
|
||||||
|
@ -67,7 +67,14 @@
|
||||||
<summary lang="ru_RU">Нативная интеграция сервера Plex в Kodi</summary>
|
<summary lang="ru_RU">Нативная интеграция сервера Plex в Kodi</summary>
|
||||||
<description lang="ru_RU">Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск</description>
|
<description lang="ru_RU">Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск</description>
|
||||||
<disclaimer lang="ru_RU">Используйте на свой страх и риск</disclaimer>
|
<disclaimer lang="ru_RU">Используйте на свой страх и риск</disclaimer>
|
||||||
<news>version 2.0.19 (beta only):
|
<news>version 2.0.20 (beta only):
|
||||||
|
- Fix missing episode poster in certain views. You will have to manually reset your Kodi database to benefit
|
||||||
|
- Fix episode artwork sometimes not being complete
|
||||||
|
- Fix IndexError for certain Plex channels
|
||||||
|
- Kodi Leia: Fix playback failing
|
||||||
|
- Fix TV On Deck direct paths asking to choose between different media
|
||||||
|
|
||||||
|
version 2.0.19 (beta only):
|
||||||
- Fix PKC playback startup getting caught in infinity loop
|
- Fix PKC playback startup getting caught in infinity loop
|
||||||
- Rewire library sync, suspend sync during playback
|
- Rewire library sync, suspend sync during playback
|
||||||
- Fix playback failing in certain cases
|
- Fix playback failing in certain cases
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
version 2.0.20 (beta only):
|
||||||
|
- Fix missing episode poster in certain views. You will have to manually reset your Kodi database to benefit
|
||||||
|
- Fix episode artwork sometimes not being complete
|
||||||
|
- Fix IndexError for certain Plex channels
|
||||||
|
- Kodi Leia: Fix playback failing
|
||||||
|
- Fix TV On Deck direct paths asking to choose between different media
|
||||||
|
|
||||||
version 2.0.19 (beta only):
|
version 2.0.19 (beta only):
|
||||||
- Fix PKC playback startup getting caught in infinity loop
|
- Fix PKC playback startup getting caught in infinity loop
|
||||||
- Rewire library sync, suspend sync during playback
|
- Rewire library sync, suspend sync during playback
|
||||||
|
|
|
@ -41,6 +41,26 @@ msgctxt "#30005"
|
||||||
msgid "Username: "
|
msgid "Username: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
# Sync notification displayed if there is still artwork to be cached to Kodi
|
||||||
|
msgctxt "#30006"
|
||||||
|
msgid "Caching %s images"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# Sync notification displayed if syncing of major artwork is done
|
||||||
|
msgctxt "#30007"
|
||||||
|
msgid "Major image caching done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# PKC settings artwork: Enable notifications for artwork image sync
|
||||||
|
msgctxt "#30008"
|
||||||
|
msgid "Enable notifications for image caching"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# PKC settings artwork: Enable image caching during Kodi playback
|
||||||
|
msgctxt "#30009"
|
||||||
|
msgid "Enable image caching during Kodi playback (restart Kodi!)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
# Button text
|
# Button text
|
||||||
msgctxt "#30012"
|
msgctxt "#30012"
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
|
|
|
@ -124,8 +124,12 @@ class API(object):
|
||||||
# Local path
|
# Local path
|
||||||
filename = ans.rsplit("\\", 1)[1]
|
filename = ans.rsplit("\\", 1)[1]
|
||||||
else:
|
else:
|
||||||
# Network share
|
try:
|
||||||
filename = ans.rsplit("/", 1)[1]
|
# Network share
|
||||||
|
filename = ans.rsplit("/", 1)[1]
|
||||||
|
except IndexError:
|
||||||
|
# E.g. certain Plex channels
|
||||||
|
filename = None
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def file_path(self, force_first_media=False):
|
def file_path(self, force_first_media=False):
|
||||||
|
@ -772,33 +776,41 @@ class API(object):
|
||||||
# Artwork lookup for episodes is broken for addon paths
|
# Artwork lookup for episodes is broken for addon paths
|
||||||
# Episodes is a bit special, only get the thumb, because all
|
# Episodes is a bit special, only get the thumb, because all
|
||||||
# the other artwork will be saved under season and show
|
# the other artwork will be saved under season and show
|
||||||
art = self._one_artwork('thumb')
|
# EXCEPT if you're constructing a listitem
|
||||||
if art:
|
if not full_artwork:
|
||||||
artworks['thumb'] = art
|
art = self._one_artwork('thumb')
|
||||||
if full_artwork:
|
if art:
|
||||||
with plexdb.Get_Plex_DB() as plex_db:
|
artworks['thumb'] = art
|
||||||
db_item = plex_db.getItem_byId(self.plex_id())
|
return artworks
|
||||||
|
for kodi_artwork, plex_artwork in \
|
||||||
|
v.KODI_TO_PLEX_ARTWORK_EPISODE.iteritems():
|
||||||
|
art = self._one_artwork(plex_artwork)
|
||||||
|
if art:
|
||||||
|
artworks[kodi_artwork] = art
|
||||||
|
if not full_artwork:
|
||||||
|
return artworks
|
||||||
|
with plexdb.Get_Plex_DB() as plex_db:
|
||||||
try:
|
try:
|
||||||
season_id = db_item[3]
|
season_id = plex_db.getItem_byId(self.plex_id())[3]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return artworks
|
return artworks
|
||||||
# Grab artwork from the season
|
# Grab artwork from the season
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodidb.GetKodiDB('video') as kodi_db:
|
||||||
season_art = kodi_db.get_art(season_id, v.KODI_TYPE_SEASON)
|
season_art = kodi_db.get_art(season_id, v.KODI_TYPE_SEASON)
|
||||||
for kodi_art in season_art:
|
for kodi_art in season_art:
|
||||||
artworks['season.%s' % kodi_art] = season_art[kodi_art]
|
artworks['season.%s' % kodi_art] = season_art[kodi_art]
|
||||||
# Get the show id
|
# Get the show id
|
||||||
with plexdb.Get_Plex_DB() as plex_db:
|
with plexdb.Get_Plex_DB() as plex_db:
|
||||||
db_item = plex_db.getItem_byId(self.grandparent_id())
|
|
||||||
try:
|
try:
|
||||||
show_id = db_item[0]
|
show_id = plex_db.getItem_byKodiId(season_id,
|
||||||
|
v.KODI_TYPE_SEASON)[1]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return artworks
|
return artworks
|
||||||
# Grab more artwork from the show
|
# Grab more artwork from the show
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodidb.GetKodiDB('video') as kodi_db:
|
||||||
show_art = kodi_db.get_art(show_id, v.KODI_TYPE_SHOW)
|
show_art = kodi_db.get_art(show_id, v.KODI_TYPE_SHOW)
|
||||||
for kodi_art in show_art:
|
for kodi_art in show_art:
|
||||||
artworks['tvshow.%s' % kodi_art] = show_art[kodi_art]
|
artworks['tvshow.%s' % kodi_art] = show_art[kodi_art]
|
||||||
return artworks
|
return artworks
|
||||||
|
|
||||||
if kodi_id:
|
if kodi_id:
|
||||||
|
@ -844,7 +856,6 @@ class API(object):
|
||||||
external_id = self.retrieve_external_item_id()
|
external_id = self.retrieve_external_item_id()
|
||||||
if external_id is not None:
|
if external_id is not None:
|
||||||
artworks = self.lookup_fanart_tv(external_id[0], artworks)
|
artworks = self.lookup_fanart_tv(external_id[0], artworks)
|
||||||
LOG.debug('fanart artworks: %s', artworks)
|
|
||||||
return artworks
|
return artworks
|
||||||
|
|
||||||
def retrieve_external_item_id(self, collection=False):
|
def retrieve_external_item_id(self, collection=False):
|
||||||
|
|
|
@ -22,6 +22,10 @@ LOG = getLogger("PLEX." + __name__)
|
||||||
# Disable annoying requests warnings
|
# Disable annoying requests warnings
|
||||||
requests.packages.urllib3.disable_warnings()
|
requests.packages.urllib3.disable_warnings()
|
||||||
ARTWORK_QUEUE = Queue()
|
ARTWORK_QUEUE = Queue()
|
||||||
|
IMAGE_CACHING_SUSPENDS = ['SUSPEND_LIBRARY_THREAD', 'DB_SCAN', 'STOP_SYNC']
|
||||||
|
if not settings('imageSyncDuringPlayback') == 'true':
|
||||||
|
IMAGE_CACHING_SUSPENDS.append('SUSPEND_SYNC')
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,11 +37,9 @@ def double_urldecode(text):
|
||||||
return unquote(unquote(text))
|
return unquote(unquote(text))
|
||||||
|
|
||||||
|
|
||||||
@thread_methods(add_suspends=['SUSPEND_LIBRARY_THREAD',
|
@thread_methods(add_suspends=IMAGE_CACHING_SUSPENDS)
|
||||||
'DB_SCAN',
|
|
||||||
'STOP_SYNC'])
|
|
||||||
class Image_Cache_Thread(Thread):
|
class Image_Cache_Thread(Thread):
|
||||||
sleep_between = 50
|
sleep_between = 200
|
||||||
# Potentially issues with limited number of threads
|
# Potentially issues with limited number of threads
|
||||||
# Hence let Kodi wait till download is successful
|
# Hence let Kodi wait till download is successful
|
||||||
timeout = (35.1, 35.1)
|
timeout = (35.1, 35.1)
|
||||||
|
@ -47,6 +49,7 @@ class Image_Cache_Thread(Thread):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
LOG.info("---===### Starting Image_Cache_Thread ###===---")
|
||||||
stopped = self.stopped
|
stopped = self.stopped
|
||||||
suspended = self.suspended
|
suspended = self.suspended
|
||||||
queue = self.queue
|
queue = self.queue
|
||||||
|
@ -65,6 +68,16 @@ class Image_Cache_Thread(Thread):
|
||||||
except Empty:
|
except Empty:
|
||||||
sleep(1000)
|
sleep(1000)
|
||||||
continue
|
continue
|
||||||
|
if isinstance(url, ArtworkSyncMessage):
|
||||||
|
if state.IMAGE_SYNC_NOTIFICATIONS:
|
||||||
|
dialog('notification',
|
||||||
|
heading=lang(29999),
|
||||||
|
message=url.message,
|
||||||
|
icon='{plex}',
|
||||||
|
sound=False)
|
||||||
|
queue.task_done()
|
||||||
|
continue
|
||||||
|
url = double_urlencode(try_encode(url))
|
||||||
sleeptime = 0
|
sleeptime = 0
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -116,6 +129,44 @@ class Artwork():
|
||||||
if enableTextureCache:
|
if enableTextureCache:
|
||||||
queue = ARTWORK_QUEUE
|
queue = ARTWORK_QUEUE
|
||||||
|
|
||||||
|
def cache_major_artwork(self):
|
||||||
|
"""
|
||||||
|
Takes the existing Kodi library and caches posters and fanart.
|
||||||
|
Necessary because otherwise PKC caches artwork e.g. from fanart.tv
|
||||||
|
which basically blocks Kodi from getting needed artwork fast (e.g.
|
||||||
|
while browsing the library)
|
||||||
|
"""
|
||||||
|
if not self.enableTextureCache:
|
||||||
|
return
|
||||||
|
artworks = list()
|
||||||
|
# Get all posters and fanart/background for video and music
|
||||||
|
for kind in ('video', 'music'):
|
||||||
|
connection = kodi_sql(kind)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
for typus in ('poster', 'fanart'):
|
||||||
|
cursor.execute('SELECT url FROM art WHERE type == ?',
|
||||||
|
(typus, ))
|
||||||
|
artworks.extend(cursor.fetchall())
|
||||||
|
connection.close()
|
||||||
|
artworks_to_cache = list()
|
||||||
|
connection = kodi_sql('texture')
|
||||||
|
cursor = connection.cursor()
|
||||||
|
for url in artworks:
|
||||||
|
query = 'SELECT url FROM texture WHERE url == ? LIMIT 1'
|
||||||
|
cursor.execute(query, (url[0], ))
|
||||||
|
if not cursor.fetchone():
|
||||||
|
artworks_to_cache.append(url)
|
||||||
|
connection.close()
|
||||||
|
if not artworks_to_cache:
|
||||||
|
LOG.info('Caching of major images to Kodi texture cache done')
|
||||||
|
return
|
||||||
|
LOG.info('Caching has not been completed - caching %s major images',
|
||||||
|
len(artworks_to_cache))
|
||||||
|
self.queue.put(ArtworkSyncMessage(lang(30006) % len(artworks_to_cache)))
|
||||||
|
for url in artworks_to_cache:
|
||||||
|
self.queue.put(url[0])
|
||||||
|
self.queue.put(ArtworkSyncMessage(lang(30007)))
|
||||||
|
|
||||||
def fullTextureCacheSync(self):
|
def fullTextureCacheSync(self):
|
||||||
"""
|
"""
|
||||||
This method will sync all Kodi artwork to textures13.db
|
This method will sync all Kodi artwork to textures13.db
|
||||||
|
@ -174,10 +225,10 @@ class Artwork():
|
||||||
|
|
||||||
def cache_texture(self, url):
|
def cache_texture(self, url):
|
||||||
'''
|
'''
|
||||||
Cache a single image url to the texture cache
|
Cache a single image url to the texture cache. url: unicode
|
||||||
'''
|
'''
|
||||||
if url and self.enableTextureCache:
|
if url and self.enableTextureCache:
|
||||||
self.queue.put(double_urlencode(try_encode(url)))
|
self.queue.put(url)
|
||||||
|
|
||||||
def modify_artwork(self, artworks, kodi_id, kodi_type, cursor):
|
def modify_artwork(self, artworks, kodi_id, kodi_type, cursor):
|
||||||
"""
|
"""
|
||||||
|
@ -266,3 +317,11 @@ class Artwork():
|
||||||
for path in paths:
|
for path in paths:
|
||||||
makedirs(try_decode(translatePath("special://thumbnails/%s"
|
makedirs(try_decode(translatePath("special://thumbnails/%s"
|
||||||
% path)))
|
% path)))
|
||||||
|
|
||||||
|
|
||||||
|
class ArtworkSyncMessage(object):
|
||||||
|
"""
|
||||||
|
Put in artwork queue to display the message as a Kodi notification
|
||||||
|
"""
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = message
|
||||||
|
|
|
@ -369,7 +369,7 @@ def getRecentEpisodes(viewid, mediatype, tagname, limit):
|
||||||
append_show_title = settings('RecentTvAppendShow') == 'true'
|
append_show_title = settings('RecentTvAppendShow') == 'true'
|
||||||
append_sxxexx = settings('RecentTvAppendSeason') == 'true'
|
append_sxxexx = settings('RecentTvAppendSeason') == 'true'
|
||||||
# First we get a list of all the TV shows - filtered by tag
|
# First we get a list of all the TV shows - filtered by tag
|
||||||
allshowsIds = set()
|
allshowsIds = list()
|
||||||
params = {
|
params = {
|
||||||
'sort': {'order': "descending", 'method': "dateadded"},
|
'sort': {'order': "descending", 'method': "dateadded"},
|
||||||
'filter': {'operator': "is", 'field': "tag", 'value': "%s" % tagname},
|
'filter': {'operator': "is", 'field': "tag", 'value': "%s" % tagname},
|
||||||
|
@ -553,7 +553,7 @@ def getOnDeck(viewid, mediatype, tagname, limit):
|
||||||
append_show_title=append_show_title,
|
append_show_title=append_show_title,
|
||||||
append_sxxexx=append_sxxexx)
|
append_sxxexx=append_sxxexx)
|
||||||
if directpaths:
|
if directpaths:
|
||||||
url = api.file_path()
|
url = api.file_path(force_first_media=True)
|
||||||
else:
|
else:
|
||||||
url = ('plugin://%s.tvshows/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
url = ('plugin://%s.tvshows/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
% (v.ADDON_ID,
|
% (v.ADDON_ID,
|
||||||
|
@ -561,8 +561,7 @@ def getOnDeck(viewid, mediatype, tagname, limit):
|
||||||
api.plex_type(),
|
api.plex_type(),
|
||||||
api.file_name(force_first_media=True)))
|
api.file_name(force_first_media=True)))
|
||||||
if api.resume_point():
|
if api.resume_point():
|
||||||
listitem.setProperty('resumetime',
|
listitem.setProperty('resumetime', str(api.resume_point()))
|
||||||
str(api.resume_point()))
|
|
||||||
xbmcplugin.addDirectoryItem(
|
xbmcplugin.addDirectoryItem(
|
||||||
handle=HANDLE,
|
handle=HANDLE,
|
||||||
url=url,
|
url=url,
|
||||||
|
|
|
@ -141,7 +141,7 @@ class InitialSetup(object):
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
LOG.debug('Entering initialsetup class')
|
LOG.debug('Entering initialsetup class')
|
||||||
self.server = UserClient().getServer()
|
self.server = UserClient().get_server()
|
||||||
self.serverid = settings('plex_machineIdentifier')
|
self.serverid = settings('plex_machineIdentifier')
|
||||||
# Get Plex credentials from settings file, if they exist
|
# Get Plex credentials from settings file, if they exist
|
||||||
plexdict = PF.GetPlexLoginFromSettings()
|
plexdict = PF.GetPlexLoginFromSettings()
|
||||||
|
|
|
@ -1217,6 +1217,7 @@ class Music(Items):
|
||||||
"""
|
"""
|
||||||
Adds a single music album
|
Adds a single music album
|
||||||
children: list of child xml's, so in this case songs
|
children: list of child xml's, so in this case songs
|
||||||
|
scan_children: set to False if you don't want to add children
|
||||||
"""
|
"""
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
plex_db = self.plex_db
|
plex_db = self.plex_db
|
||||||
|
@ -1224,20 +1225,18 @@ class Music(Items):
|
||||||
api = API(item)
|
api = API(item)
|
||||||
|
|
||||||
update_item = True
|
update_item = True
|
||||||
itemid = api.plex_id()
|
plex_id = api.plex_id()
|
||||||
if not itemid:
|
if not plex_id:
|
||||||
LOG.error('Error processing Album, skipping')
|
LOG.error('Error processing Album, skipping')
|
||||||
return
|
return
|
||||||
plex_dbitem = plex_db.getItem_byId(itemid)
|
plex_dbitem = plex_db.getItem_byId(plex_id)
|
||||||
try:
|
try:
|
||||||
albumid = plex_dbitem[0]
|
album_id = plex_dbitem[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Albumid not found
|
|
||||||
update_item = False
|
update_item = False
|
||||||
|
|
||||||
# The album details #####
|
# The album details #####
|
||||||
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
dateadded = api.date_created()
|
|
||||||
userdata = api.userdata()
|
userdata = api.userdata()
|
||||||
checksum = api.checksum()
|
checksum = api.checksum()
|
||||||
|
|
||||||
|
@ -1269,22 +1268,22 @@ class Music(Items):
|
||||||
|
|
||||||
# UPDATE THE ALBUM #####
|
# UPDATE THE ALBUM #####
|
||||||
if update_item:
|
if update_item:
|
||||||
LOG.info("UPDATE album itemid: %s - Name: %s", itemid, name)
|
LOG.info("UPDATE album plex_id: %s - Name: %s", plex_id, name)
|
||||||
# Update the checksum in plex table
|
# Update the checksum in plex table
|
||||||
plex_db.updateReference(itemid, checksum)
|
plex_db.updateReference(plex_id, checksum)
|
||||||
|
|
||||||
# OR ADD THE ALBUM #####
|
# OR ADD THE ALBUM #####
|
||||||
else:
|
else:
|
||||||
LOG.info("ADD album itemid: %s - Name: %s", itemid, name)
|
LOG.info("ADD album plex_id: %s - Name: %s", plex_id, name)
|
||||||
# safety checks: It looks like plex supports the same artist
|
# safety checks: It looks like plex supports the same artist
|
||||||
# multiple times.
|
# multiple times.
|
||||||
# Kodi doesn't allow that. In case that happens we just merge the
|
# Kodi doesn't allow that. In case that happens we just merge the
|
||||||
# artist entries.
|
# artist entries.
|
||||||
albumid = self.kodi_db.addAlbum(name, musicBrainzId)
|
album_id = self.kodi_db.addAlbum(name, musicBrainzId)
|
||||||
# Create the reference in plex table
|
# Create the reference in plex table
|
||||||
plex_db.addReference(itemid,
|
plex_db.addReference(plex_id,
|
||||||
v.PLEX_TYPE_ALBUM,
|
v.PLEX_TYPE_ALBUM,
|
||||||
albumid,
|
album_id,
|
||||||
v.KODI_TYPE_ALBUM,
|
v.KODI_TYPE_ALBUM,
|
||||||
view_id=viewid,
|
view_id=viewid,
|
||||||
checksum=checksum)
|
checksum=checksum)
|
||||||
|
@ -1302,7 +1301,7 @@ class Music(Items):
|
||||||
kodicursor.execute(query, (artistname, year, self.genre, bio,
|
kodicursor.execute(query, (artistname, year, self.genre, bio,
|
||||||
thumb, rating, lastScraped,
|
thumb, rating, lastScraped,
|
||||||
v.KODI_TYPE_ALBUM, studio,
|
v.KODI_TYPE_ALBUM, studio,
|
||||||
self.compilation, albumid))
|
self.compilation, album_id))
|
||||||
elif v.KODIVERSION == 17:
|
elif v.KODIVERSION == 17:
|
||||||
# Kodi Krypton
|
# Kodi Krypton
|
||||||
query = '''
|
query = '''
|
||||||
|
@ -1315,7 +1314,7 @@ class Music(Items):
|
||||||
kodicursor.execute(query, (artistname, year, self.genre, bio,
|
kodicursor.execute(query, (artistname, year, self.genre, bio,
|
||||||
thumb, rating, lastScraped,
|
thumb, rating, lastScraped,
|
||||||
v.KODI_TYPE_ALBUM, studio,
|
v.KODI_TYPE_ALBUM, studio,
|
||||||
self.compilation, albumid))
|
self.compilation, album_id))
|
||||||
elif v.KODIVERSION == 16:
|
elif v.KODIVERSION == 16:
|
||||||
# Kodi Jarvis
|
# Kodi Jarvis
|
||||||
query = '''
|
query = '''
|
||||||
|
@ -1328,71 +1327,46 @@ class Music(Items):
|
||||||
kodicursor.execute(query, (artistname, year, self.genre, bio,
|
kodicursor.execute(query, (artistname, year, self.genre, bio,
|
||||||
thumb, rating, lastScraped,
|
thumb, rating, lastScraped,
|
||||||
v.KODI_TYPE_ALBUM, studio,
|
v.KODI_TYPE_ALBUM, studio,
|
||||||
self.compilation, albumid))
|
self.compilation, album_id))
|
||||||
|
|
||||||
# Associate the parentid for plex reference
|
# Associate the parentid for plex reference
|
||||||
parent_id = api.parent_plex_id()
|
parent_id = api.parent_plex_id()
|
||||||
|
artist_id = None
|
||||||
if parent_id is not None:
|
if parent_id is not None:
|
||||||
plex_dbartist = plex_db.getItem_byId(parent_id)
|
|
||||||
try:
|
try:
|
||||||
artistid = plex_dbartist[0]
|
artist_id = plex_db.getItem_byId(parent_id)[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
LOG.info('Artist %s does not exist in plex database',
|
LOG.info('Artist %s does not yet exist in Plex DB', parent_id)
|
||||||
parent_id)
|
|
||||||
artist = GetPlexMetadata(parent_id)
|
artist = GetPlexMetadata(parent_id)
|
||||||
# Item may not be an artist, verification necessary.
|
try:
|
||||||
if artist is not None and artist != 401:
|
artist[0].attrib
|
||||||
if artist[0].attrib.get('type') == v.PLEX_TYPE_ARTIST:
|
except (TypeError, IndexError, AttributeError):
|
||||||
# Update with the parent_id, for remove reference
|
LOG.error('Could not get artist xml for %s', parent_id)
|
||||||
plex_db.addReference(parent_id,
|
else:
|
||||||
v.PLEX_TYPE_ARTIST,
|
self.add_updateArtist(artist[0])
|
||||||
parent_id,
|
plex_dbartist = plex_db.getItem_byId(parent_id)
|
||||||
v.KODI_TYPE_ARTIST,
|
try:
|
||||||
view_id=viewid)
|
artist_id = plex_dbartist[0]
|
||||||
plex_db.updateParentId(itemid, parent_id)
|
except TypeError:
|
||||||
else:
|
LOG.error('Adding artist failed for %s', parent_id)
|
||||||
# Update plex reference with the artistid
|
# Update plex reference with the artist_id
|
||||||
plex_db.updateParentId(itemid, artistid)
|
plex_db.updateParentId(plex_id, artist_id)
|
||||||
|
|
||||||
# Assign main artists to album
|
|
||||||
# Plex unfortunately only supports 1 artist :-(
|
|
||||||
artist_id = parent_id
|
|
||||||
plex_dbartist = plex_db.getItem_byId(artist_id)
|
|
||||||
try:
|
|
||||||
artistid = plex_dbartist[0]
|
|
||||||
except TypeError:
|
|
||||||
# Artist does not exist in plex database, create the reference
|
|
||||||
LOG.info('Artist %s does not exist in Plex database', artist_id)
|
|
||||||
artist = GetPlexMetadata(artist_id)
|
|
||||||
if artist is not None and artist != 401:
|
|
||||||
self.add_updateArtist(artist[0])
|
|
||||||
plex_dbartist = plex_db.getItem_byId(artist_id)
|
|
||||||
artistid = plex_dbartist[0]
|
|
||||||
else:
|
|
||||||
# Best take this name over anything else.
|
|
||||||
query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?"
|
|
||||||
kodicursor.execute(query, (artistname, artistid,))
|
|
||||||
LOG.info("UPDATE artist: strArtist: %s, idArtist: %s",
|
|
||||||
artistname, artistid)
|
|
||||||
|
|
||||||
# Add artist to album
|
# Add artist to album
|
||||||
query = '''
|
query = '''
|
||||||
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
'''
|
'''
|
||||||
kodicursor.execute(query, (artistid, albumid, artistname))
|
kodicursor.execute(query, (artist_id, album_id, artistname))
|
||||||
# Update discography
|
# Update discography
|
||||||
query = '''
|
query = '''
|
||||||
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
'''
|
'''
|
||||||
kodicursor.execute(query, (artistid, name, year))
|
kodicursor.execute(query, (artist_id, name, year))
|
||||||
# Update plex reference with parentid
|
|
||||||
plex_db.updateParentId(artist_id, albumid)
|
|
||||||
if v.KODIVERSION < 18:
|
if v.KODIVERSION < 18:
|
||||||
self.kodi_db.addMusicGenres(albumid, self.genres, v.KODI_TYPE_ALBUM)
|
self.kodi_db.addMusicGenres(album_id, self.genres, v.KODI_TYPE_ALBUM)
|
||||||
# Update artwork
|
# Update artwork
|
||||||
artwork.modify_artwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
artwork.modify_artwork(artworks, album_id, v.KODI_TYPE_ALBUM, kodicursor)
|
||||||
# Add all children - all tracks
|
# Add all children - all tracks
|
||||||
if scan_children:
|
if scan_children:
|
||||||
for child in children:
|
for child in children:
|
||||||
|
|
|
@ -50,7 +50,8 @@ STATE_SETTINGS = {
|
||||||
'remapSMBphotoNew': 'remapSMBphotoNew',
|
'remapSMBphotoNew': 'remapSMBphotoNew',
|
||||||
'enableMusic': 'ENABLE_MUSIC',
|
'enableMusic': 'ENABLE_MUSIC',
|
||||||
'forceReloadSkinOnPlaybackStop': 'FORCE_RELOAD_SKIN',
|
'forceReloadSkinOnPlaybackStop': 'FORCE_RELOAD_SKIN',
|
||||||
'fetch_pms_item_number': 'FETCH_PMS_ITEM_NUMBER'
|
'fetch_pms_item_number': 'FETCH_PMS_ITEM_NUMBER',
|
||||||
|
'imageSyncNotifications': 'IMAGE_SYNC_NOTIFICATIONS'
|
||||||
}
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
|
@ -16,6 +16,7 @@ from downloadutils import DownloadUtils as DU
|
||||||
import itemtypes
|
import itemtypes
|
||||||
import plexdb_functions as plexdb
|
import plexdb_functions as plexdb
|
||||||
import kodidb_functions as kodidb
|
import kodidb_functions as kodidb
|
||||||
|
import artwork
|
||||||
import videonodes
|
import videonodes
|
||||||
import variables as v
|
import variables as v
|
||||||
|
|
||||||
|
@ -1453,7 +1454,6 @@ class LibrarySync(Thread):
|
||||||
elif state.RUN_LIB_SCAN == 'textures':
|
elif state.RUN_LIB_SCAN == 'textures':
|
||||||
state.DB_SCAN = True
|
state.DB_SCAN = True
|
||||||
window('plex_dbScan', value="true")
|
window('plex_dbScan', value="true")
|
||||||
import artwork
|
|
||||||
artwork.Artwork().fullTextureCacheSync()
|
artwork.Artwork().fullTextureCacheSync()
|
||||||
window('plex_dbScan', clear=True)
|
window('plex_dbScan', clear=True)
|
||||||
state.DB_SCAN = False
|
state.DB_SCAN = False
|
||||||
|
@ -1517,8 +1517,6 @@ class LibrarySync(Thread):
|
||||||
last_time_sync = utils.unix_timestamp()
|
last_time_sync = utils.unix_timestamp()
|
||||||
window('plex_dbScan', clear=True)
|
window('plex_dbScan', clear=True)
|
||||||
state.DB_SCAN = False
|
state.DB_SCAN = False
|
||||||
# Start the fanart download thread
|
|
||||||
self.fanartthread.start()
|
|
||||||
|
|
||||||
while not self.stopped():
|
while not self.stopped():
|
||||||
# In the event the server goes offline
|
# In the event the server goes offline
|
||||||
|
@ -1544,6 +1542,7 @@ class LibrarySync(Thread):
|
||||||
initial_sync_done = True
|
initial_sync_done = True
|
||||||
kodi_db_version_checked = True
|
kodi_db_version_checked = True
|
||||||
last_sync = utils.unix_timestamp()
|
last_sync = utils.unix_timestamp()
|
||||||
|
self.fanartthread.start()
|
||||||
else:
|
else:
|
||||||
LOG.error('Initial start-up full sync unsuccessful')
|
LOG.error('Initial start-up full sync unsuccessful')
|
||||||
xbmc.executebuiltin('InhibitIdleShutdown(false)')
|
xbmc.executebuiltin('InhibitIdleShutdown(false)')
|
||||||
|
@ -1587,6 +1586,8 @@ class LibrarySync(Thread):
|
||||||
if settings('FanartTV') == 'true':
|
if settings('FanartTV') == 'true':
|
||||||
self.sync_fanart()
|
self.sync_fanart()
|
||||||
LOG.info('Done initial sync on Kodi startup')
|
LOG.info('Done initial sync on Kodi startup')
|
||||||
|
artwork.Artwork().cache_major_artwork()
|
||||||
|
self.fanartthread.start()
|
||||||
else:
|
else:
|
||||||
LOG.info('Startup sync has not yet been successful')
|
LOG.info('Startup sync has not yet been successful')
|
||||||
window('plex_dbScan', clear=True)
|
window('plex_dbScan', clear=True)
|
||||||
|
|
|
@ -4,12 +4,31 @@ from cPickle import dumps, loads
|
||||||
|
|
||||||
from xbmcgui import Window
|
from xbmcgui import Window
|
||||||
from xbmc import log, LOGDEBUG
|
from xbmc import log, LOGDEBUG
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
WINDOW = Window(10000)
|
WINDOW = Window(10000)
|
||||||
PREFIX = 'PLEX.%s: ' % __name__
|
PREFIX = 'PLEX.%s: ' % __name__
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def try_encode(input_str, encoding='utf-8'):
|
||||||
|
"""
|
||||||
|
Will try to encode input_str (in unicode) to encoding. This possibly
|
||||||
|
fails with e.g. Android TV's Python, which does not accept arguments for
|
||||||
|
string.encode()
|
||||||
|
|
||||||
|
COPY to avoid importing utils on calling default.py
|
||||||
|
"""
|
||||||
|
if isinstance(input_str, str):
|
||||||
|
# already encoded
|
||||||
|
return input_str
|
||||||
|
try:
|
||||||
|
input_str = input_str.encode(encoding, "ignore")
|
||||||
|
except TypeError:
|
||||||
|
input_str = input_str.encode()
|
||||||
|
return input_str
|
||||||
|
|
||||||
|
|
||||||
def pickl_window(property, value=None, clear=False):
|
def pickl_window(property, value=None, clear=False):
|
||||||
"""
|
"""
|
||||||
Get or set window property - thread safe! For use with Pickle
|
Get or set window property - thread safe! For use with Pickle
|
||||||
|
@ -20,7 +39,7 @@ def pickl_window(property, value=None, clear=False):
|
||||||
elif value is not None:
|
elif value is not None:
|
||||||
WINDOW.setProperty(property, value)
|
WINDOW.setProperty(property, value)
|
||||||
else:
|
else:
|
||||||
return WINDOW.getProperty(property)
|
return try_encode(WINDOW.getProperty(property))
|
||||||
|
|
||||||
|
|
||||||
def pickle_me(obj, window_var='plex_result'):
|
def pickle_me(obj, window_var='plex_result'):
|
||||||
|
|
|
@ -39,6 +39,8 @@ FORCE_RELOAD_SKIN = True
|
||||||
# Stemming from the PKC settings.xml
|
# Stemming from the PKC settings.xml
|
||||||
# Shall we show Kodi dialogs when synching?
|
# Shall we show Kodi dialogs when synching?
|
||||||
SYNC_DIALOG = True
|
SYNC_DIALOG = True
|
||||||
|
# Shall Kodi show dialogs for syncing/caching images? (e.g. images left to sync)
|
||||||
|
IMAGE_SYNC_NOTIFICATIONS = True
|
||||||
# Is synching of Plex music enabled?
|
# Is synching of Plex music enabled?
|
||||||
ENABLE_MUSIC = True
|
ENABLE_MUSIC = True
|
||||||
# How often shall we sync?
|
# How often shall we sync?
|
||||||
|
|
|
@ -22,7 +22,9 @@ LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
@thread_methods(add_suspends=['SUSPEND_USER_CLIENT'])
|
@thread_methods(add_suspends=['SUSPEND_USER_CLIENT'])
|
||||||
class UserClient(Thread):
|
class UserClient(Thread):
|
||||||
|
"""
|
||||||
|
Manage Plex users
|
||||||
|
"""
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
__shared_state = {}
|
__shared_state = {}
|
||||||
|
|
||||||
|
@ -32,100 +34,98 @@ class UserClient(Thread):
|
||||||
self.auth = True
|
self.auth = True
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
|
|
||||||
self.currUser = None
|
self.user = None
|
||||||
self.currServer = None
|
self.has_access = True
|
||||||
self.currToken = None
|
|
||||||
self.HasAccess = True
|
|
||||||
self.AdditionalUser = []
|
|
||||||
|
|
||||||
self.userSettings = None
|
self.server = None
|
||||||
|
self.server_name = None
|
||||||
|
self.machine_identifier = None
|
||||||
|
self.token = None
|
||||||
|
self.ssl = None
|
||||||
|
self.sslcert = None
|
||||||
|
|
||||||
self.addon = xbmcaddon.Addon()
|
self.addon = xbmcaddon.Addon()
|
||||||
self.doUtils = DU()
|
self.do_utils = None
|
||||||
|
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
def getUsername(self):
|
def get_server(self):
|
||||||
"""
|
"""
|
||||||
Returns username as unicode
|
Get the current PMS' URL
|
||||||
"""
|
"""
|
||||||
username = settings('username')
|
|
||||||
if not username:
|
|
||||||
LOG.debug("No username saved, trying to get Plex username")
|
|
||||||
username = settings('plexLogin')
|
|
||||||
if not username:
|
|
||||||
LOG.debug("Also no Plex username found")
|
|
||||||
return ""
|
|
||||||
return username
|
|
||||||
|
|
||||||
def getServer(self, prefix=True):
|
|
||||||
# Original host
|
# Original host
|
||||||
self.servername = settings('plex_servername')
|
self.server_name = settings('plex_servername')
|
||||||
HTTPS = settings('https') == "true"
|
https = settings('https') == "true"
|
||||||
host = settings('ipaddress')
|
host = settings('ipaddress')
|
||||||
port = settings('port')
|
port = settings('port')
|
||||||
self.machineIdentifier = settings('plex_machineIdentifier')
|
self.machine_identifier = settings('plex_machineIdentifier')
|
||||||
|
|
||||||
server = host + ":" + port
|
|
||||||
|
|
||||||
if not host:
|
if not host:
|
||||||
LOG.debug("No server information saved.")
|
LOG.debug("No server information saved.")
|
||||||
return False
|
return False
|
||||||
|
server = host + ":" + port
|
||||||
# If https is true
|
# If https is true
|
||||||
if prefix and HTTPS:
|
if https:
|
||||||
server = "https://%s" % server
|
server = "https://%s" % server
|
||||||
# If https is false
|
# If https is false
|
||||||
elif prefix and not HTTPS:
|
else:
|
||||||
server = "http://%s" % server
|
server = "http://%s" % server
|
||||||
# User entered IP; we need to get the machineIdentifier
|
# User entered IP; we need to get the machineIdentifier
|
||||||
if self.machineIdentifier == '' and prefix is True:
|
if not self.machine_identifier:
|
||||||
self.machineIdentifier = PF.GetMachineIdentifier(server)
|
self.machine_identifier = PF.GetMachineIdentifier(server)
|
||||||
if self.machineIdentifier is None:
|
if not self.machine_identifier:
|
||||||
self.machineIdentifier = ''
|
self.machine_identifier = ''
|
||||||
settings('plex_machineIdentifier', value=self.machineIdentifier)
|
settings('plex_machineIdentifier', value=self.machine_identifier)
|
||||||
LOG.debug('Returning active server: %s', server)
|
LOG.debug('Returning active server: %s', server)
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def getSSLverify(self):
|
@staticmethod
|
||||||
# Verify host certificate
|
def get_ssl_verify():
|
||||||
|
"""
|
||||||
|
Do we need to verify the SSL certificate? Return None if that is the
|
||||||
|
case, else False
|
||||||
|
"""
|
||||||
return None if settings('sslverify') == 'true' else False
|
return None if settings('sslverify') == 'true' else False
|
||||||
|
|
||||||
def getSSL(self):
|
@staticmethod
|
||||||
# Client side certificate
|
def get_ssl_certificate():
|
||||||
|
"""
|
||||||
|
Client side certificate
|
||||||
|
"""
|
||||||
return None if settings('sslcert') == 'None' \
|
return None if settings('sslcert') == 'None' \
|
||||||
else settings('sslcert')
|
else settings('sslcert')
|
||||||
|
|
||||||
def setUserPref(self):
|
def set_user_prefs(self):
|
||||||
|
"""
|
||||||
|
Load a user's profile picture
|
||||||
|
"""
|
||||||
LOG.debug('Setting user preferences')
|
LOG.debug('Setting user preferences')
|
||||||
# Only try to get user avatar if there is a token
|
# Only try to get user avatar if there is a token
|
||||||
if self.currToken:
|
if self.token:
|
||||||
url = PF.GetUserArtworkURL(self.currUser)
|
url = PF.GetUserArtworkURL(self.user)
|
||||||
if url:
|
if url:
|
||||||
window('PlexUserImage', value=url)
|
window('PlexUserImage', value=url)
|
||||||
# Set resume point max
|
|
||||||
# url = "{server}/emby/System/Configuration?format=json"
|
|
||||||
# result = doUtils.downloadUrl(url)
|
|
||||||
|
|
||||||
def hasAccess(self):
|
@staticmethod
|
||||||
|
def check_access():
|
||||||
# Plex: always return True for now
|
# Plex: always return True for now
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def loadCurrUser(self, username, userId, usertoken, authenticated=False):
|
def load_user(self, username, user_id, usertoken, authenticated=False):
|
||||||
|
"""
|
||||||
|
Load the current user's details for PKC
|
||||||
|
"""
|
||||||
LOG.debug('Loading current user')
|
LOG.debug('Loading current user')
|
||||||
doUtils = self.doUtils
|
self.token = usertoken
|
||||||
|
self.server = self.get_server()
|
||||||
self.currToken = usertoken
|
self.ssl = self.get_ssl_verify()
|
||||||
self.currServer = self.getServer()
|
self.sslcert = self.get_ssl_certificate()
|
||||||
self.ssl = self.getSSLverify()
|
|
||||||
self.sslcert = self.getSSL()
|
|
||||||
|
|
||||||
if authenticated is False:
|
if authenticated is False:
|
||||||
if self.currServer is None:
|
if self.server is None:
|
||||||
return False
|
return False
|
||||||
LOG.debug('Testing validity of current token')
|
LOG.debug('Testing validity of current token')
|
||||||
res = PF.check_connection(self.currServer,
|
res = PF.check_connection(self.server,
|
||||||
token=self.currToken,
|
token=self.token,
|
||||||
verifySSL=self.ssl)
|
verifySSL=self.ssl)
|
||||||
if res is False:
|
if res is False:
|
||||||
# PMS probably offline
|
# PMS probably offline
|
||||||
|
@ -138,7 +138,7 @@ class UserClient(Thread):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Set to windows property
|
# Set to windows property
|
||||||
state.PLEX_USER_ID = userId or None
|
state.PLEX_USER_ID = user_id or None
|
||||||
state.PLEX_USERNAME = username
|
state.PLEX_USERNAME = username
|
||||||
# This is the token for the current PMS (might also be '')
|
# This is the token for the current PMS (might also be '')
|
||||||
window('pms_token', value=usertoken)
|
window('pms_token', value=usertoken)
|
||||||
|
@ -150,9 +150,9 @@ class UserClient(Thread):
|
||||||
window('plex_restricteduser', value=settings('plex_restricteduser'))
|
window('plex_restricteduser', value=settings('plex_restricteduser'))
|
||||||
state.RESTRICTED_USER = True \
|
state.RESTRICTED_USER = True \
|
||||||
if settings('plex_restricteduser') == 'true' else False
|
if settings('plex_restricteduser') == 'true' else False
|
||||||
window('pms_server', value=self.currServer)
|
window('pms_server', value=self.server)
|
||||||
window('plex_machineIdentifier', value=self.machineIdentifier)
|
window('plex_machineIdentifier', value=self.machine_identifier)
|
||||||
window('plex_servername', value=self.servername)
|
window('plex_servername', value=self.server_name)
|
||||||
window('plex_authenticated', value='true')
|
window('plex_authenticated', value='true')
|
||||||
state.AUTHENTICATED = True
|
state.AUTHENTICATED = True
|
||||||
|
|
||||||
|
@ -166,19 +166,22 @@ class UserClient(Thread):
|
||||||
if settings('force_transcode_pix') == "1" else 'false')
|
if settings('force_transcode_pix') == "1" else 'false')
|
||||||
|
|
||||||
# Start DownloadUtils session
|
# Start DownloadUtils session
|
||||||
doUtils.startSession(reset=True)
|
self.do_utils = DU()
|
||||||
# self.getAdditionalUsers()
|
self.do_utils.startSession(reset=True)
|
||||||
# Set user preferences in settings
|
# Set user preferences in settings
|
||||||
self.currUser = username
|
self.user = username
|
||||||
self.setUserPref()
|
self.set_user_prefs()
|
||||||
|
|
||||||
# Writing values to settings file
|
# Writing values to settings file
|
||||||
settings('username', value=username)
|
settings('username', value=username)
|
||||||
settings('userid', value=userId)
|
settings('userid', value=user_id)
|
||||||
settings('accessToken', value=usertoken)
|
settings('accessToken', value=usertoken)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def authenticate(self):
|
def authenticate(self):
|
||||||
|
"""
|
||||||
|
Authenticate the current user
|
||||||
|
"""
|
||||||
LOG.debug('Authenticating user')
|
LOG.debug('Authenticating user')
|
||||||
|
|
||||||
# Give attempts at entering password / selecting user
|
# Give attempts at entering password / selecting user
|
||||||
|
@ -198,7 +201,7 @@ class UserClient(Thread):
|
||||||
LOG.error("Error, no settings.xml found.")
|
LOG.error("Error, no settings.xml found.")
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return False
|
return False
|
||||||
server = self.getServer()
|
server = self.get_server()
|
||||||
# If there is no server we can connect to
|
# If there is no server we can connect to
|
||||||
if not server:
|
if not server:
|
||||||
LOG.info("Missing server information.")
|
LOG.info("Missing server information.")
|
||||||
|
@ -213,10 +216,10 @@ class UserClient(Thread):
|
||||||
# Found a user in the settings, try to authenticate
|
# Found a user in the settings, try to authenticate
|
||||||
if username and enforceLogin == 'false':
|
if username and enforceLogin == 'false':
|
||||||
LOG.debug('Trying to authenticate with old settings')
|
LOG.debug('Trying to authenticate with old settings')
|
||||||
answ = self.loadCurrUser(username,
|
answ = self.load_user(username,
|
||||||
userId,
|
userId,
|
||||||
usertoken,
|
usertoken,
|
||||||
authenticated=False)
|
authenticated=False)
|
||||||
if answ is True:
|
if answ is True:
|
||||||
# SUCCESS: loaded a user from the settings
|
# SUCCESS: loaded a user from the settings
|
||||||
return True
|
return True
|
||||||
|
@ -248,7 +251,7 @@ class UserClient(Thread):
|
||||||
userId = ''
|
userId = ''
|
||||||
usertoken = ''
|
usertoken = ''
|
||||||
|
|
||||||
if self.loadCurrUser(username, userId, usertoken, authenticated=False):
|
if self.load_user(username, userId, usertoken, authenticated=False):
|
||||||
# SUCCESS: loaded a user from the settings
|
# SUCCESS: loaded a user from the settings
|
||||||
return True
|
return True
|
||||||
# Something went wrong, try again
|
# Something went wrong, try again
|
||||||
|
@ -256,9 +259,12 @@ class UserClient(Thread):
|
||||||
self.retry += 1
|
self.retry += 1
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def resetClient(self):
|
def reset_client(self):
|
||||||
|
"""
|
||||||
|
Reset all user settings
|
||||||
|
"""
|
||||||
LOG.debug("Reset UserClient authentication.")
|
LOG.debug("Reset UserClient authentication.")
|
||||||
self.doUtils.stopSession()
|
self.do_utils.stopSession()
|
||||||
|
|
||||||
window('plex_authenticated', clear=True)
|
window('plex_authenticated', clear=True)
|
||||||
state.AUTHENTICATED = False
|
state.AUTHENTICATED = False
|
||||||
|
@ -279,13 +285,16 @@ class UserClient(Thread):
|
||||||
settings('userid', value='')
|
settings('userid', value='')
|
||||||
settings('accessToken', value='')
|
settings('accessToken', value='')
|
||||||
|
|
||||||
self.currToken = None
|
self.token = None
|
||||||
self.auth = True
|
self.auth = True
|
||||||
self.currUser = None
|
self.user = None
|
||||||
|
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""
|
||||||
|
Do the work
|
||||||
|
"""
|
||||||
LOG.info("----===## Starting UserClient ##===----")
|
LOG.info("----===## Starting UserClient ##===----")
|
||||||
stopped = self.stopped
|
stopped = self.stopped
|
||||||
suspended = self.suspended
|
suspended = self.suspended
|
||||||
|
@ -299,19 +308,14 @@ class UserClient(Thread):
|
||||||
sleep(500)
|
sleep(500)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Verify the connection status to server
|
|
||||||
elif state.PMS_STATUS == "restricted":
|
|
||||||
# Parental control is restricting access
|
|
||||||
self.HasAccess = False
|
|
||||||
|
|
||||||
elif state.PMS_STATUS == "401":
|
elif state.PMS_STATUS == "401":
|
||||||
# Unauthorized access, revoke token
|
# Unauthorized access, revoke token
|
||||||
state.PMS_STATUS = 'Auth'
|
state.PMS_STATUS = 'Auth'
|
||||||
window('plex_serverStatus', value='Auth')
|
window('plex_serverStatus', value='Auth')
|
||||||
self.resetClient()
|
self.reset_client()
|
||||||
sleep(3000)
|
sleep(3000)
|
||||||
|
|
||||||
if self.auth and (self.currUser is None):
|
if self.auth and (self.user is None):
|
||||||
# Try to authenticate user
|
# Try to authenticate user
|
||||||
if not state.PMS_STATUS or state.PMS_STATUS == "Auth":
|
if not state.PMS_STATUS or state.PMS_STATUS == "Auth":
|
||||||
# Set auth flag because we no longer need
|
# Set auth flag because we no longer need
|
||||||
|
@ -320,16 +324,16 @@ class UserClient(Thread):
|
||||||
if self.authenticate():
|
if self.authenticate():
|
||||||
# Successfully authenticated and loaded a user
|
# Successfully authenticated and loaded a user
|
||||||
LOG.info("Successfully authenticated!")
|
LOG.info("Successfully authenticated!")
|
||||||
LOG.info("Current user: %s", self.currUser)
|
LOG.info("Current user: %s", self.user)
|
||||||
LOG.info("Current userId: %s", state.PLEX_USER_ID)
|
LOG.info("Current userId: %s", state.PLEX_USER_ID)
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
state.SUSPEND_LIBRARY_THREAD = False
|
state.SUSPEND_LIBRARY_THREAD = False
|
||||||
window('plex_serverStatus', clear=True)
|
window('plex_serverStatus', clear=True)
|
||||||
state.PMS_STATUS = False
|
state.PMS_STATUS = False
|
||||||
|
|
||||||
if not self.auth and (self.currUser is None):
|
if not self.auth and (self.user is None):
|
||||||
# Loop if no server found
|
# Loop if no server found
|
||||||
server = self.getServer()
|
server = self.get_server()
|
||||||
|
|
||||||
# The status Stop is for when user cancelled password dialog.
|
# The status Stop is for when user cancelled password dialog.
|
||||||
# Or retried too many times
|
# Or retried too many times
|
||||||
|
|
|
@ -325,6 +325,13 @@ KODI_TO_PLEX_ARTWORK = {
|
||||||
'fanart': 'art'
|
'fanart': 'art'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KODI_TO_PLEX_ARTWORK_EPISODE = {
|
||||||
|
'thumb': 'thumb',
|
||||||
|
'poster': 'grandparentThumb',
|
||||||
|
'banner': 'banner',
|
||||||
|
'fanart': 'art'
|
||||||
|
}
|
||||||
|
|
||||||
# Might be implemented in the future: 'icon', 'landscape' (16:9)
|
# Might be implemented in the future: 'icon', 'landscape' (16:9)
|
||||||
ALL_KODI_ARTWORK = (
|
ALL_KODI_ARTWORK = (
|
||||||
'thumb',
|
'thumb',
|
||||||
|
|
|
@ -125,6 +125,8 @@
|
||||||
<setting id="enableTextureCache" label="30512" type="bool" default="true" /> <!-- Force Artwork Caching -->
|
<setting id="enableTextureCache" label="30512" type="bool" default="true" /> <!-- Force Artwork Caching -->
|
||||||
<setting id="FanartTV" label="30539" type="bool" default="false" /><!-- Download additional art from FanArtTV -->
|
<setting id="FanartTV" label="30539" type="bool" default="false" /><!-- Download additional art from FanArtTV -->
|
||||||
<setting label="39222" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=fanart)" option="close" visible="eq(-1,true)" subsetting="true" /> <!-- Look for missing fanart on FanartTV now -->
|
<setting label="39222" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=fanart)" option="close" visible="eq(-1,true)" subsetting="true" /> <!-- Look for missing fanart on FanartTV now -->
|
||||||
|
<setting id="imageSyncNotifications" label="30008" type="bool" default="true" /><!-- Enable notifications for image caching -->
|
||||||
|
<setting id="imageSyncDuringPlayback" label="30009" type="bool" default="true" /><!-- Enable image caching during Kodi playback (restart Kodi!) -->
|
||||||
<setting label="39020" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=texturecache)" option="close" /> <!-- Cache all images to Kodi texture cache now -->
|
<setting label="39020" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=texturecache)" option="close" /> <!-- Cache all images to Kodi texture cache now -->
|
||||||
</category>
|
</category>
|
||||||
<!--
|
<!--
|
||||||
|
|
12
service.py
12
service.py
|
@ -135,7 +135,7 @@ class Service():
|
||||||
if window('plex_online') == "true":
|
if window('plex_online') == "true":
|
||||||
# Plex server is online
|
# Plex server is online
|
||||||
# Verify if user is set and has access to the server
|
# Verify if user is set and has access to the server
|
||||||
if (self.user.currUser is not None) and self.user.HasAccess:
|
if (self.user.user is not None) and self.user.has_access:
|
||||||
if not self.kodimonitor_running:
|
if not self.kodimonitor_running:
|
||||||
# Start up events
|
# Start up events
|
||||||
self.warn_auth = True
|
self.warn_auth = True
|
||||||
|
@ -145,7 +145,7 @@ class Service():
|
||||||
dialog('notification',
|
dialog('notification',
|
||||||
lang(29999),
|
lang(29999),
|
||||||
"%s %s" % (lang(33000),
|
"%s %s" % (lang(33000),
|
||||||
self.user.currUser),
|
self.user.user),
|
||||||
icon='{plex}',
|
icon='{plex}',
|
||||||
time=2000,
|
time=2000,
|
||||||
sound=False)
|
sound=False)
|
||||||
|
@ -178,7 +178,7 @@ class Service():
|
||||||
self.image_cache_thread_running = True
|
self.image_cache_thread_running = True
|
||||||
self.image_cache_thread.start()
|
self.image_cache_thread.start()
|
||||||
else:
|
else:
|
||||||
if (self.user.currUser is None) and self.warn_auth:
|
if (self.user.user is None) and self.warn_auth:
|
||||||
# Alert user is not authenticated and suppress future
|
# Alert user is not authenticated and suppress future
|
||||||
# warning
|
# warning
|
||||||
self.warn_auth = False
|
self.warn_auth = False
|
||||||
|
@ -187,9 +187,9 @@ class Service():
|
||||||
# User access is restricted.
|
# User access is restricted.
|
||||||
# Keep verifying until access is granted
|
# Keep verifying until access is granted
|
||||||
# unless server goes offline or Kodi is shut down.
|
# unless server goes offline or Kodi is shut down.
|
||||||
while self.user.HasAccess is False:
|
while self.user.has_access is False:
|
||||||
# Verify access with an API call
|
# Verify access with an API call
|
||||||
self.user.hasAccess()
|
self.user.check_access()
|
||||||
|
|
||||||
if window('plex_online') != "true":
|
if window('plex_online') != "true":
|
||||||
# Server went offline
|
# Server went offline
|
||||||
|
@ -202,7 +202,7 @@ class Service():
|
||||||
# Wait until Plex server is online
|
# Wait until Plex server is online
|
||||||
# or Kodi is shut down.
|
# or Kodi is shut down.
|
||||||
while not self.__stop_PKC():
|
while not self.__stop_PKC():
|
||||||
server = self.user.getServer()
|
server = self.user.get_server()
|
||||||
if server is False:
|
if server is False:
|
||||||
# No server info set in add-on settings
|
# No server info set in add-on settings
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in a new issue