Merge branch 'beta-version'

This commit is contained in:
croneter 2018-06-24 10:52:03 +02:00
commit b646f89970
6 changed files with 96 additions and 73 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-2.1.1-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.2.2-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.1.2-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.2.3-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)
@ -53,7 +53,7 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th
### PKC Features
- Support for Kodi 18 Leia Alpha 1 (nightly versions are NOT supported!)
- Support for Kodi 18 Leia Alpha 2 (nightly versions are NOT supported!)
- Support for Kodi 17 Krypton
- [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa)
- [Cinema Trailers & Extras](https://support.plex.tv/articles/202934883-cinema-trailers-extras/)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.2.2" provider-name="croneter">
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.2.3" provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.9.1" />
@ -67,7 +67,11 @@
<summary lang="ru_RU">Нативная интеграция сервера Plex в Kodi</summary>
<description lang="ru_RU">Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск</description>
<disclaimer lang="ru_RU">Используйте на свой страх и риск</disclaimer>
<news>version 2.2.2 (beta only):
<news>version 2.2.3 (beta only):
- Compatibility with Kodi Krypton Alpha 2
- Append tv show and SxxExx to episode playlist entries
version 2.2.2 (beta only):
- Fixes to locking mechanisms which resulted in weird behavior in some cases
- Switch to Python __future__ unicode_literals and absolute paths
- Fix UnboundLocalError for playlists

View file

@ -1,3 +1,7 @@
version 2.2.3 (beta only):
- Compatibility with Kodi Krypton Alpha 2
- Append tv show and SxxExx to episode playlist entries
version 2.2.2 (beta only):
- Fixes to locking mechanisms which resulted in weird behavior in some cases
- Switch to Python __future__ unicode_literals and absolute paths

View file

@ -22,11 +22,6 @@ from . import variables as v
###############################################################################
LOG = getLogger('PLEX.entrypoint')
try:
HANDLE = int(argv[1])
ARGV_0 = path_ops.decode_path(argv[0])
except IndexError:
pass
###############################################################################
@ -119,7 +114,7 @@ def directory_item(label, path, folder=True):
{"fanart": "special://home/addons/plugin.video.plexkodiconnect/fanart.jpg"})
listitem.setArt(
{"landscape":"special://home/addons/plugin.video.plexkodiconnect/fanart.jpg"})
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=path,
listitem=listitem,
isFolder=folder)
@ -130,7 +125,7 @@ def show_main_menu(content_type=None):
Shows the main PKC menu listing with all libraries, Channel, settings, etc.
"""
LOG.debug('Do main listing with content_type: %s', content_type)
xbmcplugin.setContent(HANDLE, 'files')
xbmcplugin.setContent(int(argv[1]), 'files')
# Get emby nodes from the window props
plexprops = utils.window('Plex.nodes.total')
if plexprops:
@ -169,7 +164,7 @@ def show_main_menu(content_type=None):
"plugin://%s?mode=refreshplaylist" % v.ADDON_ID)
directory_item(utils.lang(39204),
"plugin://%s?mode=manualsync" % v.ADDON_ID)
xbmcplugin.endOfDirectory(HANDLE)
xbmcplugin.endOfDirectory(int(argv[1]))
def switch_plex_user():
@ -268,7 +263,7 @@ def next_up_episodes(tagname, limit):
count = 0
# if the addon is called with nextup parameter,
# we return the nextepisodes list of the given tagname
xbmcplugin.setContent(HANDLE, 'episodes')
xbmcplugin.setContent(int(argv[1]), 'episodes')
# First we get a list of all the TV shows - filtered by tag
params = {
'sort': {'order': "descending", 'method': "lastplayed"},
@ -317,13 +312,13 @@ def next_up_episodes(tagname, limit):
'limits': {"end": 1}
}
for episode in js.get_episodes(params):
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=episode['file'],
listitem=create_listitem(episode))
count += 1
if count == limit:
break
xbmcplugin.endOfDirectory(handle=HANDLE)
xbmcplugin.endOfDirectory(handle=int(argv[1]))
def in_progress_episodes(tagname, limit):
@ -333,7 +328,7 @@ def in_progress_episodes(tagname, limit):
count = 0
# if the addon is called with inprogressepisodes parameter,
# we return the inprogressepisodes list of the given tagname
xbmcplugin.setContent(HANDLE, 'episodes')
xbmcplugin.setContent(int(argv[1]), 'episodes')
# First we get a list of all the in-progress TV shows - filtered by tag
params = {
'sort': {'order': "descending", 'method': "lastplayed"},
@ -359,13 +354,13 @@ def in_progress_episodes(tagname, limit):
"lastplayed"]
}
for episode in js.get_episodes(params):
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=episode['file'],
listitem=create_listitem(episode))
count += 1
if count == limit:
break
xbmcplugin.endOfDirectory(handle=HANDLE)
xbmcplugin.endOfDirectory(handle=int(argv[1]))
def recent_episodes(mediatype, tagname, limit):
@ -375,7 +370,7 @@ def recent_episodes(mediatype, tagname, limit):
count = 0
# if the addon is called with recentepisodes parameter,
# we return the recentepisodes list of the given tagname
xbmcplugin.setContent(HANDLE, 'episodes')
xbmcplugin.setContent(int(argv[1]), 'episodes')
append_show_title = utils.settings('RecentTvAppendShow') == 'true'
append_sxxexx = utils.settings('RecentTvAppendSeason') == 'true'
# First we get a list of all the TV shows - filtered by tag
@ -405,13 +400,13 @@ def recent_episodes(mediatype, tagname, limit):
listitem = create_listitem(episode,
append_show_title=append_show_title,
append_sxxexx=append_sxxexx)
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=episode['file'],
listitem=listitem)
count += 1
if count == limit:
break
xbmcplugin.endOfDirectory(handle=HANDLE)
xbmcplugin.endOfDirectory(handle=int(argv[1]))
def get_video_files(plex_id, params):
@ -435,14 +430,14 @@ def get_video_files(plex_id, params):
if plex_id is None:
LOG.info('No Plex ID found, abort getting Extras')
return xbmcplugin.endOfDirectory(HANDLE)
return xbmcplugin.endOfDirectory(int(argv[1]))
item = PF.GetPlexMetadata(plex_id)
try:
path = utils.try_decode(item[0][0][0].attrib['file'])
except (TypeError, IndexError, AttributeError, KeyError):
LOG.error('Could not get file path for item %s', plex_id)
return xbmcplugin.endOfDirectory(HANDLE)
return xbmcplugin.endOfDirectory(int(argv[1]))
# Assign network protocol
if path.startswith('\\\\'):
path = path.replace('\\\\', 'smb://')
@ -458,20 +453,20 @@ def get_video_files(plex_id, params):
item_path = utils.try_encode(path_ops.path.join(root,
directory))
listitem = ListItem(item_path, path=item_path)
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=item_path,
listitem=listitem,
isFolder=True)
for file in files:
item_path = utils.try_encode(path_ops.path.join(root, file))
listitem = ListItem(item_path, path=item_path)
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=file,
listitem=listitem)
break
else:
LOG.error('Kodi cannot access folder %s', path)
xbmcplugin.endOfDirectory(HANDLE)
xbmcplugin.endOfDirectory(int(argv[1]))
@utils.catch_exceptions(warnuser=False)
@ -487,7 +482,7 @@ def extra_fanart(plex_id, plex_path):
plex_id = plex_path.split("/")[-2]
if not plex_id:
LOG.error('Could not get a plex_id, aborting')
return xbmcplugin.endOfDirectory(HANDLE)
return xbmcplugin.endOfDirectory(int(argv[1]))
# We need to store the images locally for this to work
# because of the caching system in xbmc
@ -499,7 +494,7 @@ def extra_fanart(plex_id, plex_path):
xml = PF.GetPlexMetadata(plex_id)
if xml is None:
LOG.error('Could not download metadata for %s', plex_id)
return xbmcplugin.endOfDirectory(HANDLE)
return xbmcplugin.endOfDirectory(int(argv[1]))
api = API(xml[0])
backdrops = api.artwork()['Backdrop']
@ -509,7 +504,7 @@ def extra_fanart(plex_id, plex_path):
fanart_dir, "fanart%.3d.jpg" % count))
listitem = ListItem("%.3d" % count, path=art_file)
xbmcplugin.addDirectoryItem(
handle=HANDLE,
handle=int(argv[1]),
url=art_file,
listitem=listitem)
path_ops.copyfile(backdrop, utils.try_decode(art_file))
@ -523,10 +518,10 @@ def extra_fanart(plex_id, plex_path):
file = utils.decode_path(file)
art_file = utils.try_encode(path_ops.path.join(root, file))
listitem = ListItem(file, path=art_file)
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=art_file,
listitem=listitem)
xbmcplugin.endOfDirectory(HANDLE)
xbmcplugin.endOfDirectory(int(argv[1]))
def on_deck_episodes(viewid, tagname, limit):
@ -538,7 +533,7 @@ def on_deck_episodes(viewid, tagname, limit):
tagname: Name of the Plex library, e.g. "My Movies"
limit: Max. number of items to retrieve, e.g. 50
"""
xbmcplugin.setContent(HANDLE, 'episodes')
xbmcplugin.setContent(int(argv[1]), 'episodes')
append_show_title = utils.settings('OnDeckTvAppendShow') == 'true'
append_sxxexx = utils.settings('OnDeckTvAppendSeason') == 'true'
if utils.settings('OnDeckTVextended') == 'false':
@ -550,13 +545,13 @@ def on_deck_episodes(viewid, tagname, limit):
if counter == 300:
LOG.error('Aborting On Deck view, we were not authenticated '
'for the PMS')
xbmcplugin.endOfDirectory(HANDLE, False)
xbmcplugin.endOfDirectory(int(argv[1]), False)
return
sleep(100)
xml = DU().downloadUrl('{server}/library/sections/%s/onDeck' % viewid)
if xml in (None, 401):
LOG.error('Could not download PMS xml for view %s', viewid)
xbmcplugin.endOfDirectory(HANDLE, False)
xbmcplugin.endOfDirectory(int(argv[1]), False)
return
direct_paths = utils.settings('useDirectPaths') == '1'
counter = 0
@ -569,14 +564,14 @@ def on_deck_episodes(viewid, tagname, limit):
listitem.setProperty('resumetime', str(api.resume_point()))
path = api.path(force_first_media=False, direct_paths=direct_paths)
xbmcplugin.addDirectoryItem(
handle=HANDLE,
handle=int(argv[1]),
url=path,
listitem=listitem)
counter += 1
if counter == limit:
break
xbmcplugin.endOfDirectory(
handle=HANDLE,
handle=int(argv[1]),
cacheToDisc=utils.settings('enableTextureCache') == 'true')
return
@ -594,7 +589,7 @@ def on_deck_episodes(viewid, tagname, limit):
items = js.get_tv_shows(params)
if not items:
# Now items retrieved - empty directory
xbmcplugin.endOfDirectory(handle=HANDLE)
xbmcplugin.endOfDirectory(handle=int(argv[1]))
return
params = {
@ -646,14 +641,14 @@ def on_deck_episodes(viewid, tagname, limit):
listitem = create_listitem(episode,
append_show_title=append_show_title,
append_sxxexx=append_sxxexx)
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=episode['file'],
listitem=listitem,
isFolder=False)
count += 1
if count >= limit:
break
xbmcplugin.endOfDirectory(handle=HANDLE)
xbmcplugin.endOfDirectory(handle=int(argv[1]))
def watchlater():
@ -662,24 +657,25 @@ def watchlater():
"""
if utils.window('plex_token') == '':
LOG.error('No watch later - not signed in to plex.tv')
return xbmcplugin.endOfDirectory(HANDLE, False)
return xbmcplugin.endOfDirectory(int(argv[1]), False)
if utils.window('plex_restricteduser') == 'true':
LOG.error('No watch later - restricted user')
return xbmcplugin.endOfDirectory(HANDLE, False)
return xbmcplugin.endOfDirectory(int(argv[1]), False)
xml = DU().downloadUrl('https://plex.tv/pms/playlists/queue/all',
authenticate=False,
headerOptions={'X-Plex-Token': utils.window('plex_token')})
if xml in (None, 401):
LOG.error('Could not download watch later list from plex.tv')
return xbmcplugin.endOfDirectory(HANDLE, False)
return xbmcplugin.endOfDirectory(int(argv[1]), False)
LOG.info('Displaying watch later plex.tv items')
xbmcplugin.setContent(HANDLE, 'movies')
xbmcplugin.setContent(int(argv[1]), 'movies')
direct_paths = utils.settings('useDirectPaths') == '1'
for item in xml:
__build_item(item, direct_paths)
xbmcplugin.endOfDirectory(
handle=HANDLE,
handle=int(argv[1]),
cacheToDisc=utils.settings('enableTextureCache') == 'true')
@ -692,16 +688,16 @@ def channels():
xml[0].attrib
except (ValueError, AttributeError, IndexError, TypeError):
LOG.error('Could not download Plex Channels')
return xbmcplugin.endOfDirectory(HANDLE, False)
return xbmcplugin.endOfDirectory(int(argv[1]), False)
LOG.info('Displaying Plex Channels')
xbmcplugin.setContent(HANDLE, 'files')
xbmcplugin.setContent(int(argv[1]), 'files')
for method in v.SORT_METHODS_DIRECTORY:
xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method))
xbmcplugin.addSortMethod(int(argv[1]), getattr(xbmcplugin, method))
for item in xml:
__build_folder(item)
xbmcplugin.endOfDirectory(
handle=HANDLE,
handle=int(argv[1]),
cacheToDisc=utils.settings('enableTextureCache') == 'true')
@ -718,7 +714,7 @@ def browse_plex(key=None, plex_section_id=None):
xml[0].attrib
except (ValueError, AttributeError, IndexError, TypeError):
LOG.error('Could not browse to %s', key)
return xbmcplugin.endOfDirectory(HANDLE, False)
return xbmcplugin.endOfDirectory(int(argv[1]), False)
photos = False
movies = False
@ -757,45 +753,45 @@ def browse_plex(key=None, plex_section_id=None):
# Set the correct content type
if movies is True:
xbmcplugin.setContent(HANDLE, 'movies')
xbmcplugin.setContent(int(argv[1]), 'movies')
sort_methods = v.SORT_METHODS_MOVIES
elif clips is True:
xbmcplugin.setContent(HANDLE, 'movies')
xbmcplugin.setContent(int(argv[1]), 'movies')
sort_methods = v.SORT_METHODS_CLIPS
elif photos is True:
xbmcplugin.setContent(HANDLE, 'images')
xbmcplugin.setContent(int(argv[1]), 'images')
sort_methods = v.SORT_METHODS_PHOTOS
elif tvshows is True:
xbmcplugin.setContent(HANDLE, 'tvshows')
xbmcplugin.setContent(int(argv[1]), 'tvshows')
sort_methods = v.SORT_METHOD_TVSHOWS
elif episodes is True:
xbmcplugin.setContent(HANDLE, 'episodes')
xbmcplugin.setContent(int(argv[1]), 'episodes')
sort_methods = v.SORT_METHODS_EPISODES
elif songs is True:
xbmcplugin.setContent(HANDLE, 'songs')
xbmcplugin.setContent(int(argv[1]), 'songs')
sort_methods = v.SORT_METHODS_SONGS
elif artists is True:
xbmcplugin.setContent(HANDLE, 'artists')
xbmcplugin.setContent(int(argv[1]), 'artists')
sort_methods = v.SORT_METHODS_ARTISTS
elif albums is True:
xbmcplugin.setContent(HANDLE, 'albums')
xbmcplugin.setContent(int(argv[1]), 'albums')
sort_methods = v.SORT_METHODS_ALBUMS
elif musicvideos is True:
xbmcplugin.setContent(HANDLE, 'musicvideos')
xbmcplugin.setContent(int(argv[1]), 'musicvideos')
sort_methods = v.SORT_METHODS_MOVIES
else:
xbmcplugin.setContent(HANDLE, 'files')
xbmcplugin.setContent(int(argv[1]), 'files')
sort_methods = v.SORT_METHODS_DIRECTORY
for method in sort_methods:
xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method))
xbmcplugin.addSortMethod(int(argv[1]), getattr(xbmcplugin, method))
# Set the Kodi title for this view
title = xml.attrib.get('librarySectionTitle', xml.attrib.get('title1'))
xbmcplugin.setPluginCategory(HANDLE, title)
xbmcplugin.setPluginCategory(int(argv[1]), title)
xbmcplugin.endOfDirectory(
handle=HANDLE,
handle=int(argv[1]),
cacheToDisc=utils.settings('enableTextureCache') == 'true')
@ -812,7 +808,7 @@ def __build_folder(xml_element, plex_section_id=None):
listitem = ListItem(xml_element.attrib.get('title'))
listitem.setArt({'thumb': xml_element.attrib.get('thumb'),
'poster': xml_element.attrib.get('art')})
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url="%s?%s" % (url, urlencode(params)),
isFolder=True,
listitem=listitem)
@ -838,7 +834,7 @@ def __build_item(xml_element, direct_paths):
url = api.path(direct_paths=direct_paths)
if api.resume_point():
listitem.setProperty('resumetime', str(api.resume_point()))
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=url,
listitem=listitem)
@ -847,20 +843,20 @@ def extras(plex_id):
"""
Lists all extras for plex_id
"""
xbmcplugin.setContent(HANDLE, 'movies')
xbmcplugin.setContent(int(argv[1]), 'movies')
xml = PF.GetPlexMetadata(plex_id)
try:
xml[0].attrib
except (TypeError, IndexError, KeyError):
xbmcplugin.endOfDirectory(HANDLE)
xbmcplugin.endOfDirectory(int(argv[1]))
return
for item in API(xml[0]).extras():
api = API(item)
listitem = api.create_listitem()
xbmcplugin.addDirectoryItem(handle=HANDLE,
xbmcplugin.addDirectoryItem(handle=int(argv[1]),
url=api.path(),
listitem=listitem)
xbmcplugin.endOfDirectory(HANDLE)
xbmcplugin.endOfDirectory(int(argv[1]))
def create_new_pms():

View file

@ -206,6 +206,25 @@ def _write_playlist_to_file(playlist, xml):
text = '#EXTCPlayListM3U::M3U\n'
for element in xml:
api = API(element)
append_season_episode = False
if api.plex_type() == v.PLEX_TYPE_EPISODE:
_, show, season_id, episode_id = api.episode_data()
try:
season_id = int(season_id)
episode_id = int(episode_id)
except ValueError:
pass
else:
append_season_episode = True
if append_season_episode:
text += ('#EXTINF:%s,%s S%.2dE%.2d - %s\n%s\n'
% (api.runtime(), show, season_id, episode_id,
api.title(), api.path()))
else:
# Only append the TV show name
text += ('#EXTINF:%s,%s - %s\n%s\n'
% (api.runtime(), show, api.title(), api.path()))
else:
text += ('#EXTINF:%s,%s\n%s\n'
% (api.runtime(), api.title(), api.path()))
text += '\n'

View file

@ -85,14 +85,14 @@ MIN_DB_VERSION = '2.0.27'
# Database paths
_DB_VIDEO_VERSION = {
17: 107, # Krypton
18: 109 # Leia
18: 110 # Leia
}
DB_VIDEO_PATH = try_decode(xbmc.translatePath(
"special://database/MyVideos%s.db" % _DB_VIDEO_VERSION[KODIVERSION]))
_DB_MUSIC_VERSION = {
17: 60, # Krypton
18: 70 # Leia
18: 72 # Leia
}
DB_MUSIC_PATH = try_decode(xbmc.translatePath(
"special://database/MyMusic%s.db" % _DB_MUSIC_VERSION[KODIVERSION]))