Merge branch 'translations' into l10n_translations
This commit is contained in:
commit
1d424cf547
11 changed files with 476 additions and 316 deletions
23
addon.xml
23
addon.xml
|
@ -1,14 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.plexkodiconnect"
|
||||
name="PlexKodiConnect"
|
||||
version="1.6.4"
|
||||
provider-name="croneter">
|
||||
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.6.5" provider-name="croneter">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.1.0"/>
|
||||
<import addon="script.module.requests" version="2.3.0" />
|
||||
</requires>
|
||||
<extension point="xbmc.python.pluginsource"
|
||||
library="default.py">
|
||||
<extension point="xbmc.python.pluginsource" library="default.py">
|
||||
<provides>video audio image</provides>
|
||||
</extension>
|
||||
<extension point="xbmc.service" library="service.py" start="login">
|
||||
|
@ -21,6 +17,13 @@
|
|||
</item>
|
||||
</extension>
|
||||
<extension point="xbmc.addon.metadata">
|
||||
<platform>all</platform>
|
||||
<language></language>
|
||||
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
|
||||
<forum>https://forums.plex.tv</forum>
|
||||
<website>https://github.com/croneter/PlexKodiConnect</website>
|
||||
<email></email>
|
||||
<source>https://github.com/croneter/PlexKodiConnect</source>
|
||||
<summary lang="en">Native Integration of Plex into Kodi</summary>
|
||||
<summary lang="en_gb">Native Integration of Plex into Kodi</summary>
|
||||
<summary lang="en_us">Native Integration of Plex into Kodi</summary>
|
||||
|
@ -37,11 +40,5 @@
|
|||
<description lang="es">Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk!</description>
|
||||
<description lang="dk">Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på eget ansvar!</description>
|
||||
<description lang="nl">Verbind Kodi met je Plex Media Server. Deze plugin gaat ervan uit dat je al je video's met Plex (en niet met Kodi) beheerd. Je kunt gegevens reeds opgeslagen in de databases voor video en muziek van Kodi (deze plugin wijzigt deze gegevens direct) verliezen. Gebruik op eigen risico!</description>
|
||||
<platform>all</platform>
|
||||
<license>GPL v2.0</license>
|
||||
<forum>https://forums.plex.tv</forum>
|
||||
<website>https://github.com/croneter/PlexKodiConnect</website>
|
||||
<email></email>
|
||||
<source>https://github.com/croneter/PlexKodiConnect</source>
|
||||
</extension>
|
||||
</addon>
|
||||
</addon>
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
version 1.6.5 (beta only)
|
||||
- Plex Channels!
|
||||
- Browse video nodes by folder/path
|
||||
- Update Danish translation
|
||||
- Code optimization
|
||||
|
||||
version 1.6.4 (beta only)
|
||||
- Amazon Alexa support! Be mindful to check the Alexa forum thread first; there are still many issues completely unrelated to PKC
|
||||
- Enable skipping for Plex Companion
|
||||
|
|
263
default.py
263
default.py
|
@ -36,13 +36,14 @@ from utils import window, pickl_window, reset, passwordsXML, language as lang,\
|
|||
dialog
|
||||
from pickler import unpickle_me
|
||||
from PKC_listitem import convert_PKC_to_listitem
|
||||
import variables as v
|
||||
|
||||
###############################################################################
|
||||
|
||||
import loghandler
|
||||
|
||||
loghandler.config()
|
||||
log = logging.getLogger("PLEX.default")
|
||||
log = logging.getLogger('PLEX.default')
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
@ -50,144 +51,160 @@ HANDLE = int(argv[1])
|
|||
|
||||
|
||||
class Main():
|
||||
|
||||
# MAIN ENTRY POINT
|
||||
# @utils.profiling()
|
||||
def __init__(self):
|
||||
log.debug("Full sys.argv received: %s" % argv)
|
||||
log.debug('Full sys.argv received: %s' % argv)
|
||||
# Parse parameters
|
||||
params = dict(parse_qsl(argv[2][1:]))
|
||||
try:
|
||||
mode = params['mode']
|
||||
itemid = params.get('id', '')
|
||||
except:
|
||||
mode = ""
|
||||
itemid = ''
|
||||
mode = params.get('mode', '')
|
||||
itemid = params.get('id', '')
|
||||
|
||||
if mode == 'play':
|
||||
# Put the request into the "queue"
|
||||
while window('plex_play_new_item'):
|
||||
sleep(50)
|
||||
window('plex_play_new_item',
|
||||
value='%s%s' % (mode, argv[2]))
|
||||
# Wait for the result
|
||||
while not pickl_window('plex_result'):
|
||||
sleep(50)
|
||||
result = unpickle_me()
|
||||
if result is None:
|
||||
log.error('Error encountered, aborting')
|
||||
dialog('notification',
|
||||
heading='{plex}',
|
||||
message=lang(30128),
|
||||
icon='{error}',
|
||||
time=3000)
|
||||
setResolvedUrl(HANDLE, False, ListItem())
|
||||
elif result.listitem:
|
||||
listitem = convert_PKC_to_listitem(result.listitem)
|
||||
setResolvedUrl(HANDLE, True, listitem)
|
||||
return
|
||||
self.play()
|
||||
|
||||
modes = {
|
||||
'reset': reset,
|
||||
'resetauth': entrypoint.resetAuth,
|
||||
'passwords': passwordsXML,
|
||||
'getsubfolders': entrypoint.GetSubFolders,
|
||||
'nextup': entrypoint.getNextUpEpisodes,
|
||||
'inprogressepisodes': entrypoint.getInProgressEpisodes,
|
||||
'recentepisodes': entrypoint.getRecentEpisodes,
|
||||
'refreshplaylist': entrypoint.refreshPlaylist,
|
||||
'switchuser': entrypoint.switchPlexUser,
|
||||
'deviceid': entrypoint.resetDeviceId,
|
||||
'browseplex': entrypoint.BrowsePlexContent,
|
||||
'ondeck': entrypoint.getOnDeck,
|
||||
'chooseServer': entrypoint.chooseServer,
|
||||
'watchlater': entrypoint.watchlater,
|
||||
'enterPMS': entrypoint.enterPMS,
|
||||
'togglePlexTV': entrypoint.togglePlexTV,
|
||||
'Plex_Node': entrypoint.Plex_Node
|
||||
}
|
||||
elif mode == 'ondeck':
|
||||
entrypoint.getOnDeck(itemid,
|
||||
params.get('type'),
|
||||
params.get('tagname'),
|
||||
int(params.get('limit')))
|
||||
|
||||
if "/extrafanart" in argv[0]:
|
||||
plexpath = argv[2][1:]
|
||||
plexid = params.get('id')
|
||||
entrypoint.getExtraFanArt(plexid, plexpath)
|
||||
entrypoint.getVideoFiles(plexid, plexpath)
|
||||
return
|
||||
elif mode == 'recentepisodes':
|
||||
entrypoint.getRecentEpisodes(itemid,
|
||||
params.get('type'),
|
||||
params.get('tagname'),
|
||||
int(params.get('limit')))
|
||||
|
||||
if mode == 'fanart':
|
||||
elif mode == 'nextup':
|
||||
entrypoint.getNextUpEpisodes(params['tagname'],
|
||||
int(params['limit']))
|
||||
|
||||
elif mode == 'inprogressepisodes':
|
||||
entrypoint.getInProgressEpisodes(params['tagname'],
|
||||
int(params['limit']))
|
||||
|
||||
elif mode == 'Plex_Node':
|
||||
entrypoint.Plex_Node(itemid, params.get('viewOffset'))
|
||||
|
||||
elif mode == 'browseplex':
|
||||
entrypoint.browse_plex(key=params.get('key'),
|
||||
plex_section_id=params.get('id'))
|
||||
|
||||
elif mode == 'getsubfolders':
|
||||
entrypoint.GetSubFolders(itemid)
|
||||
|
||||
elif mode == 'watchlater':
|
||||
entrypoint.watchlater()
|
||||
|
||||
elif mode == 'channels':
|
||||
entrypoint.channels()
|
||||
|
||||
elif mode == 'settings':
|
||||
executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID)
|
||||
|
||||
elif mode == 'enterPMS':
|
||||
entrypoint.enterPMS()
|
||||
|
||||
elif mode == 'reset':
|
||||
reset()
|
||||
|
||||
elif mode == 'togglePlexTV':
|
||||
entrypoint.togglePlexTV()
|
||||
|
||||
elif mode == 'resetauth':
|
||||
entrypoint.resetAuth()
|
||||
|
||||
elif mode == 'passwords':
|
||||
passwordsXML()
|
||||
|
||||
elif mode == 'switchuser':
|
||||
entrypoint.switchPlexUser()
|
||||
|
||||
elif mode in ('manualsync', 'repair'):
|
||||
if window('plex_online') != 'true':
|
||||
# Server is not online, do not run the sync
|
||||
dialog('ok',
|
||||
heading=lang(29999),
|
||||
message=lang(39205))
|
||||
log.error('Not connected to a PMS.')
|
||||
else:
|
||||
if mode == 'repair':
|
||||
window('plex_runLibScan', value='repair')
|
||||
log.info('Requesting repair lib sync')
|
||||
elif mode == 'manualsync':
|
||||
log.info('Requesting full library scan')
|
||||
window('plex_runLibScan', value='full')
|
||||
|
||||
elif mode == 'texturecache':
|
||||
window('plex_runLibScan', value='del_textures')
|
||||
|
||||
elif mode == 'chooseServer':
|
||||
entrypoint.chooseServer()
|
||||
|
||||
elif mode == 'refreshplaylist':
|
||||
log.info('Requesting playlist/nodes refresh')
|
||||
window('plex_runLibScan', value='views')
|
||||
|
||||
elif mode == 'deviceid':
|
||||
self.deviceid()
|
||||
|
||||
elif mode == 'fanart':
|
||||
log.info('User requested fanarttv refresh')
|
||||
window('plex_runLibScan', value='fanart')
|
||||
|
||||
elif '/extrafanart' in argv[0]:
|
||||
plexpath = argv[2][1:]
|
||||
plexid = itemid
|
||||
entrypoint.getExtraFanArt(plexid, plexpath)
|
||||
entrypoint.getVideoFiles(plexid, plexpath)
|
||||
|
||||
# Called by e.g. 3rd party plugin video extras
|
||||
if ("/Extras" in argv[0] or "/VideoFiles" in argv[0] or
|
||||
"/Extras" in argv[2]):
|
||||
plexId = params.get('id', None)
|
||||
elif ('/Extras' in argv[0] or '/VideoFiles' in argv[0] or
|
||||
'/Extras' in argv[2]):
|
||||
plexId = itemid or None
|
||||
entrypoint.getVideoFiles(plexId, params)
|
||||
|
||||
if modes.get(mode):
|
||||
# Simple functions
|
||||
if mode == "play":
|
||||
dbid = params.get('dbid')
|
||||
# modes[mode](itemid, dbid)
|
||||
modes[mode](itemid, dbid)
|
||||
|
||||
elif mode in ("nextup", "inprogressepisodes"):
|
||||
limit = int(params['limit'])
|
||||
modes[mode](params['tagname'], limit)
|
||||
|
||||
elif mode in ("channels","getsubfolders"):
|
||||
modes[mode](itemid)
|
||||
|
||||
elif mode == "browsecontent":
|
||||
modes[mode](itemid, params.get('type'), params.get('folderid'))
|
||||
|
||||
elif mode == 'browseplex':
|
||||
modes[mode](
|
||||
itemid,
|
||||
params.get('type'),
|
||||
params.get('folderid'))
|
||||
|
||||
elif mode in ('ondeck', 'recentepisodes'):
|
||||
modes[mode](
|
||||
itemid,
|
||||
params.get('type'),
|
||||
params.get('tagname'),
|
||||
int(params.get('limit')))
|
||||
|
||||
elif mode == "channelsfolder":
|
||||
folderid = params['folderid']
|
||||
modes[mode](itemid, folderid)
|
||||
elif mode == "companion":
|
||||
modes[mode](itemid, params=argv[2])
|
||||
elif mode == 'Plex_Node':
|
||||
modes[mode](params.get('id'),
|
||||
params.get('viewOffset'))
|
||||
else:
|
||||
modes[mode]()
|
||||
else:
|
||||
# Other functions
|
||||
if mode == "settings":
|
||||
executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
|
||||
elif mode in ("manualsync", "repair"):
|
||||
if window('plex_online') != "true":
|
||||
# Server is not online, do not run the sync
|
||||
dialog('ok',
|
||||
heading=lang(29999),
|
||||
message=lang(39205))
|
||||
log.error("Not connected to a PMS.")
|
||||
else:
|
||||
if mode == 'repair':
|
||||
window('plex_runLibScan', value="repair")
|
||||
log.info("Requesting repair lib sync")
|
||||
elif mode == 'manualsync':
|
||||
log.info("Requesting full library scan")
|
||||
window('plex_runLibScan', value="full")
|
||||
elif mode == "texturecache":
|
||||
window('plex_runLibScan', value='del_textures')
|
||||
else:
|
||||
entrypoint.doMainListing()
|
||||
entrypoint.doMainListing()
|
||||
|
||||
if __name__ == "__main__":
|
||||
log.info('plugin.video.plexkodiconnect started')
|
||||
def play(self):
|
||||
# Put the request into the 'queue'
|
||||
while window('plex_play_new_item'):
|
||||
sleep(50)
|
||||
window('plex_play_new_item',
|
||||
value='%s%s' % ('play', argv[2]))
|
||||
# Wait for the result
|
||||
while not pickl_window('plex_result'):
|
||||
sleep(50)
|
||||
result = unpickle_me()
|
||||
if result is None:
|
||||
log.error('Error encountered, aborting')
|
||||
dialog('notification',
|
||||
heading='{plex}',
|
||||
message=lang(30128),
|
||||
icon='{error}',
|
||||
time=3000)
|
||||
setResolvedUrl(HANDLE, False, ListItem())
|
||||
elif result.listitem:
|
||||
listitem = convert_PKC_to_listitem(result.listitem)
|
||||
setResolvedUrl(HANDLE, True, listitem)
|
||||
|
||||
def deviceid(self):
|
||||
deviceId_old = window('plex_client_Id')
|
||||
from clientinfo import getDeviceId
|
||||
try:
|
||||
deviceId = getDeviceId(reset=True)
|
||||
except Exception as e:
|
||||
log.error('Failed to generate a new device Id: %s' % e)
|
||||
dialog('ok', lang(29999), lang(33032))
|
||||
else:
|
||||
log.info('Successfully removed old device ID: %s New deviceId:'
|
||||
'%s' % (deviceId_old, deviceId))
|
||||
# 'Kodi will now restart to apply the changes'
|
||||
dialog('ok', lang(29999), lang(33033))
|
||||
executebuiltin('RestartApp')
|
||||
|
||||
if __name__ == '__main__':
|
||||
log.info('%s started' % v.ADDON_ID)
|
||||
Main()
|
||||
log.info('plugin.video.plexkodiconnect stopped')
|
||||
log.info('%s stopped' % v.ADDON_ID)
|
||||
|
|
|
@ -515,6 +515,5 @@
|
|||
<string id="39603">Nulstil alle indstillinger for PlexKodiConnect Addon? (dette er normalt ikke anbefalet og unødvendigt!)</string>
|
||||
|
||||
<string id="39700">Amazon Alexa (Voice Recognition)</string>
|
||||
<string id="39701">Activate Alexa</string>
|
||||
<string id="39702">Browse by folder</string>
|
||||
<string id="39701">Alexa aktivieren</string>
|
||||
</strings>
|
|
@ -106,7 +106,10 @@ def Plex_Node(url, viewOffset, playdirectly=False, node=True):
|
|||
log.info('Plex_Node called with url: %s, viewOffset: %s'
|
||||
% (url, viewOffset))
|
||||
# Plex redirect, e.g. watch later. Need to get actual URLs
|
||||
xml = downloadutils.DownloadUtils().downloadUrl(url)
|
||||
if url.startswith('http'):
|
||||
xml = downloadutils.DownloadUtils().downloadUrl(url)
|
||||
else:
|
||||
xml = downloadutils.DownloadUtils().downloadUrl('{server}%s' % url)
|
||||
try:
|
||||
xml[0].attrib
|
||||
except:
|
||||
|
@ -114,8 +117,7 @@ def Plex_Node(url, viewOffset, playdirectly=False, node=True):
|
|||
return
|
||||
if viewOffset != '0':
|
||||
try:
|
||||
viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR *
|
||||
float(viewOffset))
|
||||
viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(viewOffset))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
|
@ -194,6 +196,9 @@ def doMainListing():
|
|||
# Plex Watch later
|
||||
addDirectoryItem(lang(39211),
|
||||
"plugin://plugin.video.plexkodiconnect/?mode=watchlater")
|
||||
# Plex Channels
|
||||
addDirectoryItem(lang(30173),
|
||||
"plugin://plugin.video.plexkodiconnect/?mode=channels")
|
||||
# Plex user switch
|
||||
addDirectoryItem(lang(39200) + window('plex_username'),
|
||||
"plugin://plugin.video.plexkodiconnect/"
|
||||
|
@ -211,23 +216,6 @@ def doMainListing():
|
|||
xbmcplugin.endOfDirectory(HANDLE)
|
||||
|
||||
|
||||
##### Generate a new deviceId
|
||||
def resetDeviceId():
|
||||
deviceId_old = window('plex_client_Id')
|
||||
from clientinfo import getDeviceId
|
||||
try:
|
||||
deviceId = getDeviceId(reset=True)
|
||||
except Exception as e:
|
||||
log.error("Failed to generate a new device Id: %s" % e)
|
||||
dialog('ok', lang(29999), lang(33032))
|
||||
else:
|
||||
log.info("Successfully removed old deviceId: %s New deviceId: %s"
|
||||
% (deviceId_old, deviceId))
|
||||
# "Kodi will now restart to apply the changes"
|
||||
dialog('ok', lang(29999), lang(33033))
|
||||
executebuiltin('RestartApp')
|
||||
|
||||
|
||||
def switchPlexUser():
|
||||
"""
|
||||
Signs out currently logged in user (if applicable). Triggers sign-in of a
|
||||
|
@ -249,12 +237,6 @@ def switchPlexUser():
|
|||
__LogIn()
|
||||
|
||||
|
||||
##### REFRESH EMBY PLAYLISTS #####
|
||||
def refreshPlaylist():
|
||||
log.info('Requesting playlist/nodes refresh')
|
||||
window('plex_runLibScan', value="views")
|
||||
|
||||
|
||||
#### SHOW SUBFOLDERS FOR NODE #####
|
||||
def GetSubFolders(nodeindex):
|
||||
nodetypes = ["",".recent",".recentepisodes",".inprogress",".inprogressepisodes",".unwatched",".nextepisodes",".sets",".genres",".random",".recommended"]
|
||||
|
@ -661,83 +643,6 @@ def RunLibScan(mode):
|
|||
window('plex_runLibScan', value='full')
|
||||
|
||||
|
||||
def BrowsePlexContent(viewid, mediatype="", folderid=""):
|
||||
"""
|
||||
Browse Plex Photos:
|
||||
viewid: PMS name of the library
|
||||
mediatype: mediatype, 'photos'
|
||||
nodetype: e.g. 'ondeck' (TBD!!)
|
||||
"""
|
||||
log.debug("BrowsePlexContent called with viewid: %s, mediatype: "
|
||||
"%s, folderid: %s" % (viewid, mediatype, folderid))
|
||||
|
||||
if not folderid:
|
||||
# Top-level navigation, so get the content of this section
|
||||
# Get all sections
|
||||
xml = GetPlexSectionResults(
|
||||
viewid,
|
||||
containerSize=int(settings('limitindex')))
|
||||
try:
|
||||
xml.attrib
|
||||
except AttributeError:
|
||||
log.error("Error download section %s" % viewid)
|
||||
return xbmcplugin.endOfDirectory(HANDLE, False)
|
||||
else:
|
||||
# folderid was passed so we can directly access the folder
|
||||
xml = downloadutils.DownloadUtils().downloadUrl(
|
||||
"{server}%s" % folderid)
|
||||
try:
|
||||
xml.attrib
|
||||
except AttributeError:
|
||||
log.error("Error downloading %s" % folderid)
|
||||
return xbmcplugin.endOfDirectory(HANDLE, False)
|
||||
|
||||
# Set the folder's name
|
||||
xbmcplugin.setPluginCategory(HANDLE,
|
||||
xml.attrib.get('librarySectionTitle'))
|
||||
|
||||
# set the correct params for the content type
|
||||
if mediatype == "photos":
|
||||
xbmcplugin.setContent(HANDLE, 'photos')
|
||||
|
||||
# process the listing
|
||||
for item in xml:
|
||||
api = API(item)
|
||||
if item.tag == 'Directory':
|
||||
li = ListItem(item.attrib.get('title', 'Missing title'))
|
||||
# for folders we add an additional browse request, passing the
|
||||
# folderId
|
||||
li.setProperty('IsFolder', 'true')
|
||||
li.setProperty('IsPlayable', 'false')
|
||||
path = "%s?id=%s&mode=browseplex&type=%s&folderid=%s" \
|
||||
% (ARGV_0, viewid, mediatype, api.getKey())
|
||||
api.set_listitem_artwork(li)
|
||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||
url=path,
|
||||
listitem=li,
|
||||
isFolder=True)
|
||||
else:
|
||||
li = api.CreateListItemFromPlexItem()
|
||||
api.set_listitem_artwork(li)
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=HANDLE,
|
||||
url=li.getProperty("path"),
|
||||
listitem=li)
|
||||
|
||||
xbmcplugin.addSortMethod(HANDLE,
|
||||
xbmcplugin.SORT_METHOD_VIDEO_TITLE)
|
||||
xbmcplugin.addSortMethod(HANDLE,
|
||||
xbmcplugin.SORT_METHOD_DATE)
|
||||
xbmcplugin.addSortMethod(HANDLE,
|
||||
xbmcplugin.SORT_METHOD_VIDEO_RATING)
|
||||
xbmcplugin.addSortMethod(HANDLE,
|
||||
xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)
|
||||
|
||||
xbmcplugin.endOfDirectory(
|
||||
handle=HANDLE,
|
||||
cacheToDisc=settings('enableTextureCache') == 'true')
|
||||
|
||||
|
||||
def getOnDeck(viewid, mediatype, tagname, limit):
|
||||
"""
|
||||
Retrieves Plex On Deck items, currently only for TV shows
|
||||
|
@ -905,28 +810,179 @@ def watchlater():
|
|||
|
||||
log.info('Displaying watch later plex.tv items')
|
||||
xbmcplugin.setContent(HANDLE, 'movies')
|
||||
url = "plugin://plugin.video.plexkodiconnect/"
|
||||
params = {
|
||||
'mode': "Plex_Node",
|
||||
}
|
||||
for item in xml:
|
||||
api = API(item)
|
||||
listitem = api.CreateListItemFromPlexItem()
|
||||
api.AddStreamInfo(listitem)
|
||||
api.set_listitem_artwork(listitem)
|
||||
params['id'] = item.attrib.get('key')
|
||||
params['viewOffset'] = item.attrib.get('viewOffset', '0')
|
||||
params['plex_type'] = item.attrib.get('type')
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=HANDLE,
|
||||
url="%s?%s" % (url, urlencode(params)),
|
||||
listitem=listitem)
|
||||
__build_item(item)
|
||||
|
||||
xbmcplugin.endOfDirectory(
|
||||
handle=HANDLE,
|
||||
cacheToDisc=settings('enableTextureCache') == 'true')
|
||||
|
||||
|
||||
def channels():
|
||||
"""
|
||||
Listing for Plex Channels
|
||||
"""
|
||||
if window('plex_restricteduser') == 'true':
|
||||
log.error('No Plex Channels - restricted user')
|
||||
return xbmcplugin.endOfDirectory(HANDLE, False)
|
||||
|
||||
xml = downloadutils.DownloadUtils().downloadUrl('{server}/channels/all')
|
||||
try:
|
||||
xml[0].attrib
|
||||
except (ValueError, AttributeError, IndexError):
|
||||
log.error('Could not download Plex Channels')
|
||||
return xbmcplugin.endOfDirectory(HANDLE, False)
|
||||
|
||||
log.info('Displaying Plex Channels')
|
||||
xbmcplugin.setContent(HANDLE, 'files')
|
||||
for method in v.SORT_METHODS_DIRECTORY:
|
||||
xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method))
|
||||
for item in xml:
|
||||
__build_folder(item)
|
||||
xbmcplugin.endOfDirectory(
|
||||
handle=HANDLE,
|
||||
cacheToDisc=settings('enableTextureCache') == 'true')
|
||||
|
||||
|
||||
def browse_plex(key=None, plex_section_id=None):
|
||||
"""
|
||||
Lists the content of a Plex folder, e.g. channels. Either pass in key (to
|
||||
be used directly for PMS url {server}<key>) or the plex_section_id
|
||||
"""
|
||||
if key:
|
||||
xml = downloadutils.DownloadUtils().downloadUrl('{server}%s' % key)
|
||||
else:
|
||||
xml = GetPlexSectionResults(
|
||||
plex_section_id,
|
||||
containerSize=int(settings('limitindex')))
|
||||
try:
|
||||
xml[0].attrib
|
||||
except (ValueError, AttributeError, IndexError):
|
||||
log.error('Could not browse to %s' % key)
|
||||
return xbmcplugin.endOfDirectory(HANDLE, False)
|
||||
|
||||
photos = False
|
||||
movies = False
|
||||
clips = False
|
||||
tvshows = False
|
||||
episodes = False
|
||||
songs = False
|
||||
artists = False
|
||||
albums = False
|
||||
musicvideos = False
|
||||
for item in xml:
|
||||
typus = item.attrib.get('type')
|
||||
if item.tag == 'Directory':
|
||||
__build_folder(item, plex_section_id=plex_section_id)
|
||||
else:
|
||||
__build_item(item)
|
||||
if typus == v.PLEX_TYPE_PHOTO:
|
||||
photos = True
|
||||
elif typus == v.PLEX_TYPE_MOVIE:
|
||||
movies = True
|
||||
elif typus == v.PLEX_TYPE_CLIP:
|
||||
clips = True
|
||||
elif typus in (v.PLEX_TYPE_SHOW, v.PLEX_TYPE_SEASON):
|
||||
tvshows = True
|
||||
elif typus == v.PLEX_TYPE_EPISODE:
|
||||
episodes = True
|
||||
elif typus == v.PLEX_TYPE_SONG:
|
||||
songs = True
|
||||
elif typus == v.PLEX_TYPE_ARTIST:
|
||||
artists = True
|
||||
elif typus == v.PLEX_TYPE_ALBUM:
|
||||
albums = True
|
||||
elif typus == v.PLEX_TYPE_MUSICVIDEO:
|
||||
musicvideos = True
|
||||
|
||||
# Set the correct content type
|
||||
if movies is True:
|
||||
xbmcplugin.setContent(HANDLE, 'movies')
|
||||
sort_methods = v.SORT_METHODS_MOVIES
|
||||
elif clips is True:
|
||||
xbmcplugin.setContent(HANDLE, 'movies')
|
||||
sort_methods = v.SORT_METHODS_CLIPS
|
||||
elif photos is True:
|
||||
xbmcplugin.setContent(HANDLE, 'files')
|
||||
sort_methods = v.SORT_METHODS_PHOTOS
|
||||
elif tvshows is True:
|
||||
xbmcplugin.setContent(HANDLE, 'tvshows')
|
||||
sort_methods = v.SORT_METHOD_TVSHOWS
|
||||
elif episodes is True:
|
||||
xbmcplugin.setContent(HANDLE, 'episodes')
|
||||
sort_methods = v.SORT_METHODS_EPISODES
|
||||
elif songs is True:
|
||||
xbmcplugin.setContent(HANDLE, 'songs')
|
||||
sort_methods = v.SORT_METHODS_SONGS
|
||||
elif artists is True:
|
||||
xbmcplugin.setContent(HANDLE, 'artists')
|
||||
sort_methods = v.SORT_METHODS_ARTISTS
|
||||
elif albums is True:
|
||||
xbmcplugin.setContent(HANDLE, 'albums')
|
||||
sort_methods = v.SORT_METHODS_ALBUMS
|
||||
elif musicvideos is True:
|
||||
xbmcplugin.setContent(HANDLE, 'musicvideos')
|
||||
sort_methods = v.SORT_METHODS_MOVIES
|
||||
else:
|
||||
xbmcplugin.setContent(HANDLE, 'files')
|
||||
sort_methods = v.SORT_METHODS_DIRECTORY
|
||||
|
||||
for method in sort_methods:
|
||||
xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method))
|
||||
|
||||
# Set the Kodi title for this view
|
||||
title = xml.attrib.get('librarySectionTitle', xml.attrib.get('title1'))
|
||||
xbmcplugin.setPluginCategory(HANDLE, title)
|
||||
|
||||
xbmcplugin.endOfDirectory(
|
||||
handle=HANDLE,
|
||||
cacheToDisc=settings('enableTextureCache') == 'true')
|
||||
|
||||
|
||||
def __build_folder(xml_element, plex_section_id=None):
|
||||
url = "plugin://%s/" % v.ADDON_ID
|
||||
key = xml_element.attrib.get('fastKey', xml_element.attrib.get('key'))
|
||||
if not key.startswith('/'):
|
||||
key = '/library/sections/%s/%s' % (plex_section_id, key)
|
||||
params = {
|
||||
'mode': "browseplex",
|
||||
'key': key,
|
||||
'id': plex_section_id
|
||||
}
|
||||
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,
|
||||
url="%s?%s" % (url, urlencode(params)),
|
||||
isFolder=True,
|
||||
listitem=listitem)
|
||||
|
||||
|
||||
def __build_item(xml_element):
|
||||
api = API(xml_element)
|
||||
listitem = api.CreateListItemFromPlexItem()
|
||||
api.AddStreamInfo(listitem)
|
||||
api.set_listitem_artwork(listitem)
|
||||
if api.getType() == v.PLEX_TYPE_CLIP:
|
||||
params = {
|
||||
'mode': "Plex_Node",
|
||||
'id': xml_element.attrib.get('key'),
|
||||
'viewOffset': xml_element.attrib.get('viewOffset', '0'),
|
||||
'plex_type': xml_element.attrib.get('type')
|
||||
}
|
||||
else:
|
||||
params = {
|
||||
'filename': api.getKey(),
|
||||
'id': api.getRatingKey(),
|
||||
'dbid': listitem.getProperty('dbid') or '',
|
||||
'mode': "play"
|
||||
}
|
||||
url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params))
|
||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||
url=url,
|
||||
listitem=listitem)
|
||||
|
||||
|
||||
def enterPMS():
|
||||
"""
|
||||
Opens dialogs for the user the plug in the PMS details
|
||||
|
|
|
@ -260,7 +260,6 @@ def init_Plex_playlist(playlist, plex_id=None, kodi_item=None):
|
|||
except KeyError:
|
||||
log.error('Could not init Plex playlist')
|
||||
return
|
||||
item.ID = xml[-1].attrib['%sItemID' % playlist.kind]
|
||||
playlist.items.append(item)
|
||||
log.debug('Initialized the playlist on the Plex side: %s' % playlist)
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ class Playqueue(Thread):
|
|||
else:
|
||||
# Currently, only video or audio playqueues available
|
||||
playqueue.kodi_pl = PlayList(PLAYLIST_VIDEO)
|
||||
# Overwrite 'picture' with 'photo'
|
||||
playqueue.type = v.KODI_TYPE_PHOTO
|
||||
self.playqueues.append(playqueue)
|
||||
# sort the list by their playlistid, just in case
|
||||
self.playqueues = sorted(
|
||||
|
|
|
@ -70,19 +70,6 @@ class PlayUtils():
|
|||
log.info("The playurl is: %s" % playurl)
|
||||
return playurl
|
||||
|
||||
def httpPlay(self):
|
||||
# Audio, Video, Photo
|
||||
|
||||
itemid = self.item['Id']
|
||||
mediatype = self.item['MediaType']
|
||||
|
||||
if mediatype == "Audio":
|
||||
playurl = "%s/emby/Audio/%s/stream" % (self.server, itemid)
|
||||
else:
|
||||
playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, itemid)
|
||||
|
||||
return playurl
|
||||
|
||||
def isDirectPlay(self):
|
||||
"""
|
||||
Returns the path/playurl if we can direct play, None otherwise
|
||||
|
|
|
@ -113,6 +113,7 @@ PLEX_TYPE_AUDIO = 'music'
|
|||
PLEX_TYPE_SONG = 'track'
|
||||
PLEX_TYPE_ALBUM = 'album'
|
||||
PLEX_TYPE_ARTIST = 'artist'
|
||||
PLEX_TYPE_MUSICVIDEO = 'musicvideo'
|
||||
|
||||
PLEX_TYPE_PHOTO = 'photo'
|
||||
|
||||
|
@ -131,6 +132,7 @@ KODI_TYPE_AUDIO = 'audio'
|
|||
KODI_TYPE_SONG = 'song'
|
||||
KODI_TYPE_ALBUM = 'album'
|
||||
KODI_TYPE_ARTIST = 'artist'
|
||||
KODI_TYPE_MUSICVIDEO = 'musicvideo'
|
||||
|
||||
KODI_TYPE_PHOTO = 'photo'
|
||||
|
||||
|
@ -195,7 +197,8 @@ KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE = {
|
|||
PLEX_TYPE_ARTIST: KODI_TYPE_AUDIO,
|
||||
PLEX_TYPE_ALBUM: KODI_TYPE_AUDIO,
|
||||
PLEX_TYPE_SONG: KODI_TYPE_AUDIO,
|
||||
PLEX_TYPE_AUDIO: KODI_TYPE_AUDIO
|
||||
PLEX_TYPE_AUDIO: KODI_TYPE_AUDIO,
|
||||
PLEX_TYPE_PHOTO: KODI_TYPE_PHOTO
|
||||
}
|
||||
|
||||
|
||||
|
@ -266,3 +269,88 @@ ALEXA_TO_COMPANION = {
|
|||
'queryContainerKey': 'containerKey',
|
||||
'queryToken': 'token',
|
||||
}
|
||||
|
||||
# Kodi sort methods for xbmcplugin.addSortMethod()
|
||||
SORT_METHODS_DIRECTORY = (
|
||||
'SORT_METHOD_UNSORTED', # sorted as returned from Plex
|
||||
'SORT_METHOD_LABEL',
|
||||
)
|
||||
|
||||
SORT_METHODS_PHOTOS = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_LABEL',
|
||||
'SORT_METHOD_DATE',
|
||||
'SORT_METHOD_DATEADDED',
|
||||
)
|
||||
|
||||
SORT_METHODS_CLIPS = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_DURATION',
|
||||
)
|
||||
|
||||
SORT_METHODS_MOVIES = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_DURATION',
|
||||
'SORT_METHOD_VIDEO_RATING',
|
||||
'SORT_METHOD_VIDEO_USER_RATING',
|
||||
'SORT_METHOD_MPAA_RATING',
|
||||
'SORT_METHOD_COUNTRY',
|
||||
'SORT_METHOD_STUDIO',
|
||||
'SORT_METHOD_GENRE',
|
||||
)
|
||||
|
||||
SORT_METHOD_TVSHOWS = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_VIDEO_RATING',
|
||||
'SORT_METHOD_VIDEO_USER_RATING',
|
||||
'SORT_METHOD_MPAA_RATING',
|
||||
'SORT_METHOD_COUNTRY',
|
||||
'SORT_METHOD_GENRE',
|
||||
)
|
||||
|
||||
SORT_METHODS_EPISODES = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_EPISODE',
|
||||
'SORT_METHOD_DURATION',
|
||||
'SORT_METHOD_VIDEO_RATING',
|
||||
'SORT_METHOD_VIDEO_USER_RATING',
|
||||
'SORT_METHOD_MPAA_RATING',
|
||||
'SORT_METHOD_FILE',
|
||||
'SORT_METHOD_FULLPATH',
|
||||
)
|
||||
|
||||
SORT_METHODS_SONGS = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_TRACKNUM',
|
||||
'SORT_METHOD_DURATION',
|
||||
'SORT_METHOD_ARTIST',
|
||||
'SORT_METHOD_ARTIST_AND_YEAR',
|
||||
'SORT_METHOD_ALBUM',
|
||||
'SORT_METHOD_SONG_RATING',
|
||||
'SORT_METHOD_SONG_USER_RATING'
|
||||
)
|
||||
|
||||
SORT_METHODS_ARTISTS = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_TRACKNUM',
|
||||
'SORT_METHOD_DURATION',
|
||||
'SORT_METHOD_ARTIST',
|
||||
'SORT_METHOD_ARTIST_AND_YEAR',
|
||||
'SORT_METHOD_ALBUM',
|
||||
)
|
||||
|
||||
SORT_METHODS_ALBUMS = (
|
||||
'SORT_METHOD_UNSORTED',
|
||||
'SORT_METHOD_TITLE',
|
||||
'SORT_METHOD_TRACKNUM',
|
||||
'SORT_METHOD_DURATION',
|
||||
'SORT_METHOD_ARTIST',
|
||||
'SORT_METHOD_ARTIST_AND_YEAR',
|
||||
'SORT_METHOD_ALBUM',
|
||||
)
|
||||
|
|
|
@ -102,7 +102,7 @@ class VideoNodes(object):
|
|||
return
|
||||
|
||||
if mediatype == "photos":
|
||||
path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=getsubfolders" % indexnumber
|
||||
path = "plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s&id=%s" % (viewid, viewid)
|
||||
|
||||
window('Plex.nodes.%s.index' % indexnumber, value=path)
|
||||
|
||||
|
@ -131,58 +131,64 @@ class VideoNodes(object):
|
|||
'9': "genres",
|
||||
'10': "random",
|
||||
'11': "recommended",
|
||||
'12': "ondeck"
|
||||
'12': "ondeck",
|
||||
'13': 'browsefiles'
|
||||
}
|
||||
mediatypes = {
|
||||
# label according to nodetype per mediatype
|
||||
'movies':
|
||||
'movies':
|
||||
{
|
||||
'1': tagname,
|
||||
'2': 30174,
|
||||
# '4': 30177,
|
||||
# '6': 30189,
|
||||
'8': 39501,
|
||||
'9': 135,
|
||||
'10': 30227,
|
||||
'11': 30230,
|
||||
'12': 39500,
|
||||
'1': tagname,
|
||||
'2': 30174,
|
||||
# '4': 30177,
|
||||
# '6': 30189,
|
||||
'8': 39501,
|
||||
'9': 135,
|
||||
'10': 30227,
|
||||
'11': 30230,
|
||||
'12': 39500,
|
||||
'13': 39702
|
||||
},
|
||||
|
||||
'tvshows':
|
||||
'tvshows':
|
||||
{
|
||||
'1': tagname,
|
||||
# '2': 30170,
|
||||
'3': 30174,
|
||||
# '4': 30171,
|
||||
# '5': 30178,
|
||||
# '7': 30179,
|
||||
'9': 135,
|
||||
'10': 30227,
|
||||
# '11': 30230,
|
||||
'12': 39500,
|
||||
},
|
||||
|
||||
'homevideos':
|
||||
{
|
||||
'1': tagname,
|
||||
'2': 30251,
|
||||
'11': 30253
|
||||
},
|
||||
|
||||
'photos':
|
||||
{
|
||||
'1': tagname,
|
||||
'2': 30252,
|
||||
'8': 30255,
|
||||
'11': 30254
|
||||
'1': tagname,
|
||||
# '2': 30170,
|
||||
'3': 30174,
|
||||
# '4': 30171,
|
||||
# '5': 30178,
|
||||
# '7': 30179,
|
||||
'9': 135,
|
||||
'10': 30227,
|
||||
# '11': 30230,
|
||||
'12': 39500,
|
||||
'13': 39702
|
||||
},
|
||||
|
||||
'musicvideos':
|
||||
'homevideos':
|
||||
{
|
||||
'1': tagname,
|
||||
'2': 30256,
|
||||
'4': 30257,
|
||||
'6': 30258
|
||||
'1': tagname,
|
||||
'2': 30251,
|
||||
'11': 30253,
|
||||
'13': 39702
|
||||
},
|
||||
|
||||
'photos':
|
||||
{
|
||||
'1': tagname,
|
||||
'2': 30252,
|
||||
'8': 30255,
|
||||
'11': 30254,
|
||||
'13': 39702
|
||||
},
|
||||
|
||||
'musicvideos':
|
||||
{
|
||||
'1': tagname,
|
||||
'2': 30256,
|
||||
'4': 30257,
|
||||
'6': 30258,
|
||||
'13': 39702
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,6 +206,7 @@ class VideoNodes(object):
|
|||
'10': '8', # "random",
|
||||
'11': '5', # "recommended",
|
||||
'12': '1', # "ondeck"
|
||||
'13': '9' # browse by folder
|
||||
}
|
||||
|
||||
nodes = mediatypes[mediatype]
|
||||
|
@ -244,6 +251,8 @@ class VideoNodes(object):
|
|||
elif mediatype =="movies":
|
||||
# Reset nodetype; we got the label
|
||||
nodetype = 'inprogress'
|
||||
elif nodetype == 'browsefiles':
|
||||
path = 'plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s/folder' % viewid
|
||||
else:
|
||||
path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype)
|
||||
|
||||
|
@ -285,7 +294,7 @@ class VideoNodes(object):
|
|||
continue
|
||||
|
||||
# Create the root
|
||||
if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes') or mediatype == "homevideos"):
|
||||
if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes', 'browsefiles') or mediatype == "homevideos"):
|
||||
# Folder type with plugin path
|
||||
root = self.commonRoot(order=sortorder[node], label=label, tagname=tagname, roottype=2)
|
||||
etree.SubElement(root, 'path').text = path
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
<setting id="offerDelete" type="bool" label="30114" default="false" visible="false"/>
|
||||
<setting id="deleteTV" type="bool" label="30115" visible="eq(-1,true)" default="false" subsetting="true" />
|
||||
<setting id="deleteMovies" type="bool" label="30116" visible="eq(-2,true)" default="false" subsetting="true" />
|
||||
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" />
|
||||
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" visible="false"/>
|
||||
<setting type="sep" />
|
||||
<setting id="playType" type="enum" label="30002" values="Direct Play (default)|Direct Stream|Force Transcode" default="0" />
|
||||
<setting id="transcoderVideoQualities" type="enum" label="30160" values="420x420, 320kbps|576x320, 720kbps|720x480, 1.5Mbps|1024x768, 2Mbps|1280x720, 3Mbps|1280x720, 4Mbps|1920x1080, 8Mbps|1920x1080, 10Mbps|1920x1080, 12Mbps|1920x1080, 20Mbps|1920x1080, 40Mbps" default="10" /><!-- Video Quality if Transcoding necessary -->
|
||||
|
|
Loading…
Reference in a new issue