Merge pull request #633 from croneter/revamp_direct_paths
Better, safer way to enter network credentials for Direct Paths
This commit is contained in:
commit
9ec9278888
7 changed files with 234 additions and 143 deletions
|
@ -95,7 +95,8 @@ class Main():
|
||||||
entrypoint.toggle_plex_tv_sign_in()
|
entrypoint.toggle_plex_tv_sign_in()
|
||||||
|
|
||||||
elif mode == 'passwords':
|
elif mode == 'passwords':
|
||||||
utils.passwords_xml()
|
from resources.lib.windows import direct_path_sources
|
||||||
|
direct_path_sources.start()
|
||||||
|
|
||||||
elif mode == 'switchuser':
|
elif mode == 'switchuser':
|
||||||
entrypoint.switch_plex_user()
|
entrypoint.switch_plex_user()
|
||||||
|
|
|
@ -306,6 +306,36 @@ msgctxt "#30198"
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
# For setting up direct paths and adding network credentials
|
||||||
|
msgctxt "#30200"
|
||||||
|
msgid "In the following window, enter the server's hostname (or IP) where your Plex media resides. Mind the case!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# For setting up direct paths and adding network credentials - input window for hostname
|
||||||
|
msgctxt "#30201"
|
||||||
|
msgid "Enter server hostname (or IP)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# For setting up direct paths and adding network credentials
|
||||||
|
msgctxt "#30202"
|
||||||
|
msgid "In the following window, enter the network protocol you would like to use. This is likely 'smb'."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# For setting up direct paths and adding network credentials - input window protocol
|
||||||
|
msgctxt "#30203"
|
||||||
|
msgid "Enter network protocol"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# For setting up direct paths and adding network credentials
|
||||||
|
msgctxt "#30204"
|
||||||
|
msgid "The hostname or IP '{0}' that you entered is not valid."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# For setting up direct paths and adding network credentials
|
||||||
|
msgctxt "#30205"
|
||||||
|
msgid "The protocol '{0}' that you entered is not supported."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
# Video node naming for random e.g. movies
|
# Video node naming for random e.g. movies
|
||||||
msgctxt "#30227"
|
msgctxt "#30227"
|
||||||
msgid "Random"
|
msgid "Random"
|
||||||
|
@ -490,7 +520,7 @@ msgstr ""
|
||||||
|
|
||||||
# PKC Settings - Connection
|
# PKC Settings - Connection
|
||||||
msgctxt "#30517"
|
msgctxt "#30517"
|
||||||
msgid "Enter network credentials"
|
msgid "Set network credentials for Direct Paths and direct play"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# PKC Settings - Playback
|
# PKC Settings - Playback
|
||||||
|
|
|
@ -602,7 +602,8 @@ class InitialSetup(object):
|
||||||
# Go to network credentials?
|
# Go to network credentials?
|
||||||
if utils.yesno_dialog(utils.lang(39029), utils.lang(39030)):
|
if utils.yesno_dialog(utils.lang(39029), utils.lang(39030)):
|
||||||
LOG.debug("Presenting network credentials dialog.")
|
LOG.debug("Presenting network credentials dialog.")
|
||||||
utils.passwords_xml()
|
from .windows import direct_path_sources
|
||||||
|
direct_path_sources.start()
|
||||||
# Disable Plex music?
|
# Disable Plex music?
|
||||||
if utils.yesno_dialog(utils.lang(29999), utils.lang(39016)):
|
if utils.yesno_dialog(utils.lang(29999), utils.lang(39016)):
|
||||||
LOG.debug("User opted to disable Plex music library.")
|
LOG.debug("User opted to disable Plex music library.")
|
||||||
|
|
|
@ -14,7 +14,6 @@ import xml.etree.ElementTree as etree
|
||||||
import defusedxml.ElementTree as defused_etree # etree parse unsafe
|
import defusedxml.ElementTree as defused_etree # etree parse unsafe
|
||||||
from xml.etree.ElementTree import ParseError
|
from xml.etree.ElementTree import ParseError
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from urllib import quote_plus
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
import gc
|
import gc
|
||||||
|
@ -824,126 +823,6 @@ class XmlKodiSetting(object):
|
||||||
return element
|
return element
|
||||||
|
|
||||||
|
|
||||||
def passwords_xml():
|
|
||||||
"""
|
|
||||||
To add network credentials to Kodi's password xml
|
|
||||||
"""
|
|
||||||
path = path_ops.translate_path('special://userdata/')
|
|
||||||
xmlpath = "%spasswords.xml" % path
|
|
||||||
try:
|
|
||||||
xmlparse = defused_etree.parse(xmlpath)
|
|
||||||
except IOError:
|
|
||||||
# Document is blank or missing
|
|
||||||
root = etree.Element('passwords')
|
|
||||||
skip_find = True
|
|
||||||
except ParseError:
|
|
||||||
LOG.error('Error parsing %s', xmlpath)
|
|
||||||
# "Kodi cannot parse {0}. PKC will not function correctly. Please visit
|
|
||||||
# {1} and correct your file!"
|
|
||||||
messageDialog(lang(29999), lang(39716).format(
|
|
||||||
'passwords.xml', 'http://forum.kodi.tv/'))
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
root = xmlparse.getroot()
|
|
||||||
skip_find = False
|
|
||||||
|
|
||||||
credentials = settings('networkCreds')
|
|
||||||
if credentials:
|
|
||||||
# Present user with options
|
|
||||||
option = dialog('select',
|
|
||||||
"Modify/Remove network credentials",
|
|
||||||
["Modify", "Remove"])
|
|
||||||
|
|
||||||
if option < 0:
|
|
||||||
# User cancelled dialog
|
|
||||||
return
|
|
||||||
|
|
||||||
elif option == 1:
|
|
||||||
# User selected remove
|
|
||||||
success = False
|
|
||||||
for paths in root.getiterator('passwords'):
|
|
||||||
for path in paths:
|
|
||||||
if path.find('.//from').text == "smb://%s/" % credentials:
|
|
||||||
paths.remove(path)
|
|
||||||
LOG.info("Successfully removed credentials for: %s",
|
|
||||||
credentials)
|
|
||||||
etree.ElementTree(root).write(xmlpath,
|
|
||||||
encoding="UTF-8")
|
|
||||||
success = True
|
|
||||||
if not success:
|
|
||||||
LOG.error("Failed to find saved server: %s in passwords.xml",
|
|
||||||
credentials)
|
|
||||||
dialog('notification',
|
|
||||||
heading='{plex}',
|
|
||||||
message="%s not found" % credentials,
|
|
||||||
icon='{warning}',
|
|
||||||
sound=False)
|
|
||||||
return
|
|
||||||
settings('networkCreds', value="")
|
|
||||||
dialog('notification',
|
|
||||||
heading='{plex}',
|
|
||||||
message="%s removed from passwords.xml" % credentials,
|
|
||||||
icon='{plex}',
|
|
||||||
sound=False)
|
|
||||||
return
|
|
||||||
|
|
||||||
elif option == 0:
|
|
||||||
# User selected to modify
|
|
||||||
server = dialog('input',
|
|
||||||
"Modify the computer name or ip address",
|
|
||||||
credentials)
|
|
||||||
if not server:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# No credentials added
|
|
||||||
messageDialog("Network credentials",
|
|
||||||
'Input the server name or IP address as indicated in your plex '
|
|
||||||
'library paths. For example, the server name: '
|
|
||||||
'\\\\SERVER-PC\\path\\ or smb://SERVER-PC/path is SERVER-PC')
|
|
||||||
server = dialog('input', "Enter the server name or IP address")
|
|
||||||
if not server:
|
|
||||||
return
|
|
||||||
server = quote_plus(server)
|
|
||||||
|
|
||||||
# Network username
|
|
||||||
user = dialog('input', "Enter the network username")
|
|
||||||
if not user:
|
|
||||||
return
|
|
||||||
user = quote_plus(user)
|
|
||||||
# Network password
|
|
||||||
password = dialog('input',
|
|
||||||
"Enter the network password",
|
|
||||||
'', # Default input
|
|
||||||
type='{alphanum}',
|
|
||||||
option='{hide}')
|
|
||||||
# Need to url-encode the password
|
|
||||||
password = quote_plus(password)
|
|
||||||
# Add elements. Annoying etree bug where findall hangs forever
|
|
||||||
if skip_find is False:
|
|
||||||
skip_find = True
|
|
||||||
for path in root.findall('.//path'):
|
|
||||||
if path.find('.//from').text.lower() == "smb://%s/" % server.lower():
|
|
||||||
# Found the server, rewrite credentials
|
|
||||||
path.find('.//to').text = ("smb://%s:%s@%s/"
|
|
||||||
% (user, password, server))
|
|
||||||
skip_find = False
|
|
||||||
break
|
|
||||||
if skip_find:
|
|
||||||
# Server not found, add it.
|
|
||||||
path = etree.SubElement(root, 'path')
|
|
||||||
etree.SubElement(path, 'from', {'pathversion': "1"}).text = \
|
|
||||||
"smb://%s/" % server
|
|
||||||
topath = "smb://%s:%s@%s/" % (user, password, server)
|
|
||||||
etree.SubElement(path, 'to', {'pathversion': "1"}).text = topath
|
|
||||||
|
|
||||||
# Add credentials
|
|
||||||
settings('networkCreds', value="%s" % server)
|
|
||||||
LOG.info("Added server: %s to passwords.xml", server)
|
|
||||||
# Prettify and write to file
|
|
||||||
indent(root)
|
|
||||||
etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
|
|
||||||
|
|
||||||
|
|
||||||
def playlist_xsp(mediatype, tagname, viewid, viewtype="", delete=False):
|
def playlist_xsp(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
"""
|
"""
|
||||||
Feed with tagname as unicode
|
Feed with tagname as unicode
|
||||||
|
|
180
resources/lib/windows/direct_path_sources.py
Normal file
180
resources/lib/windows/direct_path_sources.py
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
:module: plexkodiconnect.userselect
|
||||||
|
:synopsis: Prompts the user to add network paths and username passwords for
|
||||||
|
e.g. smb paths
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
from logging import getLogger
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
import xbmc
|
||||||
|
|
||||||
|
from .. import path_ops, utils
|
||||||
|
|
||||||
|
LOG = getLogger('PLEX.direct_path_sources')
|
||||||
|
|
||||||
|
SUPPORTED_PROTOCOLS = ('smb', 'nfs', 'http', 'https', 'ftp', 'sftp')
|
||||||
|
PATH = path_ops.translate_path('special://userdata/')
|
||||||
|
|
||||||
|
|
||||||
|
def get_etree(topelement):
|
||||||
|
try:
|
||||||
|
xml = utils.defused_etree.parse(
|
||||||
|
path_ops.path.join(PATH, '%s.xml' % topelement))
|
||||||
|
except IOError:
|
||||||
|
# Document is blank or missing
|
||||||
|
LOG.info('%s.xml is missing or blank, creating it', topelement)
|
||||||
|
root = utils.etree.Element(topelement)
|
||||||
|
except utils.ParseError:
|
||||||
|
LOG.error('Error parsing %s', topelement)
|
||||||
|
# "Kodi cannot parse {0}. PKC will not function correctly. Please visit
|
||||||
|
# {1} and correct your file!"
|
||||||
|
utils.messageDialog(utils.lang(29999), utils.lang(39716).format(
|
||||||
|
'%s.xml' % topelement, 'http://forum.kodi.tv/'))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
root = xml.getroot()
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_hostname(hostname):
|
||||||
|
if len(hostname) > 255:
|
||||||
|
return False
|
||||||
|
if hostname[-1] == ".":
|
||||||
|
# strip exactly one dot from the right, if present
|
||||||
|
hostname = hostname[:-1]
|
||||||
|
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||||
|
return all(allowed.match(x) for x in hostname.split("."))
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_ip(ip):
|
||||||
|
try:
|
||||||
|
socket.inet_aton(ip)
|
||||||
|
# legal
|
||||||
|
except socket.error:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def start():
|
||||||
|
"""
|
||||||
|
Hit this function to start entering network credentials
|
||||||
|
"""
|
||||||
|
LOG.info('Editing sources.xml and passwords.xml')
|
||||||
|
# Fix for:
|
||||||
|
# DEBUG: Activating window ID: 13000
|
||||||
|
# INFO: Activate of window '13000' refused because there are active modal dialogs
|
||||||
|
# DEBUG: Activating window ID: 13000
|
||||||
|
xbmc.executebuiltin("Dialog.Close(all, true)")
|
||||||
|
# "In the following window, enter the server's hostname (or IP) where your
|
||||||
|
# Plex media resides. Mind the case!"
|
||||||
|
utils.messageDialog(utils.lang(29999), utils.lang(30200))
|
||||||
|
# "Enter server hostname (or IP)"
|
||||||
|
hostname = utils.dialog('input', utils.lang(30201))
|
||||||
|
if not hostname:
|
||||||
|
return
|
||||||
|
hostname = hostname.decode('utf-8').strip()
|
||||||
|
if not is_valid_hostname(hostname) and not is_valid_ip(hostname):
|
||||||
|
LOG.error('Entered invalid hostname or IP: %s', hostname)
|
||||||
|
# "The hostname or IP '{0}' that you entered is not valid"
|
||||||
|
utils.messageDialog(utils.lang(29999),
|
||||||
|
utils.lang(30204).format(hostname))
|
||||||
|
return
|
||||||
|
# "In the following window, enter the network protocol you would like to
|
||||||
|
# use. This is likely 'smb'."
|
||||||
|
utils.messageDialog(utils.lang(29999), utils.lang(30202))
|
||||||
|
# "Enter network protocol"
|
||||||
|
protocol = utils.dialog('input', utils.lang(30203))
|
||||||
|
if not protocol:
|
||||||
|
return
|
||||||
|
protocol = protocol.decode('utf-8').lower().strip()
|
||||||
|
if protocol not in SUPPORTED_PROTOCOLS:
|
||||||
|
LOG.error('Entered invalid protocol %s', protocol)
|
||||||
|
# "The protocol '{0}' that you entered is not supported."
|
||||||
|
utils.messageDialog(utils.lang(29999),
|
||||||
|
utils.lang(30205).format(protocol))
|
||||||
|
return
|
||||||
|
path = '%s://%s' % (protocol, hostname)
|
||||||
|
# Trailing slash at the end
|
||||||
|
paths = (path, '%s/' % path)
|
||||||
|
# Add hostname to sources.xml, if not already there
|
||||||
|
LOG.info('Hostname we are adding to sources.xml and passwords.xml: %s',
|
||||||
|
path)
|
||||||
|
try:
|
||||||
|
with utils.XmlKodiSetting('sources.xml',
|
||||||
|
force_create=True,
|
||||||
|
top_element='sources') as xml:
|
||||||
|
files = xml.root.find('files')
|
||||||
|
if files is None:
|
||||||
|
files = utils.etree.SubElement(xml.root, 'files')
|
||||||
|
utils.etree.SubElement(files,
|
||||||
|
'default',
|
||||||
|
attrib={'pathversion': '1'})
|
||||||
|
for source in files:
|
||||||
|
entry = source.find('path')
|
||||||
|
if entry is None:
|
||||||
|
LOG.debug('Entry is None')
|
||||||
|
continue
|
||||||
|
LOG.debug('entry found: %s', entry.text)
|
||||||
|
if entry.text in paths:
|
||||||
|
LOG.debug('Already have %s in sources.xml', path)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Need to add an element for our hostname
|
||||||
|
LOG.debug('Adding subelement to sources.xml for %s', hostname)
|
||||||
|
source = utils.etree.SubElement(files, 'source')
|
||||||
|
utils.etree.SubElement(source, 'name').text = 'PKC %s' % hostname
|
||||||
|
utils.etree.SubElement(source,
|
||||||
|
'path',
|
||||||
|
attrib={'pathversion': '1'}).text = '%s/' % path
|
||||||
|
utils.etree.SubElement(source, 'allowsharing').text = 'false'
|
||||||
|
xml.write_xml = True
|
||||||
|
except utils.ParseError:
|
||||||
|
return
|
||||||
|
# Add or change username and password in passwords.xml
|
||||||
|
try:
|
||||||
|
with utils.XmlKodiSetting('passwords.xml',
|
||||||
|
force_create=True,
|
||||||
|
top_element='passwords') as xml:
|
||||||
|
for entry in xml.root:
|
||||||
|
source = entry.find('from')
|
||||||
|
if source is None:
|
||||||
|
continue
|
||||||
|
if source.text in paths:
|
||||||
|
LOG.debug('Found an existing passwords.xml entry for %s, '
|
||||||
|
'replacing it',
|
||||||
|
path)
|
||||||
|
xml.root.remove(entry)
|
||||||
|
entry = utils.etree.SubElement(xml.root, 'path')
|
||||||
|
# "Username"
|
||||||
|
user = utils.dialog('input', utils.lang(1014))
|
||||||
|
if user is None:
|
||||||
|
xml.write_xml = False
|
||||||
|
return
|
||||||
|
user = user.strip()
|
||||||
|
user = urllib.quote(user)
|
||||||
|
user = user.decode('utf-8')
|
||||||
|
# "Password"
|
||||||
|
# May also be blank!! (=user aborts dialog)
|
||||||
|
password = utils.dialog('input',
|
||||||
|
utils.lang(733),
|
||||||
|
'',
|
||||||
|
type='{alphanum}',
|
||||||
|
option='{hide}')
|
||||||
|
password = urllib.quote(password)
|
||||||
|
password = password.decode('utf-8')
|
||||||
|
utils.etree.SubElement(entry,
|
||||||
|
'from',
|
||||||
|
attrib={'pathversion': '1'}).text = '%s/' % path
|
||||||
|
login = '%s://%s:%s@%s/' % (protocol, user, password, hostname)
|
||||||
|
utils.etree.SubElement(entry,
|
||||||
|
'to',
|
||||||
|
attrib={'pathversion': '1'}).text = login
|
||||||
|
xml.write_xml = True
|
||||||
|
except utils.ParseError:
|
||||||
|
return
|
||||||
|
LOG.info('Successfully completed editing sources.xml and padsswords.xml')
|
|
@ -15,7 +15,6 @@
|
||||||
<setting type="sep" text=""/>
|
<setting type="sep" text=""/>
|
||||||
|
|
||||||
<setting id="enforceUserLogin" label="30536" type="bool" default="false" />
|
<setting id="enforceUserLogin" label="30536" type="bool" default="false" />
|
||||||
<setting label="[COLOR yellow]$ADDON[plugin.video.plexkodiconnect 30517][/COLOR]" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Network credentials -->
|
|
||||||
<setting id="accessToken" type="text" visible="false" default="" />
|
<setting id="accessToken" type="text" visible="false" default="" />
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
|
@ -76,7 +75,8 @@
|
||||||
<setting id="showExtrasInsteadOfTrailer" type="bool" label="30514" default="false" /><!-- Show all Plex extras instead of immediately playing trailers -->
|
<setting id="showExtrasInsteadOfTrailer" type="bool" label="30514" default="false" /><!-- Show all Plex extras instead of immediately playing trailers -->
|
||||||
<setting id="indicate_media_versions" type="bool" label="39719" default="false" /><!-- Replace user ratings with number of versions -->
|
<setting id="indicate_media_versions" type="bool" label="39719" default="false" /><!-- Replace user ratings with number of versions -->
|
||||||
<setting id="enableMusic" type="bool" label="30509" default="true" />
|
<setting id="enableMusic" type="bool" label="30509" default="true" />
|
||||||
<setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" visible="true"/> <!-- Playback mode -->
|
<setting id="useDirectPaths" type="enum" label="30511" values="Addon (Default)|Direct Paths" default="0" visible="true"/> <!-- Playback mode -->
|
||||||
|
<setting label="[COLOR yellow]$ADDON[plugin.video.plexkodiconnect 30517][/COLOR]" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Set network credentials for Direct Paths and direct play -->
|
||||||
|
|
||||||
<setting id="streamMusic" type="bool" label="30510" default="false" visible="false" subsetting="true"/> <!-- Direct stream Music library -->
|
<setting id="streamMusic" type="bool" label="30510" default="false" visible="false" subsetting="true"/> <!-- Direct stream Music library -->
|
||||||
<setting id="kodiplextimeoffset" type="number" label="Time difference in seconds (Koditime - Plextime)" default="0" visible="false" option="int" />
|
<setting id="kodiplextimeoffset" type="number" label="Time difference in seconds (Koditime - Plextime)" default="0" visible="false" option="int" />
|
||||||
|
@ -128,7 +128,6 @@
|
||||||
<setting id="kodi_video_cache" type="number" visible="false" default="20971520" />
|
<setting id="kodi_video_cache" type="number" visible="false" default="20971520" />
|
||||||
<setting id="warned_setting_videoplayer.autoplaynextitem" type="bool" visible="false" default="false" />
|
<setting id="warned_setting_videoplayer.autoplaynextitem" type="bool" visible="false" default="false" />
|
||||||
<setting id="failedCount" type="number" visible="false" default="0" />
|
<setting id="failedCount" type="number" visible="false" default="0" />
|
||||||
<setting id="networkCreds" type="text" visible="false" default="" />
|
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
<category label="30544"><!-- artwork -->
|
<category label="30544"><!-- artwork -->
|
||||||
|
|
|
@ -15,26 +15,26 @@
|
||||||
</control>
|
</control>
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<visible>!String.IsEmpty(Window.Property(initialized))</visible>
|
<visible>!String.IsEmpty(Window.Property(initialized))</visible>
|
||||||
<posx>585</posx>
|
<posx>480</posx>
|
||||||
<posy>360</posy>
|
<posy>300</posy>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<posx>-40</posx>
|
<posx>-40</posx>
|
||||||
<posy>-40</posy>
|
<posy>-40</posy>
|
||||||
<width>830</width>
|
<width>1040</width>
|
||||||
<height>440</height>
|
<height>440</height>
|
||||||
<texture border="42">plugin.video.plexkodiconnect/drop-shadow.png</texture>
|
<texture border="42">plugin.video.plexkodiconnect/drop-shadow.png</texture>
|
||||||
</control>
|
</control>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<posx>0</posx>
|
<posx>0</posx>
|
||||||
<posy>0</posy>
|
<posy>0</posy>
|
||||||
<width>750</width>
|
<width>960</width>
|
||||||
<height>360</height>
|
<height>480</height>
|
||||||
<texture colordiffuse="EE323232" border="10">plugin.video.plexkodiconnect/white-square-rounded.png</texture>
|
<texture colordiffuse="EE323232" border="10">plugin.video.plexkodiconnect/white-square-rounded.png</texture>
|
||||||
</control>
|
</control>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<posx>0</posx>
|
<posx>0</posx>
|
||||||
<posy>0</posy>
|
<posy>0</posy>
|
||||||
<width>750</width>
|
<width>960</width>
|
||||||
<height>80</height>
|
<height>80</height>
|
||||||
<texture colordiffuse="99000000" border="10">plugin.video.plexkodiconnect/white-square-top-rounded.png</texture>
|
<texture colordiffuse="99000000" border="10">plugin.video.plexkodiconnect/white-square-top-rounded.png</texture>
|
||||||
</control>
|
</control>
|
||||||
|
@ -50,9 +50,9 @@
|
||||||
<control type="label">
|
<control type="label">
|
||||||
<posx>115</posx>
|
<posx>115</posx>
|
||||||
<posy>0</posy>
|
<posy>0</posy>
|
||||||
<width>575</width>
|
<width>785</width>
|
||||||
<height>80</height>
|
<height>80</height>
|
||||||
<font>font12</font>
|
<font>font14</font>
|
||||||
<align>left</align>
|
<align>left</align>
|
||||||
<aligny>center</aligny>
|
<aligny>center</aligny>
|
||||||
<textcolor>FFE5A00D</textcolor>
|
<textcolor>FFE5A00D</textcolor>
|
||||||
|
@ -62,10 +62,11 @@
|
||||||
<control type="textbox">
|
<control type="textbox">
|
||||||
<posx>115</posx>
|
<posx>115</posx>
|
||||||
<posy>105</posy>
|
<posy>105</posy>
|
||||||
<width>575</width>
|
<width>785</width>
|
||||||
<height>125</height>
|
<height>245</height>
|
||||||
<font>font10</font>
|
<font>font14</font>
|
||||||
<align>left</align>
|
<align>left</align>
|
||||||
|
<aligny>center</aligny>
|
||||||
<textcolor>FFFFFFFF</textcolor>
|
<textcolor>FFFFFFFF</textcolor>
|
||||||
<label>$INFO[Window.Property(info)]</label>
|
<label>$INFO[Window.Property(info)]</label>
|
||||||
</control>
|
</control>
|
||||||
|
@ -73,8 +74,8 @@
|
||||||
<control type="grouplist" id="100">
|
<control type="grouplist" id="100">
|
||||||
<defaultcontrol always="true">1001</defaultcontrol>
|
<defaultcontrol always="true">1001</defaultcontrol>
|
||||||
<posx>-10</posx>
|
<posx>-10</posx>
|
||||||
<posy>220</posy>
|
<posy>340</posy>
|
||||||
<width>770</width>
|
<width>980</width>
|
||||||
<height>155</height>
|
<height>155</height>
|
||||||
<align>right</align>
|
<align>right</align>
|
||||||
<itemgap>-50</itemgap>
|
<itemgap>-50</itemgap>
|
||||||
|
@ -89,7 +90,7 @@
|
||||||
<posy>0</posy>
|
<posy>0</posy>
|
||||||
<width min="120">auto</width>
|
<width min="120">auto</width>
|
||||||
<height>143</height>
|
<height>143</height>
|
||||||
<font>font10</font>
|
<font>font12</font>
|
||||||
<texturefocus colordiffuse="FFE5A00D" border="50">plugin.video.plexkodiconnect/buttons/blank-focus.png</texturefocus>
|
<texturefocus colordiffuse="FFE5A00D" border="50">plugin.video.plexkodiconnect/buttons/blank-focus.png</texturefocus>
|
||||||
<texturenofocus colordiffuse="99FFFFFF" border="50">plugin.video.plexkodiconnect/buttons/blank.png</texturenofocus>
|
<texturenofocus colordiffuse="99FFFFFF" border="50">plugin.video.plexkodiconnect/buttons/blank.png</texturenofocus>
|
||||||
<textoffsetx>70</textoffsetx>
|
<textoffsetx>70</textoffsetx>
|
||||||
|
@ -105,7 +106,7 @@
|
||||||
<posy>0</posy>
|
<posy>0</posy>
|
||||||
<width min="120">auto</width>
|
<width min="120">auto</width>
|
||||||
<height>143</height>
|
<height>143</height>
|
||||||
<font>font10</font>
|
<font>font12</font>
|
||||||
<texturefocus colordiffuse="FFE5A00D" border="50">plugin.video.plexkodiconnect/buttons/blank-focus.png</texturefocus>
|
<texturefocus colordiffuse="FFE5A00D" border="50">plugin.video.plexkodiconnect/buttons/blank-focus.png</texturefocus>
|
||||||
<texturenofocus colordiffuse="99FFFFFF" border="50">plugin.video.plexkodiconnect/buttons/blank.png</texturenofocus>
|
<texturenofocus colordiffuse="99FFFFFF" border="50">plugin.video.plexkodiconnect/buttons/blank.png</texturenofocus>
|
||||||
<textoffsetx>70</textoffsetx>
|
<textoffsetx>70</textoffsetx>
|
||||||
|
@ -121,7 +122,7 @@
|
||||||
<posy>0</posy>
|
<posy>0</posy>
|
||||||
<width min="120">auto</width>
|
<width min="120">auto</width>
|
||||||
<height>143</height>
|
<height>143</height>
|
||||||
<font>font10</font>
|
<font>font12</font>
|
||||||
<texturefocus colordiffuse="FFE5A00D" border="50">plugin.video.plexkodiconnect/buttons/blank-focus.png</texturefocus>
|
<texturefocus colordiffuse="FFE5A00D" border="50">plugin.video.plexkodiconnect/buttons/blank-focus.png</texturefocus>
|
||||||
<texturenofocus colordiffuse="99FFFFFF" border="50">plugin.video.plexkodiconnect/buttons/blank.png</texturenofocus>
|
<texturenofocus colordiffuse="99FFFFFF" border="50">plugin.video.plexkodiconnect/buttons/blank.png</texturenofocus>
|
||||||
<textoffsetx>70</textoffsetx>
|
<textoffsetx>70</textoffsetx>
|
||||||
|
|
Loading…
Reference in a new issue