3.0.0 Revision Krypton
Update playback for Krytpon Support Multi source Add Force Transcode Add a small listener for external players Update dialog skin (thank you sualfred)
26
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.emby"
|
<addon id="plugin.video.emby"
|
||||||
name="Emby"
|
name="Emby"
|
||||||
version="2.3.61"
|
version="3.0.0"
|
||||||
provider-name="Emby.media">
|
provider-name="Emby.media">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.19.0"/>
|
<import addon="xbmc.python" version="2.25.0"/>
|
||||||
<import addon="script.module.requests" version="2.9.1" />
|
<import addon="script.module.requests" version="2.9.1" />
|
||||||
<import addon="plugin.video.emby.movies" version="0.11" />
|
<import addon="plugin.video.emby.movies" version="0.11" />
|
||||||
<import addon="plugin.video.emby.tvshows" version="0.11" />
|
<import addon="plugin.video.emby.tvshows" version="0.11" />
|
||||||
|
@ -16,12 +16,17 @@
|
||||||
</extension>
|
</extension>
|
||||||
<extension point="xbmc.service" library="service.py" start="login">
|
<extension point="xbmc.service" library="service.py" start="login">
|
||||||
</extension>
|
</extension>
|
||||||
<extension point="kodi.context.item" library="contextmenu.py">
|
<extension point="kodi.context.item">
|
||||||
<item>
|
<menu id="kodi.core.main">
|
||||||
<label>30401</label>
|
<item library="contextmenu.py">
|
||||||
<description>Settings for the Emby Server</description>
|
<label>30401</label>
|
||||||
<visible>[!IsEmpty(ListItem.DBID) + !StringCompare(ListItem.DBID,-1) | !IsEmpty(ListItem.Property(embyid))] + !IsEmpty(Window(10000).Property(emby_context))</visible>
|
<visible>[!String.IsEmpty(ListItem.DBID) + !String.IsEqual(ListItem.DBID,-1) | !String.IsEmpty(ListItem.Property(embyid))] + !String.IsEmpty(Window(10000).Property(emby_context))</visible>
|
||||||
</item>
|
</item>
|
||||||
|
<item library="contextmenu_play.py">
|
||||||
|
<label>30402</label>
|
||||||
|
<visible>[[!String.IsEmpty(ListItem.DBID) + !String.IsEqual(ListItem.DBID,-1) | !String.IsEmpty(ListItem.Property(embyid))] + [String.IsEqual(ListItem.DBTYPE,movie) | String.IsEqual(ListItem.DBTYPE,episode)]] + !String.IsEmpty(Window(10000).Property(emby_context)) + !String.IsEmpty(Window(10000).Property(emby_context_transcode))</visible>
|
||||||
|
</item>
|
||||||
|
</menu>
|
||||||
</extension>
|
</extension>
|
||||||
<extension point="xbmc.addon.metadata">
|
<extension point="xbmc.addon.metadata">
|
||||||
<platform>all</platform>
|
<platform>all</platform>
|
||||||
|
@ -32,8 +37,9 @@
|
||||||
<source>https://github.com/MediaBrowser/plugin.video.emby</source>
|
<source>https://github.com/MediaBrowser/plugin.video.emby</source>
|
||||||
<summary lang="en"></summary>
|
<summary lang="en"></summary>
|
||||||
<description lang="en">Welcome to Emby for Kodi A whole new way to manage and view your media library. The Emby addon for Kodi combines the best of Kodi - ultra smooth navigation, beautiful UIs and playback of any file under the sun, and Emby - the most powerful fully open source multi-client media metadata indexer and server. Emby for Kodi is the absolute best way to enjoy the incredible Kodi playback engine combined with the power of Emby's centralized database. Features: Direct integration with the Kodi library for native Kodi speed Instant synchronization with the Emby server Full support for Movie, TV and Music collections Emby Server direct stream and transcoding support - use Kodi when you are away from home!</description>
|
<description lang="en">Welcome to Emby for Kodi A whole new way to manage and view your media library. The Emby addon for Kodi combines the best of Kodi - ultra smooth navigation, beautiful UIs and playback of any file under the sun, and Emby - the most powerful fully open source multi-client media metadata indexer and server. Emby for Kodi is the absolute best way to enjoy the incredible Kodi playback engine combined with the power of Emby's centralized database. Features: Direct integration with the Kodi library for native Kodi speed Instant synchronization with the Emby server Full support for Movie, TV and Music collections Emby Server direct stream and transcoding support - use Kodi when you are away from home!</description>
|
||||||
<news>v2.3.56
|
<news>v3.0.0
|
||||||
- Add support for Kodi Leia (Alpha) MusicDatabase v67
|
- Minimum version supported is now Kodi Krypton.
|
||||||
|
- For more info, check the emby Kodi forum thread.
|
||||||
</news>
|
</news>
|
||||||
</extension>
|
</extension>
|
||||||
</addon>
|
</addon>
|
37
contextmenu_play.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import xbmc
|
||||||
|
import xbmcaddon
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
_ADDON = xbmcaddon.Addon(id='plugin.video.emby')
|
||||||
|
_CWD = _ADDON.getAddonInfo('path').decode('utf-8')
|
||||||
|
_BASE_LIB = xbmc.translatePath(os.path.join(_CWD, 'resources', 'lib')).decode('utf-8')
|
||||||
|
sys.path.append(_BASE_LIB)
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
import loghandler
|
||||||
|
from context_entry import ContextMenu
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
loghandler.config()
|
||||||
|
log = logging.getLogger("EMBY.contextmenu_play")
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Start the context menu
|
||||||
|
ContextMenu(True)
|
||||||
|
except Exception as error:
|
||||||
|
log.exception(error)
|
|
@ -102,7 +102,13 @@ class Main(object):
|
||||||
'deviceid': entrypoint.resetDeviceId,
|
'deviceid': entrypoint.resetDeviceId,
|
||||||
'delete': entrypoint.deleteItem,
|
'delete': entrypoint.deleteItem,
|
||||||
'connect': entrypoint.emby_connect,
|
'connect': entrypoint.emby_connect,
|
||||||
'backup': entrypoint.emby_backup
|
'backup': entrypoint.emby_backup,
|
||||||
|
|
||||||
|
'manuallogin': entrypoint.test_manual_login,
|
||||||
|
'connectlogin': entrypoint.test_connect_login,
|
||||||
|
'manualserver': entrypoint.test_manual_server,
|
||||||
|
'connectservers': entrypoint.test_connect_servers,
|
||||||
|
'connectusers': entrypoint.test_connect_users
|
||||||
}
|
}
|
||||||
if mode in modes:
|
if mode in modes:
|
||||||
# Simple functions
|
# Simple functions
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<string id="29999">Emby for Kodi</string>
|
<string id="29999">Emby for Kodi</string>
|
||||||
<string id="30000">Server address</string>
|
<string id="30000">Server address</string>
|
||||||
<string id="30001">Server name</string>
|
<string id="30001">Server name</string>
|
||||||
<string id="30002">Play from HTTP instead of SMB</string>
|
<string id="30002">Enable HTTP playback</string>
|
||||||
<string id="30004">Log level</string>
|
<string id="30004">Log level</string>
|
||||||
<string id="30016">Device Name</string>
|
<string id="30016">Device Name</string>
|
||||||
<string id="30022">Advanced</string>
|
<string id="30022">Advanced</string>
|
||||||
|
@ -175,7 +175,8 @@
|
||||||
<string id="30312">Channels</string>
|
<string id="30312">Channels</string>
|
||||||
|
|
||||||
<!-- contextmenu -->
|
<!-- contextmenu -->
|
||||||
<string id="30401">Emby options</string>
|
<string id="30401">Emby Options</string>
|
||||||
|
<string id="30402">Emby Force transcode</string>
|
||||||
<string id="30405">Add to Emby favorites</string>
|
<string id="30405">Add to Emby favorites</string>
|
||||||
<string id="30406">Remove from Emby favorites</string>
|
<string id="30406">Remove from Emby favorites</string>
|
||||||
<string id="30407">Set custom song rating</string>
|
<string id="30407">Set custom song rating</string>
|
||||||
|
@ -206,7 +207,7 @@
|
||||||
<string id="30517">Network credentials</string>
|
<string id="30517">Network credentials</string>
|
||||||
<string id="30518">Enable Emby cinema mode</string>
|
<string id="30518">Enable Emby cinema mode</string>
|
||||||
<string id="30519">Ask to play trailers</string>
|
<string id="30519">Ask to play trailers</string>
|
||||||
<string id="30520">Skip Emby delete confirmation for the context menu (use at your own risk)</string>
|
<string id="30520">Skip Emby delete confirmation (use at your own risk)</string>
|
||||||
<string id="30521">Jump back on resume (in seconds)</string>
|
<string id="30521">Jump back on resume (in seconds)</string>
|
||||||
<string id="30522">Force transcode H265</string>
|
<string id="30522">Force transcode H265</string>
|
||||||
<string id="30523">Music metadata options (not compatible with direct stream)</string>
|
<string id="30523">Music metadata options (not compatible with direct stream)</string>
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
import logging
|
import logging
|
||||||
from utils import settings
|
from utils import settings
|
||||||
|
|
||||||
|
import artwork
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
log = logging.getLogger("EMBY."+__name__)
|
log = logging.getLogger("EMBY."+__name__)
|
||||||
|
@ -19,6 +21,7 @@ class API(object):
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
# item is the api response
|
# item is the api response
|
||||||
self.item = item
|
self.item = item
|
||||||
|
self.artwork = artwork.Artwork()
|
||||||
|
|
||||||
def get_userdata(self):
|
def get_userdata(self):
|
||||||
# Default
|
# Default
|
||||||
|
@ -71,12 +74,8 @@ class API(object):
|
||||||
writer = []
|
writer = []
|
||||||
cast = []
|
cast = []
|
||||||
|
|
||||||
try:
|
if 'People' in self.item:
|
||||||
people = self.item['People']
|
for person in self.item['People']:
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
for person in people:
|
|
||||||
|
|
||||||
type_ = person['Type']
|
type_ = person['Type']
|
||||||
name = person['Name']
|
name = person['Name']
|
||||||
|
@ -95,6 +94,26 @@ class API(object):
|
||||||
'Cast': cast
|
'Cast': cast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_actors(self):
|
||||||
|
|
||||||
|
cast = []
|
||||||
|
|
||||||
|
if 'People' in self.item:
|
||||||
|
|
||||||
|
self.artwork.get_people_artwork(self.item['People'])
|
||||||
|
|
||||||
|
for person in self.item['People']:
|
||||||
|
|
||||||
|
if person['Type'] == "Actor":
|
||||||
|
cast.append({
|
||||||
|
'name': person['Name'],
|
||||||
|
'role': person['Role'],
|
||||||
|
'order': len(cast) + 1,
|
||||||
|
'thumbnail': person['imageurl']
|
||||||
|
})
|
||||||
|
|
||||||
|
return cast
|
||||||
|
|
||||||
def get_media_streams(self):
|
def get_media_streams(self):
|
||||||
|
|
||||||
video_tracks = []
|
video_tracks = []
|
||||||
|
|
|
@ -468,7 +468,7 @@ class Artwork(object):
|
||||||
image = ""
|
image = ""
|
||||||
person_id = person['Id']
|
person_id = person['Id']
|
||||||
|
|
||||||
if "PrimaryImageTag" in person:
|
if 'PrimaryImageTag' in person:
|
||||||
image = (
|
image = (
|
||||||
"%s/emby/Items/%s/Images/Primary?"
|
"%s/emby/Items/%s/Images/Primary?"
|
||||||
"MaxWidth=400&MaxHeight=400&Index=0&Tag=%s"
|
"MaxWidth=400&MaxHeight=400&Index=0&Tag=%s"
|
||||||
|
@ -518,14 +518,14 @@ class Artwork(object):
|
||||||
tag, custom_query))
|
tag, custom_query))
|
||||||
all_artwork['Backdrop'].append(artwork)
|
all_artwork['Backdrop'].append(artwork)
|
||||||
|
|
||||||
def get_artwork(item_id, type_, tag):
|
def get_artwork(item_id, type_, tag, override_name=None):
|
||||||
|
|
||||||
if not tag: return
|
if not tag: return
|
||||||
|
|
||||||
artwork = ("%s/emby/Items/%s/Images/%s/0?"
|
artwork = ("%s/emby/Items/%s/Images/%s/0?"
|
||||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||||
% (self.server, item_id, type_, max_width, max_height, tag, custom_query))
|
% (self.server, item_id, type_, max_width, max_height, tag, custom_query))
|
||||||
all_artwork[type_] = artwork
|
all_artwork[override_name or type_] = artwork
|
||||||
|
|
||||||
# Process backdrops
|
# Process backdrops
|
||||||
get_backdrops(item_id, backdrops)
|
get_backdrops(item_id, backdrops)
|
||||||
|
@ -554,6 +554,9 @@ class Artwork(object):
|
||||||
get_artwork(item['Parent%sItemId' % parent_artwork], parent_artwork,
|
get_artwork(item['Parent%sItemId' % parent_artwork], parent_artwork,
|
||||||
item['Parent%sImageTag' % parent_artwork])
|
item['Parent%sImageTag' % parent_artwork])
|
||||||
|
|
||||||
|
if 'SeriesPrimaryImageTag' in item:
|
||||||
|
get_artwork(item['SeriesId'], "Primary", item['SeriesPrimaryImageTag'], "Series.Primary" if all_artwork['Primary'] else None)
|
||||||
|
|
||||||
# Parent album works a bit differently
|
# Parent album works a bit differently
|
||||||
if not all_artwork['Primary']:
|
if not all_artwork['Primary']:
|
||||||
|
|
||||||
|
|
|
@ -78,22 +78,7 @@ class ClientInfo(object):
|
||||||
|
|
||||||
emby_guid = xbmc.translatePath("special://temp/emby_guid").decode('utf-8')
|
emby_guid = xbmc.translatePath("special://temp/emby_guid").decode('utf-8')
|
||||||
|
|
||||||
###$ Begin migration $###
|
|
||||||
if not xbmcvfs.exists(emby_guid):
|
|
||||||
addon_path = self.addon.getAddonInfo('path').decode('utf-8')
|
|
||||||
if os.path.supports_unicode_filenames:
|
|
||||||
path = os.path.join(addon_path, "machine_guid")
|
|
||||||
else:
|
|
||||||
path = os.path.join(addon_path.encode('utf-8'), "machine_guid")
|
|
||||||
|
|
||||||
guid_file = xbmc.translatePath(path).decode('utf-8')
|
|
||||||
if xbmcvfs.exists(guid_file):
|
|
||||||
xbmcvfs.copy(guid_file, emby_guid)
|
|
||||||
log.info("guid migration completed")
|
|
||||||
###$ End migration $###
|
|
||||||
|
|
||||||
if reset and xbmcvfs.exists(emby_guid):
|
if reset and xbmcvfs.exists(emby_guid):
|
||||||
# Reset the file
|
|
||||||
xbmcvfs.delete(emby_guid)
|
xbmcvfs.delete(emby_guid)
|
||||||
|
|
||||||
guid = xbmcvfs.File(emby_guid)
|
guid = xbmcvfs.File(emby_guid)
|
||||||
|
|
|
@ -4,21 +4,26 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
|
|
||||||
import api
|
import api
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
|
import playbackutils as pbutils
|
||||||
import embydb_functions as embydb
|
import embydb_functions as embydb
|
||||||
import musicutils as musicutils
|
import musicutils as musicutils
|
||||||
from utils import settings, dialog, language as lang
|
from utils import settings, dialog, language as lang
|
||||||
from dialogs import context
|
from dialogs import context, resume
|
||||||
from database import DatabaseConn
|
from database import DatabaseConn
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
log = logging.getLogger("EMBY."+__name__)
|
log = logging.getLogger("EMBY."+__name__)
|
||||||
|
addon = xbmcaddon.Addon('plugin.video.emby')
|
||||||
|
|
||||||
|
XML_PATH = (addon.getAddonInfo('path'), "default", "1080i")
|
||||||
OPTIONS = {
|
OPTIONS = {
|
||||||
|
|
||||||
'Refresh': lang(30410),
|
'Refresh': lang(30410),
|
||||||
|
@ -38,7 +43,7 @@ class ContextMenu(object):
|
||||||
_selected_option = None
|
_selected_option = None
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, force_transcode=False):
|
||||||
|
|
||||||
self.emby = embyserver.Read_EmbyServer()
|
self.emby = embyserver.Read_EmbyServer()
|
||||||
|
|
||||||
|
@ -53,7 +58,10 @@ class ContextMenu(object):
|
||||||
self.item = self.emby.getItem(self.item_id)
|
self.item = self.emby.getItem(self.item_id)
|
||||||
self.api = api.API(self.item)
|
self.api = api.API(self.item)
|
||||||
|
|
||||||
if self._select_menu():
|
if force_transcode:
|
||||||
|
self._force_transcode()
|
||||||
|
|
||||||
|
elif self._select_menu():
|
||||||
self._action_menu()
|
self._action_menu()
|
||||||
|
|
||||||
if self._selected_option in (OPTIONS['Delete'], OPTIONS['AddFav'],
|
if self._selected_option in (OPTIONS['Delete'], OPTIONS['AddFav'],
|
||||||
|
@ -104,10 +112,6 @@ class ContextMenu(object):
|
||||||
userdata = self.api.get_userdata()
|
userdata = self.api.get_userdata()
|
||||||
options = []
|
options = []
|
||||||
|
|
||||||
if self.item_type in ("movie", "episode", "song"):
|
|
||||||
#options.append(OPTIONS['Transcode'])
|
|
||||||
pass
|
|
||||||
|
|
||||||
if userdata['Favorite']:
|
if userdata['Favorite']:
|
||||||
# Remove from emby favourites
|
# Remove from emby favourites
|
||||||
options.append(OPTIONS['RemoveFav'])
|
options.append(OPTIONS['RemoveFav'])
|
||||||
|
@ -126,9 +130,7 @@ class ContextMenu(object):
|
||||||
# Addon settings
|
# Addon settings
|
||||||
options.append(OPTIONS['Addon'])
|
options.append(OPTIONS['Addon'])
|
||||||
|
|
||||||
addon = xbmcaddon.Addon('plugin.video.emby')
|
context_menu = context.ContextMenu("script-emby-context.xml", *XML_PATH)
|
||||||
context_menu = context.ContextMenu("script-emby-context.xml", addon.getAddonInfo('path'),
|
|
||||||
"default", "1080i")
|
|
||||||
context_menu.set_options(options)
|
context_menu.set_options(options)
|
||||||
context_menu.doModal()
|
context_menu.doModal()
|
||||||
|
|
||||||
|
@ -141,10 +143,7 @@ class ContextMenu(object):
|
||||||
|
|
||||||
selected = self._selected_option.decode('utf-8')
|
selected = self._selected_option.decode('utf-8')
|
||||||
|
|
||||||
if selected == OPTIONS['Transcode']:
|
if selected == OPTIONS['Refresh']:
|
||||||
pass
|
|
||||||
|
|
||||||
elif selected == OPTIONS['Refresh']:
|
|
||||||
self.emby.refreshItem(self.item_id)
|
self.emby.refreshItem(self.item_id)
|
||||||
|
|
||||||
elif selected == OPTIONS['AddFav']:
|
elif selected == OPTIONS['AddFav']:
|
||||||
|
@ -198,3 +197,24 @@ class ContextMenu(object):
|
||||||
if delete:
|
if delete:
|
||||||
log.info("Deleting request: %s", self.item_id)
|
log.info("Deleting request: %s", self.item_id)
|
||||||
self.emby.deleteItem(self.item_id)
|
self.emby.deleteItem(self.item_id)
|
||||||
|
|
||||||
|
def _force_transcode(self):
|
||||||
|
|
||||||
|
log.info("Force transcode called.")
|
||||||
|
seektime = self.api.adjust_resume(self.api.get_userdata()['Resume'])
|
||||||
|
|
||||||
|
if seektime:
|
||||||
|
log.info("Resume dialog called.")
|
||||||
|
|
||||||
|
dialog = resume.ResumeDialog("script-emby-resume.xml", *XML_PATH)
|
||||||
|
dialog.set_resume_point("Resume from %s" % str(timedelta(seconds=seektime)).split(".")[0])
|
||||||
|
dialog.doModal()
|
||||||
|
|
||||||
|
if dialog.is_selected():
|
||||||
|
if not dialog.get_selected(): # Start from beginning selected.
|
||||||
|
self.item['UserData']['PlaybackPositionTicks'] = 0
|
||||||
|
else: # User backed out
|
||||||
|
log.info("User exited without a selection.")
|
||||||
|
return
|
||||||
|
|
||||||
|
pbutils.PlaybackUtils(self.item).play(self.item['Id'], self.kodi_id, True)
|
||||||
|
|
|
@ -57,7 +57,6 @@ class ContextMenu(xbmcgui.WindowXMLDialog):
|
||||||
for option in self._options:
|
for option in self._options:
|
||||||
self.list_.addItem(self._add_listitem(option))
|
self.list_.addItem(self._add_listitem(option))
|
||||||
|
|
||||||
self.background = self._add_editcontrol(730, height, 30, 450)
|
|
||||||
self.setFocus(self.list_)
|
self.setFocus(self.list_)
|
||||||
|
|
||||||
def onAction(self, action):
|
def onAction(self, action):
|
||||||
|
|
|
@ -22,6 +22,8 @@ SIGN_IN = 200
|
||||||
CANCEL = 201
|
CANCEL = 201
|
||||||
ERROR_TOGGLE = 202
|
ERROR_TOGGLE = 202
|
||||||
ERROR_MSG = 203
|
ERROR_MSG = 203
|
||||||
|
USER = 204
|
||||||
|
PASSWORD = 205
|
||||||
ERROR = {
|
ERROR = {
|
||||||
'Invalid': 1,
|
'Invalid': 1,
|
||||||
'Empty': 2
|
'Empty': 2
|
||||||
|
@ -52,21 +54,14 @@ class LoginConnect(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
def onInit(self):
|
def onInit(self):
|
||||||
|
|
||||||
self.user_field = self._add_editcontrol(725, 385, 40, 500)
|
self.user_field = self.getControl(USER)
|
||||||
self.setFocus(self.user_field)
|
self.setFocus(self.user_field)
|
||||||
self.password_field = self._add_editcontrol(725, 470, 40, 500, password=1)
|
self.password_field = self.getControl(PASSWORD)
|
||||||
self.signin_button = self.getControl(SIGN_IN)
|
self.signin_button = self.getControl(SIGN_IN)
|
||||||
self.remind_button = self.getControl(CANCEL)
|
self.remind_button = self.getControl(CANCEL)
|
||||||
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
||||||
self.error_msg = self.getControl(ERROR_MSG)
|
self.error_msg = self.getControl(ERROR_MSG)
|
||||||
|
|
||||||
self.user_field.controlUp(self.remind_button)
|
|
||||||
self.user_field.controlDown(self.password_field)
|
|
||||||
self.password_field.controlUp(self.user_field)
|
|
||||||
self.password_field.controlDown(self.signin_button)
|
|
||||||
self.signin_button.controlUp(self.password_field)
|
|
||||||
self.remind_button.controlDown(self.user_field)
|
|
||||||
|
|
||||||
def onClick(self, control):
|
def onClick(self, control):
|
||||||
|
|
||||||
if control == SIGN_IN:
|
if control == SIGN_IN:
|
||||||
|
@ -97,23 +92,6 @@ class LoginConnect(xbmcgui.WindowXMLDialog):
|
||||||
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def _add_editcontrol(self, x, y, height, width, password=0):
|
|
||||||
|
|
||||||
media = os.path.join(addon.getAddonInfo('path'), 'resources', 'skins', 'default', 'media')
|
|
||||||
control = xbmcgui.ControlEdit(0, 0, 0, 0,
|
|
||||||
label="User",
|
|
||||||
font="font10",
|
|
||||||
textColor="ff525252",
|
|
||||||
focusTexture=os.path.join(media, "button-focus.png"),
|
|
||||||
noFocusTexture=os.path.join(media, "button-focus.png"),
|
|
||||||
isPassword=password)
|
|
||||||
control.setPosition(x, y)
|
|
||||||
control.setHeight(height)
|
|
||||||
control.setWidth(width)
|
|
||||||
|
|
||||||
self.addControl(control)
|
|
||||||
return control
|
|
||||||
|
|
||||||
def _login(self, username, password):
|
def _login(self, username, password):
|
||||||
|
|
||||||
result = self.connect_manager.loginToConnect(username, password)
|
result = self.connect_manager.loginToConnect(username, password)
|
||||||
|
|
|
@ -23,6 +23,8 @@ SIGN_IN = 200
|
||||||
CANCEL = 201
|
CANCEL = 201
|
||||||
ERROR_TOGGLE = 202
|
ERROR_TOGGLE = 202
|
||||||
ERROR_MSG = 203
|
ERROR_MSG = 203
|
||||||
|
USER = 204
|
||||||
|
PASSWORD = 205
|
||||||
ERROR = {
|
ERROR = {
|
||||||
'Invalid': 1,
|
'Invalid': 1,
|
||||||
'Empty': 2
|
'Empty': 2
|
||||||
|
@ -50,7 +52,7 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
||||||
self.server = server
|
self.server = server
|
||||||
|
|
||||||
def set_user(self, user):
|
def set_user(self, user):
|
||||||
self.username = user or {}
|
self.username = user or None
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
return self._user
|
return self._user
|
||||||
|
@ -61,8 +63,8 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
||||||
self.cancel_button = self.getControl(CANCEL)
|
self.cancel_button = self.getControl(CANCEL)
|
||||||
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
||||||
self.error_msg = self.getControl(ERROR_MSG)
|
self.error_msg = self.getControl(ERROR_MSG)
|
||||||
self.user_field = self._add_editcontrol(725, 400, 40, 500)
|
self.user_field = self.getControl(USER)
|
||||||
self.password_field = self._add_editcontrol(725, 475, 40, 500, password=1)
|
self.password_field = self.getControl(PASSWORD)
|
||||||
|
|
||||||
if self.username:
|
if self.username:
|
||||||
self.user_field.setText(self.username)
|
self.user_field.setText(self.username)
|
||||||
|
@ -70,13 +72,6 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
||||||
else:
|
else:
|
||||||
self.setFocus(self.user_field)
|
self.setFocus(self.user_field)
|
||||||
|
|
||||||
self.user_field.controlUp(self.cancel_button)
|
|
||||||
self.user_field.controlDown(self.password_field)
|
|
||||||
self.password_field.controlUp(self.user_field)
|
|
||||||
self.password_field.controlDown(self.signin_button)
|
|
||||||
self.signin_button.controlUp(self.password_field)
|
|
||||||
self.cancel_button.controlDown(self.user_field)
|
|
||||||
|
|
||||||
def onClick(self, control):
|
def onClick(self, control):
|
||||||
|
|
||||||
if control == SIGN_IN:
|
if control == SIGN_IN:
|
||||||
|
@ -106,23 +101,6 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
||||||
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def _add_editcontrol(self, x, y, height, width, password=0):
|
|
||||||
|
|
||||||
media = os.path.join(addon.getAddonInfo('path'), 'resources', 'skins', 'default', 'media')
|
|
||||||
control = xbmcgui.ControlEdit(0, 0, 0, 0,
|
|
||||||
label="User",
|
|
||||||
font="font10",
|
|
||||||
textColor="ff525252",
|
|
||||||
focusTexture=os.path.join(media, "button-focus.png"),
|
|
||||||
noFocusTexture=os.path.join(media, "button-focus.png"),
|
|
||||||
isPassword=password)
|
|
||||||
control.setPosition(x, y)
|
|
||||||
control.setHeight(height)
|
|
||||||
control.setWidth(width)
|
|
||||||
|
|
||||||
self.addControl(control)
|
|
||||||
return control
|
|
||||||
|
|
||||||
def _login(self, username, password):
|
def _login(self, username, password):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
60
resources/lib/dialogs/resume.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##################################################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import xbmc
|
||||||
|
import xbmcgui
|
||||||
|
import xbmcaddon
|
||||||
|
|
||||||
|
##################################################################################################
|
||||||
|
|
||||||
|
log = logging.getLogger("EMBY."+__name__)
|
||||||
|
addon = xbmcaddon.Addon('plugin.video.emby')
|
||||||
|
|
||||||
|
ACTION_PARENT_DIR = 9
|
||||||
|
ACTION_PREVIOUS_MENU = 10
|
||||||
|
ACTION_BACK = 92
|
||||||
|
RESUME = 3010
|
||||||
|
START_BEGINNING = 3011
|
||||||
|
|
||||||
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeDialog(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
|
_resume_point = None
|
||||||
|
selected_option = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def set_resume_point(self, time):
|
||||||
|
self._resume_point = time
|
||||||
|
|
||||||
|
def is_selected(self):
|
||||||
|
return True if self.selected_option is not None else False
|
||||||
|
|
||||||
|
def get_selected(self):
|
||||||
|
return self.selected_option
|
||||||
|
|
||||||
|
def onInit(self):
|
||||||
|
|
||||||
|
self.getControl(RESUME).setLabel(self._resume_point)
|
||||||
|
self.getControl(START_BEGINNING).setLabel(xbmc.getLocalizedString(12021))
|
||||||
|
|
||||||
|
def onAction(self, action):
|
||||||
|
|
||||||
|
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def onClick(self, controlID):
|
||||||
|
|
||||||
|
if controlID == RESUME:
|
||||||
|
self.selected_option = 1
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
if controlID == START_BEGINNING:
|
||||||
|
self.selected_option = 0
|
||||||
|
self.close()
|
|
@ -21,7 +21,6 @@ ACTION_BACK = 92
|
||||||
ACTION_SELECT_ITEM = 7
|
ACTION_SELECT_ITEM = 7
|
||||||
ACTION_MOUSE_LEFT_CLICK = 100
|
ACTION_MOUSE_LEFT_CLICK = 100
|
||||||
USER_IMAGE = 150
|
USER_IMAGE = 150
|
||||||
USER_NAME = 151
|
|
||||||
LIST = 155
|
LIST = 155
|
||||||
CANCEL = 201
|
CANCEL = 201
|
||||||
MESSAGE_BOX = 202
|
MESSAGE_BOX = 202
|
||||||
|
@ -35,7 +34,6 @@ MANUAL_SERVER = 206
|
||||||
|
|
||||||
class ServerConnect(xbmcgui.WindowXMLDialog):
|
class ServerConnect(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
username = ""
|
|
||||||
user_image = None
|
user_image = None
|
||||||
servers = []
|
servers = []
|
||||||
|
|
||||||
|
@ -49,7 +47,7 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
|
||||||
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
|
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def set_args(self, **kwargs):
|
def set_args(self, **kwargs):
|
||||||
# connect_manager, username, user_image, servers, emby_connect
|
# connect_manager, user_image, servers, emby_connect
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in kwargs.iteritems():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
@ -77,13 +75,11 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
|
||||||
server_type = "wifi" if server.get('ExchangeToken') else "network"
|
server_type = "wifi" if server.get('ExchangeToken') else "network"
|
||||||
self.list_.addItem(self._add_listitem(server['Name'], server['Id'], server_type))
|
self.list_.addItem(self._add_listitem(server['Name'], server['Id'], server_type))
|
||||||
|
|
||||||
self.getControl(USER_NAME).setLabel("%s %s" % (lang(33000), self.username.decode('utf-8')))
|
|
||||||
|
|
||||||
if self.user_image is not None:
|
if self.user_image is not None:
|
||||||
self.getControl(USER_IMAGE).setImage(self.user_image)
|
self.getControl(USER_IMAGE).setImage(self.user_image)
|
||||||
|
|
||||||
if not self.emby_connect: # Change connect user
|
if not self.emby_connect: # Change connect user
|
||||||
self.getControl(EMBY_CONNECT).setLabel("[UPPERCASE][B]"+lang(30618)+"[/B][/UPPERCASE]")
|
self.getControl(EMBY_CONNECT).setLabel("[UPPERCASE][B]%s[/B][/UPPERCASE]" % lang(30618))
|
||||||
|
|
||||||
if self.servers:
|
if self.servers:
|
||||||
self.setFocus(self.list_)
|
self.setFocus(self.list_)
|
||||||
|
|
|
@ -24,6 +24,8 @@ CONNECT = 200
|
||||||
CANCEL = 201
|
CANCEL = 201
|
||||||
ERROR_TOGGLE = 202
|
ERROR_TOGGLE = 202
|
||||||
ERROR_MSG = 203
|
ERROR_MSG = 203
|
||||||
|
HOST = 204
|
||||||
|
PORT = 205
|
||||||
ERROR = {
|
ERROR = {
|
||||||
'Invalid': 1,
|
'Invalid': 1,
|
||||||
'Empty': 2
|
'Empty': 2
|
||||||
|
@ -57,19 +59,12 @@ class ServerManual(xbmcgui.WindowXMLDialog):
|
||||||
self.cancel_button = self.getControl(CANCEL)
|
self.cancel_button = self.getControl(CANCEL)
|
||||||
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
||||||
self.error_msg = self.getControl(ERROR_MSG)
|
self.error_msg = self.getControl(ERROR_MSG)
|
||||||
self.host_field = self._add_editcontrol(725, 400, 40, 500)
|
self.host_field = self.getControl(HOST)
|
||||||
self.port_field = self._add_editcontrol(725, 525, 40, 500)
|
self.port_field = self.getControl(PORT)
|
||||||
|
|
||||||
self.port_field.setText('8096')
|
self.port_field.setText('8096')
|
||||||
self.setFocus(self.host_field)
|
self.setFocus(self.host_field)
|
||||||
|
|
||||||
self.host_field.controlUp(self.cancel_button)
|
|
||||||
self.host_field.controlDown(self.port_field)
|
|
||||||
self.port_field.controlUp(self.host_field)
|
|
||||||
self.port_field.controlDown(self.connect_button)
|
|
||||||
self.connect_button.controlUp(self.port_field)
|
|
||||||
self.cancel_button.controlDown(self.host_field)
|
|
||||||
|
|
||||||
def onClick(self, control):
|
def onClick(self, control):
|
||||||
|
|
||||||
if control == CONNECT:
|
if control == CONNECT:
|
||||||
|
@ -99,22 +94,6 @@ class ServerManual(xbmcgui.WindowXMLDialog):
|
||||||
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def _add_editcontrol(self, x, y, height, width):
|
|
||||||
|
|
||||||
media = os.path.join(addon.getAddonInfo('path'), 'resources', 'skins', 'default', 'media')
|
|
||||||
control = xbmcgui.ControlEdit(0, 0, 0, 0,
|
|
||||||
label="User",
|
|
||||||
font="font10",
|
|
||||||
textColor="ffc2c2c2",
|
|
||||||
focusTexture=os.path.join(media, "button-focus.png"),
|
|
||||||
noFocusTexture=os.path.join(media, "button-focus.png"))
|
|
||||||
control.setPosition(x, y)
|
|
||||||
control.setHeight(height)
|
|
||||||
control.setWidth(width)
|
|
||||||
|
|
||||||
self.addControl(control)
|
|
||||||
return control
|
|
||||||
|
|
||||||
def _connect_to_server(self, server, port):
|
def _connect_to_server(self, server, port):
|
||||||
|
|
||||||
server_address = "%s:%s" % (server, port) if port else server
|
server_address = "%s:%s" % (server, port) if port else server
|
||||||
|
|
|
@ -54,7 +54,7 @@ class UsersConnect(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
self.list_ = self.getControl(LIST)
|
self.list_ = self.getControl(LIST)
|
||||||
for user in self.users:
|
for user in self.users:
|
||||||
user_image = ("userflyoutdefault2.png" if 'PrimaryImageTag' not in user
|
user_image = ("items/logindefault.png" if 'PrimaryImageTag' not in user
|
||||||
else self._get_user_artwork(user['Id'], 'Primary'))
|
else self._get_user_artwork(user['Id'], 'Primary'))
|
||||||
self.list_.addItem(self._add_listitem(user['Name'], user['Id'], user_image))
|
self.list_.addItem(self._add_listitem(user['Name'], user['Id'], user_image))
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ class DownloadUtils(object):
|
||||||
"SetAudioStreamIndex,SetSubtitleStreamIndex,"
|
"SetAudioStreamIndex,SetSubtitleStreamIndex,"
|
||||||
"SetRepeatMode,"
|
"SetRepeatMode,"
|
||||||
"Mute,Unmute,SetVolume,"
|
"Mute,Unmute,SetVolume,"
|
||||||
"Play,Playstate,PlayNext"
|
"Play,Playstate,PlayNext,PlayMediaSource"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,15 @@ import playbackutils as pbutils
|
||||||
import playutils
|
import playutils
|
||||||
import api
|
import api
|
||||||
from views import Playlist, VideoNodes
|
from views import Playlist, VideoNodes
|
||||||
from utils import window, settings, dialog, language as lang, plugin_path
|
from utils import window, settings, dialog, language as lang, urllib_path
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
log = logging.getLogger("EMBY."+__name__)
|
log = logging.getLogger("EMBY."+__name__)
|
||||||
|
|
||||||
|
addon = xbmcaddon.Addon(id='plugin.video.emby')
|
||||||
|
XML_PATH = (addon.getAddonInfo('path'), "default", "1080i")
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,6 +110,12 @@ def doMainListing():
|
||||||
log.info(window('emby_server%s.name' % server))
|
log.info(window('emby_server%s.name' % server))
|
||||||
addDirectoryItem(window('emby_server%s.name' % server), "plugin://plugin.video.emby/?mode=%s" % server)'''
|
addDirectoryItem(window('emby_server%s.name' % server), "plugin://plugin.video.emby/?mode=%s" % server)'''
|
||||||
|
|
||||||
|
addDirectoryItem("Manual login dialog", "plugin://plugin.video.emby/?mode=manuallogin")
|
||||||
|
addDirectoryItem("Connect login dialog", "plugin://plugin.video.emby/?mode=connectlogin")
|
||||||
|
addDirectoryItem("Manual server dialog", "plugin://plugin.video.emby/?mode=manualserver")
|
||||||
|
addDirectoryItem("Connect servers dialog", "plugin://plugin.video.emby/?mode=connectservers")
|
||||||
|
addDirectoryItem("Connect users dialog", "plugin://plugin.video.emby/?mode=connectusers")
|
||||||
|
|
||||||
addDirectoryItem(lang(30517), "plugin://plugin.video.emby/?mode=passwords")
|
addDirectoryItem(lang(30517), "plugin://plugin.video.emby/?mode=passwords")
|
||||||
addDirectoryItem(lang(33053), "plugin://plugin.video.emby/?mode=settings")
|
addDirectoryItem(lang(33053), "plugin://plugin.video.emby/?mode=settings")
|
||||||
addDirectoryItem(lang(33054), "plugin://plugin.video.emby/?mode=adduser")
|
addDirectoryItem(lang(33054), "plugin://plugin.video.emby/?mode=adduser")
|
||||||
|
@ -123,6 +132,140 @@ def doMainListing():
|
||||||
|
|
||||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||||
|
|
||||||
|
def test_manual_login():
|
||||||
|
from dialogs import LoginManual
|
||||||
|
dialog = LoginManual("script-emby-connect-login-manual.xml", *XML_PATH)
|
||||||
|
dialog.set_server("Test server")
|
||||||
|
dialog.set_user("Test user")
|
||||||
|
dialog.doModal()
|
||||||
|
|
||||||
|
def test_connect_login():
|
||||||
|
from dialogs import LoginConnect
|
||||||
|
dialog = LoginConnect("script-emby-connect-login.xml", *XML_PATH)
|
||||||
|
dialog.doModal()
|
||||||
|
|
||||||
|
def test_manual_server():
|
||||||
|
from dialogs import ServerManual
|
||||||
|
dialog = ServerManual("script-emby-connect-server-manual.xml", *XML_PATH)
|
||||||
|
dialog.doModal()
|
||||||
|
|
||||||
|
def test_connect_servers():
|
||||||
|
from dialogs import ServerConnect
|
||||||
|
dialog = ServerConnect("script-emby-connect-server.xml", *XML_PATH)
|
||||||
|
test_servers = [
|
||||||
|
{
|
||||||
|
u'LastConnectionMode': 2,
|
||||||
|
u'Name': u'Server Name',
|
||||||
|
u'AccessToken': u'Token',
|
||||||
|
u'RemoteAddress': u'http://remote.address:8096',
|
||||||
|
u'UserId': u'd4000909883845059aadef13b7110375',
|
||||||
|
u'ManualAddress': u'http://manual.address:8096',
|
||||||
|
u'DateLastAccessed': '2018-01-01T02:36:58Z',
|
||||||
|
u'LocalAddress': u'http://local.address:8096',
|
||||||
|
u'Id': u'b1ef1940b1964e2188f00b73611d53fd',
|
||||||
|
u'Users': [
|
||||||
|
{
|
||||||
|
u'IsSignedInOffline': True,
|
||||||
|
u'Id': u'd4000909883845059aadef13b7110375'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
kwargs = {
|
||||||
|
'username': "Test user",
|
||||||
|
'user_image': None,
|
||||||
|
'servers': test_servers,
|
||||||
|
'emby_connect': True
|
||||||
|
}
|
||||||
|
dialog.set_args(**kwargs)
|
||||||
|
dialog.doModal()
|
||||||
|
|
||||||
|
def test_connect_users():
|
||||||
|
from dialogs import UsersConnect
|
||||||
|
test_users = [{
|
||||||
|
u'Name': u'Guest',
|
||||||
|
u'HasConfiguredEasyPassword': False,
|
||||||
|
u'LastActivityDate': u'2018-01-01T04:10:15.4195197Z',
|
||||||
|
u'HasPassword': False,
|
||||||
|
u'LastLoginDate': u'2017-12-28T02:53:01.5770625Z',
|
||||||
|
u'Policy': {
|
||||||
|
u'EnabledDevices': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'EnableMediaPlayback': True,
|
||||||
|
u'EnableRemoteControlOfOtherUsers': False,
|
||||||
|
u'RemoteClientBitrateLimit': 0,
|
||||||
|
u'BlockUnratedItems': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'EnableAllDevices': True,
|
||||||
|
u'InvalidLoginAttemptCount': 0,
|
||||||
|
u'EnableUserPreferenceAccess': True,
|
||||||
|
u'EnableLiveTvManagement': False,
|
||||||
|
u'EnableLiveTvAccess': False,
|
||||||
|
u'IsAdministrator': False,
|
||||||
|
u'EnableContentDeletion': False,
|
||||||
|
u'EnabledChannels': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'IsDisabled': False,
|
||||||
|
u'EnableSyncTranscoding': False,
|
||||||
|
u'EnableAudioPlaybackTranscoding': True,
|
||||||
|
u'EnableSharedDeviceControl': False,
|
||||||
|
u'AccessSchedules': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'IsHidden': False,
|
||||||
|
u'EnableContentDeletionFromFolders': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'EnableContentDownloading': False,
|
||||||
|
u'EnableVideoPlaybackTranscoding': True,
|
||||||
|
u'EnabledFolders': [
|
||||||
|
u'0c41907140d802bb58430fed7e2cd79e',
|
||||||
|
u'a329cda1727467c08a8f1493195d32d3',
|
||||||
|
u'f137a2dd21bbc1b99aa5c0f6bf02a805',
|
||||||
|
u'4514ec850e5ad0c47b58444e17b6346c'
|
||||||
|
],
|
||||||
|
u'EnableAllChannels': False,
|
||||||
|
u'BlockedTags': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'EnableAllFolders': False,
|
||||||
|
u'EnablePublicSharing': False,
|
||||||
|
u'EnablePlaybackRemuxing': True
|
||||||
|
},
|
||||||
|
u'ServerId': u'Test',
|
||||||
|
u'Configuration': {
|
||||||
|
u'SubtitleMode': u'Default',
|
||||||
|
u'HidePlayedInLatest': True,
|
||||||
|
u'GroupedFolders': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'DisplayCollectionsView': False,
|
||||||
|
u'OrderedViews': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'SubtitleLanguagePreference': u'',
|
||||||
|
u'AudioLanguagePreference': u'',
|
||||||
|
u'LatestItemsExcludes': [
|
||||||
|
|
||||||
|
],
|
||||||
|
u'EnableLocalPassword': False,
|
||||||
|
u'RememberAudioSelections': True,
|
||||||
|
u'RememberSubtitleSelections': True,
|
||||||
|
u'DisplayMissingEpisodes': False,
|
||||||
|
u'PlayDefaultAudioTrack': True,
|
||||||
|
u'EnableNextEpisodeAutoPlay': True
|
||||||
|
},
|
||||||
|
u'Id': u'a9d56d37cb6b47a3bfd3453c55138ff1',
|
||||||
|
u'HasConfiguredPassword': False
|
||||||
|
}]
|
||||||
|
dialog = UsersConnect("script-emby-connect-users.xml", *XML_PATH)
|
||||||
|
dialog.set_server("Test server")
|
||||||
|
dialog.set_users(test_users)
|
||||||
|
dialog.doModal()
|
||||||
|
|
||||||
def emby_connect():
|
def emby_connect():
|
||||||
|
|
||||||
# Login user to emby connect
|
# Login user to emby connect
|
||||||
|
@ -657,7 +800,7 @@ def BrowseContent(viewname, browse_type="", folderid=""):
|
||||||
'type': browse_type,
|
'type': browse_type,
|
||||||
'folderid': item['Id']
|
'folderid': item['Id']
|
||||||
}
|
}
|
||||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True)
|
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True)
|
||||||
else: #playable item, set plugin path and mediastreams
|
else: #playable item, set plugin path and mediastreams
|
||||||
xbmcplugin.setContent(int(sys.argv[1]), 'episodes' if folderid else 'files')
|
xbmcplugin.setContent(int(sys.argv[1]), 'episodes' if folderid else 'files')
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
@ -26,11 +27,15 @@ KODI = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||||
class KodiMonitor(xbmc.Monitor):
|
class KodiMonitor(xbmc.Monitor):
|
||||||
|
|
||||||
retry = True
|
retry = True
|
||||||
|
special_monitor = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
xbmc.Monitor.__init__(self)
|
xbmc.Monitor.__init__(self)
|
||||||
|
|
||||||
|
if settings('useDirectPaths') == "0":
|
||||||
|
self.special_monitor = SpecialMonitor().start()
|
||||||
|
|
||||||
self.download = downloadutils.DownloadUtils().downloadUrl
|
self.download = downloadutils.DownloadUtils().downloadUrl
|
||||||
log.info("Kodi monitor started")
|
log.info("Kodi monitor started")
|
||||||
|
|
||||||
|
@ -60,6 +65,11 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
log.info("New context setting: %s", current_context)
|
log.info("New context setting: %s", current_context)
|
||||||
window('emby_context', value=current_context)
|
window('emby_context', value=current_context)
|
||||||
|
|
||||||
|
current_context = "true" if settings('enableContextTranscode') == "true" else ""
|
||||||
|
if window('emby_context_transcode') != current_context:
|
||||||
|
log.info("New context transcode setting: %s", current_context)
|
||||||
|
window('emby_context_transcode', value=current_context)
|
||||||
|
|
||||||
@log_error()
|
@log_error()
|
||||||
def onNotification(self, sender, method, data):
|
def onNotification(self, sender, method, data):
|
||||||
|
|
||||||
|
@ -142,7 +152,7 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
else:
|
else:
|
||||||
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||||
# Set properties for player.py
|
# Set properties for player.py
|
||||||
playback.setProperties(playurl, listitem)
|
playback.set_properties(playurl, listitem)
|
||||||
|
|
||||||
def _video_update(self, data):
|
def _video_update(self, data):
|
||||||
# Manually marking as watched/unwatched
|
# Manually marking as watched/unwatched
|
||||||
|
@ -199,3 +209,58 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
log.info("Could not retrieve item Id")
|
log.info("Could not retrieve item Id")
|
||||||
|
|
||||||
return item_id
|
return item_id
|
||||||
|
|
||||||
|
|
||||||
|
class SpecialMonitor(threading.Thread):
|
||||||
|
|
||||||
|
_stop_thread = False
|
||||||
|
external_count = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
''' Detect the resume dialog for widgets.
|
||||||
|
Detect external players.
|
||||||
|
'''
|
||||||
|
monitor = xbmc.Monitor()
|
||||||
|
|
||||||
|
log.warn("----====# Starting Special Monitor #====----")
|
||||||
|
|
||||||
|
while not self._stop_thread:
|
||||||
|
|
||||||
|
player = xbmc.Player()
|
||||||
|
isPlaying = player.isPlaying()
|
||||||
|
|
||||||
|
if (not isPlaying and xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)') and
|
||||||
|
not xbmc.getCondVisibility('Window.IsVisible(MyVideoNav.xml)') and
|
||||||
|
xbmc.getInfoLabel('Control.GetLabel(1002)') == xbmc.getLocalizedString(12021)):
|
||||||
|
|
||||||
|
control = int(xbmcgui.Window(10106).getFocusId())
|
||||||
|
if control == 1002: # Start from beginning
|
||||||
|
log.info("Resume dialog: Start from beginning selected.")
|
||||||
|
window('emby.resume', value="true")
|
||||||
|
else:
|
||||||
|
window('emby.resume', clear=True)
|
||||||
|
|
||||||
|
elif isPlaying and not window('emby.external_check'):
|
||||||
|
time = player.getTime()
|
||||||
|
|
||||||
|
if time > 1:
|
||||||
|
window('emby.external_check', value="true")
|
||||||
|
self.external_count = 0
|
||||||
|
elif self.external_count == 15:
|
||||||
|
log.info("External player detected.")
|
||||||
|
window('emby.external', value="true")
|
||||||
|
window('emby.external_check', value="true")
|
||||||
|
self.external_count = 0
|
||||||
|
elif time == 0:
|
||||||
|
self.external_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
if monitor.waitForAbort(0.5):
|
||||||
|
# Abort was requested while waiting. We should exit
|
||||||
|
break
|
||||||
|
|
||||||
|
log.warn("#====---- Special Monitor Stopped ----====#")
|
||||||
|
|
||||||
|
def stop_monitor(self):
|
||||||
|
self._stop_thread = True
|
||||||
|
|
|
@ -241,7 +241,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
# use emby and video DBs
|
# use emby and video DBs
|
||||||
with database.DatabaseConn('emby') as cursor_emby:
|
with database.DatabaseConn('emby') as cursor_emby:
|
||||||
with database.DatabaseConn('video') as cursor_video:
|
with database.DatabaseConn('video') as cursor_video:
|
||||||
# content sync: movies, tvshows, musicvideos, music
|
# content sync: movies, tvshows, musicvideos, music
|
||||||
|
|
||||||
if manualrun:
|
if manualrun:
|
||||||
|
|
|
@ -63,18 +63,6 @@ class KodiMovies(KodiItems):
|
||||||
return kodi_id
|
return kodi_id
|
||||||
|
|
||||||
def add_movie(self, *args):
|
def add_movie(self, *args):
|
||||||
query = (
|
|
||||||
'''
|
|
||||||
INSERT INTO movie(
|
|
||||||
idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
|
|
||||||
c09, c10, c11, c12, c14, c15, c16, c18, c19, c21)
|
|
||||||
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def add_movie_17(self, *args):
|
|
||||||
# Create the movie entry
|
# Create the movie entry
|
||||||
query = (
|
query = (
|
||||||
'''
|
'''
|
||||||
|
@ -88,17 +76,6 @@ class KodiMovies(KodiItems):
|
||||||
self.cursor.execute(query, (args))
|
self.cursor.execute(query, (args))
|
||||||
|
|
||||||
def update_movie(self, *args):
|
def update_movie(self, *args):
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"UPDATE movie",
|
|
||||||
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
|
|
||||||
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
|
|
||||||
"c16 = ?, c18 = ?, c19 = ?, c21 = ?",
|
|
||||||
"WHERE idMovie = ?"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def update_movie_17(self, *args):
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
"UPDATE movie",
|
"UPDATE movie",
|
||||||
|
@ -177,48 +154,16 @@ class KodiMovies(KodiItems):
|
||||||
|
|
||||||
def add_countries(self, kodi_id, countries):
|
def add_countries(self, kodi_id, countries):
|
||||||
|
|
||||||
if self.kodi_version > 14:
|
for country in countries:
|
||||||
|
country_id = self._get_country(country)
|
||||||
|
|
||||||
for country in countries:
|
query = (
|
||||||
country_id = self._get_country(country)
|
'''
|
||||||
|
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
||||||
query = (
|
VALUES (?, ?, ?)
|
||||||
'''
|
'''
|
||||||
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
)
|
||||||
VALUES (?, ?, ?)
|
self.cursor.execute(query, (country_id, kodi_id, "movie"))
|
||||||
'''
|
|
||||||
)
|
|
||||||
self.cursor.execute(query, (country_id, kodi_id, "movie"))
|
|
||||||
else:
|
|
||||||
# TODO: Remove Helix code when Krypton is RC
|
|
||||||
for country in countries:
|
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"SELECT idCountry",
|
|
||||||
"FROM country",
|
|
||||||
"WHERE strCountry = ?",
|
|
||||||
"COLLATE NOCASE"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (country,))
|
|
||||||
|
|
||||||
try:
|
|
||||||
country_id = self.cursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
# Create a new entry
|
|
||||||
self.cursor.execute("select coalesce(max(idCountry),0) from country")
|
|
||||||
country_id = self.cursor.fetchone()[0] + 1
|
|
||||||
|
|
||||||
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
|
|
||||||
self.cursor.execute(query, (country_id, country))
|
|
||||||
log.debug("Add country to media, processing: %s", country)
|
|
||||||
|
|
||||||
query = (
|
|
||||||
'''
|
|
||||||
INSERT OR REPLACE INTO countrylinkmovie(idCountry, idMovie)
|
|
||||||
VALUES (?, ?)
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
self.cursor.execute(query, (country_id, kodi_id))
|
|
||||||
|
|
||||||
def _add_country(self, country):
|
def _add_country(self, country):
|
||||||
|
|
||||||
|
|
|
@ -189,26 +189,6 @@ class KodiMusic(KodiItems):
|
||||||
return album_id
|
return album_id
|
||||||
|
|
||||||
def update_album(self, *args):
|
def update_album(self, *args):
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"UPDATE album",
|
|
||||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
|
||||||
"iRating = ?, lastScraped = ?, strReleaseType = ?",
|
|
||||||
"WHERE idAlbum = ?"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def update_album_18(self, *args):
|
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"UPDATE album",
|
|
||||||
"SET strArtistsDisp = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
|
||||||
"iUserrating = ?, lastScraped = ?, strReleaseType = ?",
|
|
||||||
"WHERE idAlbum = ?"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def update_album_17(self, *args):
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
"UPDATE album",
|
"UPDATE album",
|
||||||
|
@ -218,27 +198,6 @@ class KodiMusic(KodiItems):
|
||||||
))
|
))
|
||||||
self.cursor.execute(query, (args))
|
self.cursor.execute(query, (args))
|
||||||
|
|
||||||
def update_album_15(self, *args):
|
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"UPDATE album",
|
|
||||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
|
||||||
"iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?",
|
|
||||||
"WHERE idAlbum = ?"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def update_album_14(self, *args):
|
|
||||||
# TODO: Remove Helix code when Krypton is RC
|
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"UPDATE album",
|
|
||||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
|
||||||
"iRating = ?, lastScraped = ?, dateAdded = ?",
|
|
||||||
"WHERE idAlbum = ?"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def get_album_artist(self, album_id, artists):
|
def get_album_artist(self, album_id, artists):
|
||||||
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
@ -356,8 +315,8 @@ class KodiMusic(KodiItems):
|
||||||
|
|
||||||
"UPDATE song",
|
"UPDATE song",
|
||||||
"SET idAlbum = ?, strArtistDisp = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
|
"SET idAlbum = ?, strArtistDisp = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
|
||||||
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
|
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
|
||||||
"rating = ?, comment = ?",
|
"rating = ?, comment = ?",
|
||||||
"WHERE idSong = ?"
|
"WHERE idSong = ?"
|
||||||
))
|
))
|
||||||
self.cursor.execute(query, (args))
|
self.cursor.execute(query, (args))
|
||||||
|
|
|
@ -174,9 +174,8 @@ class KodiTVShows(KodiItems):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
season_id = self._add_season(show_id, number)
|
season_id = self._add_season(show_id, number)
|
||||||
|
|
||||||
if self.kodi_version > 15 and name is not None:
|
query = "UPDATE seasons SET name = ? WHERE idSeason = ?"
|
||||||
query = "UPDATE seasons SET name = ? WHERE idSeason = ?"
|
self.cursor.execute(query, (name, season_id))
|
||||||
self.cursor.execute(query, (name, season_id))
|
|
||||||
|
|
||||||
return season_id
|
return season_id
|
||||||
|
|
||||||
|
@ -189,18 +188,6 @@ class KodiTVShows(KodiItems):
|
||||||
return season_id
|
return season_id
|
||||||
|
|
||||||
def add_episode(self, *args):
|
def add_episode(self, *args):
|
||||||
query = (
|
|
||||||
'''
|
|
||||||
INSERT INTO episode(
|
|
||||||
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
|
|
||||||
idShow, c15, c16)
|
|
||||||
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
'''
|
|
||||||
)
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def add_episode_16(self, *args):
|
|
||||||
query = (
|
query = (
|
||||||
'''
|
'''
|
||||||
INSERT INTO episode(
|
INSERT INTO episode(
|
||||||
|
@ -213,16 +200,6 @@ class KodiTVShows(KodiItems):
|
||||||
self.cursor.execute(query, (args))
|
self.cursor.execute(query, (args))
|
||||||
|
|
||||||
def update_episode(self, *args):
|
def update_episode(self, *args):
|
||||||
query = ' '.join((
|
|
||||||
|
|
||||||
"UPDATE episode",
|
|
||||||
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
|
|
||||||
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idShow = ?",
|
|
||||||
"WHERE idEpisode = ?"
|
|
||||||
))
|
|
||||||
self.cursor.execute(query, (args))
|
|
||||||
|
|
||||||
def update_episode_16(self, *args):
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
"UPDATE episode",
|
"UPDATE episode",
|
||||||
|
|
|
@ -250,28 +250,18 @@ class Movies(Items):
|
||||||
if update_item:
|
if update_item:
|
||||||
log.info("UPDATE movie itemid: %s - Title: %s", itemid, title)
|
log.info("UPDATE movie itemid: %s - Title: %s", itemid, title)
|
||||||
|
|
||||||
# update new ratings Kodi 17
|
# update ratings
|
||||||
if self.kodi_version >= 17:
|
ratingid = self.kodi_db.get_ratingid(movieid)
|
||||||
ratingid = self.kodi_db.get_ratingid(movieid)
|
self.kodi_db.update_ratings(movieid, "movie", "default", rating, votecount, ratingid)
|
||||||
|
|
||||||
self.kodi_db.update_ratings(movieid, "movie", "default", rating, votecount, ratingid)
|
# update uniqueid
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid(movieid)
|
||||||
# update new uniqueid Kodi 17
|
self.kodi_db.update_uniqueid(movieid, "movie", imdb, "imdb", uniqueid)
|
||||||
if self.kodi_version >= 17:
|
|
||||||
uniqueid = self.kodi_db.get_uniqueid(movieid)
|
|
||||||
|
|
||||||
self.kodi_db.update_uniqueid(movieid, "movie", imdb, "imdb", uniqueid)
|
|
||||||
|
|
||||||
# Update the movie entry
|
# Update the movie entry
|
||||||
if self.kodi_version >= 17:
|
self.kodi_db.update_movie(title, plot, shortplot, tagline, votecount, uniqueid,
|
||||||
self.kodi_db.update_movie_17(title, plot, shortplot, tagline, votecount, uniqueid,
|
writer, year, uniqueid, sorttitle, runtime, mpaa, genre,
|
||||||
writer, year, uniqueid, sorttitle, runtime, mpaa, genre,
|
director, title, studio, trailer, country, year, movieid)
|
||||||
director, title, studio, trailer, country, year,
|
|
||||||
movieid)
|
|
||||||
else:
|
|
||||||
self.kodi_db.update_movie(title, plot, shortplot, tagline, votecount, rating,
|
|
||||||
writer, year, imdb, sorttitle, runtime, mpaa, genre,
|
|
||||||
director, title, studio, trailer, country, movieid)
|
|
||||||
|
|
||||||
# Update the checksum in emby table
|
# Update the checksum in emby table
|
||||||
emby_db.updateReference(itemid, checksum)
|
emby_db.updateReference(itemid, checksum)
|
||||||
|
@ -280,17 +270,13 @@ class Movies(Items):
|
||||||
else:
|
else:
|
||||||
log.info("ADD movie itemid: %s - Title: %s", itemid, title)
|
log.info("ADD movie itemid: %s - Title: %s", itemid, title)
|
||||||
|
|
||||||
# add new ratings Kodi 17
|
# Add ratings
|
||||||
if self.kodi_version >= 17:
|
ratingid = self.kodi_db.create_entry_rating()
|
||||||
ratingid = self.kodi_db.create_entry_rating()
|
self.kodi_db.add_ratings(ratingid, movieid, "movie", "default", rating, votecount)
|
||||||
|
|
||||||
self.kodi_db.add_ratings(ratingid, movieid, "movie", "default", rating, votecount)
|
# Add uniqueid
|
||||||
|
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||||
# add new uniqueid Kodi 17
|
self.kodi_db.add_uniqueid(uniqueid, movieid, "movie", imdb, "imdb")
|
||||||
if self.kodi_version >= 17:
|
|
||||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
|
||||||
|
|
||||||
self.kodi_db.add_uniqueid(uniqueid, movieid, "movie", imdb, "imdb")
|
|
||||||
|
|
||||||
# Add path
|
# Add path
|
||||||
pathid = self.kodi_db.add_path(path)
|
pathid = self.kodi_db.add_path(path)
|
||||||
|
@ -298,16 +284,10 @@ class Movies(Items):
|
||||||
fileid = self.kodi_db.add_file(filename, pathid)
|
fileid = self.kodi_db.add_file(filename, pathid)
|
||||||
|
|
||||||
# Create the movie entry
|
# Create the movie entry
|
||||||
if self.kodi_version >= 17:
|
self.kodi_db.add_movie(movieid, fileid, title, plot, shortplot, tagline,
|
||||||
self.kodi_db.add_movie_17(movieid, fileid, title, plot, shortplot, tagline,
|
votecount, uniqueid, writer, year, uniqueid, sorttitle,
|
||||||
votecount, uniqueid, writer, year, uniqueid, sorttitle,
|
runtime, mpaa, genre, director, title, studio, trailer,
|
||||||
runtime, mpaa, genre, director, title, studio, trailer,
|
country, year)
|
||||||
country, year)
|
|
||||||
else:
|
|
||||||
self.kodi_db.add_movie(movieid, fileid, title, plot, shortplot, tagline,
|
|
||||||
votecount, rating, writer, year, imdb, sorttitle,
|
|
||||||
runtime, mpaa, genre, director, title, studio, trailer,
|
|
||||||
country)
|
|
||||||
|
|
||||||
# Create the reference in emby table
|
# Create the reference in emby table
|
||||||
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None,
|
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None,
|
||||||
|
|
|
@ -303,22 +303,8 @@ class Music(Items):
|
||||||
emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum)
|
emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum)
|
||||||
|
|
||||||
# Process the album info
|
# Process the album info
|
||||||
if self.kodi_version in [17,18]:
|
self.kodi_db.update_album(artistname, year, genre, bio, thumb, rating, lastScraped,
|
||||||
# Kodi Krypton/Leia
|
"album", albumid)
|
||||||
self.kodi_db.update_album_17(artistname, year, genre, bio, thumb, rating, lastScraped,
|
|
||||||
"album", albumid)
|
|
||||||
elif self.kodi_version == 16:
|
|
||||||
# Kodi Jarvis
|
|
||||||
self.kodi_db.update_album(artistname, year, genre, bio, thumb, rating, lastScraped,
|
|
||||||
"album", albumid)
|
|
||||||
elif self.kodi_version == 15:
|
|
||||||
# Kodi Isengard
|
|
||||||
self.kodi_db.update_album_15(artistname, year, genre, bio, thumb, rating, lastScraped,
|
|
||||||
dateadded, "album", albumid)
|
|
||||||
else:
|
|
||||||
# TODO: Remove Helix code when Krypton is RC
|
|
||||||
self.kodi_db.update_album_14(artistname, year, genre, bio, thumb, rating, lastScraped,
|
|
||||||
dateadded, albumid)
|
|
||||||
|
|
||||||
# Assign main artists to album
|
# Assign main artists to album
|
||||||
for artist in item['AlbumArtists']:
|
for artist in item['AlbumArtists']:
|
||||||
|
|
|
@ -9,7 +9,7 @@ import api
|
||||||
import embydb_functions as embydb
|
import embydb_functions as embydb
|
||||||
import _kodi_tvshows
|
import _kodi_tvshows
|
||||||
from _common import Items, catch_except
|
from _common import Items, catch_except
|
||||||
from utils import window, settings, language as lang, plugin_path
|
from utils import window, settings, language as lang, urllib_path
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -234,11 +234,11 @@ class TVShows(Items):
|
||||||
artwork = self.artwork
|
artwork = self.artwork
|
||||||
API = api.API(item)
|
API = api.API(item)
|
||||||
|
|
||||||
# Server api changed or something? RecursiveItemCount always returns 0
|
# If the show is empty, try to remove it.
|
||||||
"""if settings('syncEmptyShows') == "false" and not item.get('RecursiveItemCount'):
|
if settings('syncEmptyShows') == "false" and not item.get('RecursiveItemCount'):
|
||||||
if item.get('Name', None) is not None:
|
log.info("Skipping empty show: %s", item.get('Name', item['Id']))
|
||||||
log.info("Skipping empty show: %s", item['Name'])
|
return self.remove(item['Id'])
|
||||||
return"""
|
|
||||||
# If the item already exist in the local Kodi DB we'll perform a full item update
|
# If the item already exist in the local Kodi DB we'll perform a full item update
|
||||||
# If the item doesn't exist, we'll add it to the database
|
# If the item doesn't exist, we'll add it to the database
|
||||||
update_item = True
|
update_item = True
|
||||||
|
@ -308,12 +308,8 @@ class TVShows(Items):
|
||||||
|
|
||||||
if self.emby_db.get_view_grouped_series(viewid) and tvdb:
|
if self.emby_db.get_view_grouped_series(viewid) and tvdb:
|
||||||
# search kodi db for same provider id
|
# search kodi db for same provider id
|
||||||
if self.kodi_version > 16:
|
query = "SELECT idShow FROM tvshow_view WHERE uniqueid_value = ?"
|
||||||
query = "SELECT idShow FROM tvshow_view WHERE uniqueid_value = ?"
|
kodicursor.execute(query, (tvdb,))
|
||||||
kodicursor.execute(query, (tvdb,))
|
|
||||||
else:
|
|
||||||
query = "SELECT idShow FROM tvshow WHERE C12 = ?"
|
|
||||||
kodicursor.execute(query, (tvdb,))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
temps_showid = kodicursor.fetchall()
|
temps_showid = kodicursor.fetchall()
|
||||||
|
@ -342,51 +338,22 @@ class TVShows(Items):
|
||||||
force_episodes = True
|
force_episodes = True
|
||||||
|
|
||||||
|
|
||||||
# Verify series pooling
|
|
||||||
"""if not update_item and tvdb:
|
|
||||||
query = "SELECT idShow FROM tvshow WHERE C12 = ?"
|
|
||||||
kodicursor.execute(query, (tvdb,))
|
|
||||||
try:
|
|
||||||
temp_showid = kodicursor.fetchone()[0]
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
emby_other = emby_db.getItem_byKodiId(temp_showid, "tvshow")
|
|
||||||
if emby_other and viewid == emby_other[2]:
|
|
||||||
log.info("Applying series pooling for %s", title)
|
|
||||||
emby_other_item = emby_db.getItem_byId(emby_other[0])
|
|
||||||
showid = emby_other_item[0]
|
|
||||||
pathid = emby_other_item[2]
|
|
||||||
log.info("showid: %s pathid: %s", showid, pathid)
|
|
||||||
# Create the reference in emby table
|
|
||||||
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
|
|
||||||
checksum=checksum, mediafolderid=viewid)
|
|
||||||
update_item = True"""
|
|
||||||
|
|
||||||
|
|
||||||
##### UPDATE THE TVSHOW #####
|
##### UPDATE THE TVSHOW #####
|
||||||
if update_item:
|
if update_item:
|
||||||
log.info("UPDATE tvshow itemid: %s - Title: %s", itemid, title)
|
log.info("UPDATE tvshow itemid: %s - Title: %s", itemid, title)
|
||||||
|
|
||||||
# update new ratings Kodi 17
|
# update ratings
|
||||||
if self.kodi_version > 16:
|
ratingid = self.kodi_db.get_ratingid("tvshow", showid)
|
||||||
ratingid = self.kodi_db.get_ratingid("tvshow", showid)
|
self.kodi_db.update_ratings(showid, "tvshow", "default", rating, votecount,ratingid)
|
||||||
|
|
||||||
self.kodi_db.update_ratings(showid, "tvshow", "default", rating, votecount,ratingid)
|
# update uniqueid
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid("tvshow", showid)
|
||||||
# update new uniqueid Kodi 17
|
self.kodi_db.update_uniqueid(showid, "tvshow", tvdb, "unknown", uniqueid)
|
||||||
if self.kodi_version > 16:
|
|
||||||
uniqueid = self.kodi_db.get_uniqueid("tvshow", showid)
|
|
||||||
|
|
||||||
self.kodi_db.update_uniqueid(showid, "tvshow", tvdb, "unknown", uniqueid)
|
|
||||||
|
|
||||||
# Update the tvshow entry
|
# Update the tvshow entry
|
||||||
if self.kodi_version > 16:
|
self.kodi_db.update_tvshow(title, plot, uniqueid, premieredate, genre, title,
|
||||||
self.kodi_db.update_tvshow(title, plot, uniqueid, premieredate, genre, title,
|
uniqueid, mpaa, studio, sorttitle, showid)
|
||||||
uniqueid, mpaa, studio, sorttitle, showid)
|
|
||||||
else:
|
|
||||||
self.kodi_db.update_tvshow(title, plot, rating, premieredate, genre, title,
|
|
||||||
tvdb, mpaa, studio, sorttitle, showid)
|
|
||||||
# Update the checksum in emby table
|
# Update the checksum in emby table
|
||||||
emby_db.updateReference(itemid, checksum)
|
emby_db.updateReference(itemid, checksum)
|
||||||
|
|
||||||
|
@ -394,17 +361,13 @@ class TVShows(Items):
|
||||||
else:
|
else:
|
||||||
log.info("ADD tvshow itemid: %s - Title: %s", itemid, title)
|
log.info("ADD tvshow itemid: %s - Title: %s", itemid, title)
|
||||||
|
|
||||||
# add new ratings Kodi 17
|
# add ratings
|
||||||
if self.kodi_version > 16:
|
ratingid = self.kodi_db.create_entry_rating()
|
||||||
ratingid = self.kodi_db.create_entry_rating()
|
self.kodi_db.add_ratings(ratingid, showid, "tvshow", "default", rating, votecount)
|
||||||
|
|
||||||
self.kodi_db.add_ratings(ratingid, showid, "tvshow", "default", rating, votecount)
|
# add uniqueid
|
||||||
|
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||||
# add new uniqueid Kodi 17
|
self.kodi_db.add_uniqueid(uniqueid, showid, "tvshow", tvdb, "unknown")
|
||||||
if self.kodi_version > 16:
|
|
||||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
|
||||||
|
|
||||||
self.kodi_db.add_uniqueid(uniqueid, showid, "tvshow", tvdb, "unknown")
|
|
||||||
|
|
||||||
# Add top path
|
# Add top path
|
||||||
toppathid = self.kodi_db.add_path(toplevelpath)
|
toppathid = self.kodi_db.add_path(toplevelpath)
|
||||||
|
@ -414,12 +377,8 @@ class TVShows(Items):
|
||||||
pathid = self.kodi_db.add_path(path)
|
pathid = self.kodi_db.add_path(path)
|
||||||
|
|
||||||
# Create the tvshow entry
|
# Create the tvshow entry
|
||||||
if self.kodi_version > 16:
|
self.kodi_db.add_tvshow(showid, title, plot, uniqueid, premieredate, genre,
|
||||||
self.kodi_db.add_tvshow(showid, title, plot, uniqueid, premieredate, genre,
|
title, uniqueid, mpaa, studio, sorttitle)
|
||||||
title, uniqueid, mpaa, studio, sorttitle)
|
|
||||||
else:
|
|
||||||
self.kodi_db.add_tvshow(showid, title, plot, rating, premieredate, genre,
|
|
||||||
title, tvdb, mpaa, studio, sorttitle)
|
|
||||||
|
|
||||||
# Create the reference in emby table
|
# Create the reference in emby table
|
||||||
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
|
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
|
||||||
|
@ -629,39 +588,24 @@ class TVShows(Items):
|
||||||
'dbid': episodeid,
|
'dbid': episodeid,
|
||||||
'mode': "play"
|
'mode': "play"
|
||||||
}
|
}
|
||||||
filename = plugin_path(path, params)
|
filename = urllib_path(path, params)
|
||||||
|
|
||||||
##### UPDATE THE EPISODE #####
|
##### UPDATE THE EPISODE #####
|
||||||
if update_item:
|
if update_item:
|
||||||
log.info("UPDATE episode itemid: %s - Title: %s", itemid, title)
|
log.info("UPDATE episode itemid: %s - Title: %s", itemid, title)
|
||||||
|
|
||||||
# update new ratings Kodi 17
|
# update ratings
|
||||||
if self.kodi_version >= 17:
|
ratingid = self.kodi_db.get_ratingid("episode", episodeid)
|
||||||
ratingid = self.kodi_db.get_ratingid("episode", episodeid)
|
self.kodi_db.update_ratings(episodeid, "episode", "default", rating, votecount, ratingid)
|
||||||
|
|
||||||
self.kodi_db.update_ratings(episodeid, "episode", "default", rating, votecount,ratingid)
|
# update uniqueid
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid("episode", episodeid)
|
||||||
# update new uniqueid Kodi 17
|
self.kodi_db.update_uniqueid(episodeid, "episode", tvdb, "tvdb", uniqueid)
|
||||||
if self.kodi_version >= 17:
|
|
||||||
uniqueid = self.kodi_db.get_uniqueid("episode", episodeid)
|
|
||||||
|
|
||||||
self.kodi_db.update_uniqueid(episodeid, "episode", tvdb, "tvdb",uniqueid)
|
|
||||||
|
|
||||||
# Update the episode entry
|
# Update the episode entry
|
||||||
if self.kodi_version >= 17:
|
self.kodi_db.update_episode(title, plot, uniqueid, writer, premieredate, runtime,
|
||||||
# Kodi Krypton
|
director, season, episode, title, airsBeforeSeason,
|
||||||
self.kodi_db.update_episode_16(title, plot, uniqueid, writer, premieredate, runtime,
|
airsBeforeEpisode, seasonid, showid, episodeid)
|
||||||
director, season, episode, title, airsBeforeSeason,
|
|
||||||
airsBeforeEpisode, seasonid, showid, episodeid)
|
|
||||||
elif self.kodi_version >= 16 and self.kodi_version < 17:
|
|
||||||
# Kodi Jarvis
|
|
||||||
self.kodi_db.update_episode_16(title, plot, rating, writer, premieredate, runtime,
|
|
||||||
director, season, episode, title, airsBeforeSeason,
|
|
||||||
airsBeforeEpisode, seasonid, showid, episodeid)
|
|
||||||
else:
|
|
||||||
self.kodi_db.update_episode(title, plot, rating, writer, premieredate, runtime,
|
|
||||||
director, season, episode, title, airsBeforeSeason,
|
|
||||||
airsBeforeEpisode, showid, episodeid)
|
|
||||||
|
|
||||||
# Update the checksum in emby table
|
# Update the checksum in emby table
|
||||||
emby_db.updateReference(itemid, checksum)
|
emby_db.updateReference(itemid, checksum)
|
||||||
|
@ -672,17 +616,13 @@ class TVShows(Items):
|
||||||
else:
|
else:
|
||||||
log.info("ADD episode itemid: %s - Title: %s", itemid, title)
|
log.info("ADD episode itemid: %s - Title: %s", itemid, title)
|
||||||
|
|
||||||
# add new ratings Kodi 17
|
# add ratings
|
||||||
if self.kodi_version >= 17:
|
ratingid = self.kodi_db.create_entry_rating()
|
||||||
ratingid = self.kodi_db.create_entry_rating()
|
self.kodi_db.add_ratings(ratingid, episodeid, "episode", "default", rating, votecount)
|
||||||
|
|
||||||
self.kodi_db.add_ratings(ratingid, episodeid, "episode", "default", rating, votecount)
|
# add uniqueid
|
||||||
|
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||||
# add new uniqueid Kodi 17
|
self.kodi_db.add_uniqueid(uniqueid, episodeid, "episode", tvdb, "tvdb")
|
||||||
if self.kodi_version >= 17:
|
|
||||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
|
||||||
|
|
||||||
self.kodi_db.add_uniqueid(uniqueid, episodeid, "episode", tvdb, "tvdb")
|
|
||||||
|
|
||||||
# Add path
|
# Add path
|
||||||
pathid = self.kodi_db.add_path(path)
|
pathid = self.kodi_db.add_path(path)
|
||||||
|
@ -690,20 +630,9 @@ class TVShows(Items):
|
||||||
fileid = self.kodi_db.add_file(filename, pathid)
|
fileid = self.kodi_db.add_file(filename, pathid)
|
||||||
|
|
||||||
# Create the episode entry
|
# Create the episode entry
|
||||||
if self.kodi_version >= 17:
|
self.kodi_db.add_episode(episodeid, fileid, title, plot, uniqueid, writer,
|
||||||
# Kodi Krypton
|
premieredate, runtime, director, season, episode, title,
|
||||||
self.kodi_db.add_episode_16(episodeid, fileid, title, plot, uniqueid, writer,
|
showid, airsBeforeSeason, airsBeforeEpisode, seasonid)
|
||||||
premieredate, runtime, director, season, episode, title,
|
|
||||||
showid, airsBeforeSeason, airsBeforeEpisode, seasonid)
|
|
||||||
elif self.kodi_version >= 16 and self.kodi_version < 17:
|
|
||||||
# Kodi Jarvis
|
|
||||||
self.kodi_db.add_episode_16(episodeid, fileid, title, plot, rating, writer,
|
|
||||||
premieredate, runtime, director, season, episode, title,
|
|
||||||
showid, airsBeforeSeason, airsBeforeEpisode, seasonid)
|
|
||||||
else:
|
|
||||||
self.kodi_db.add_episode(episodeid, fileid, title, plot, rating, writer,
|
|
||||||
premieredate, runtime, director, season, episode, title,
|
|
||||||
showid, airsBeforeSeason, airsBeforeEpisode)
|
|
||||||
|
|
||||||
# Create the reference in emby table
|
# Create the reference in emby table
|
||||||
emby_db.addReference(itemid, episodeid, "Episode", "episode", fileid, pathid,
|
emby_db.addReference(itemid, episodeid, "Episode", "episode", fileid, pathid,
|
||||||
|
|
|
@ -21,6 +21,8 @@ import playutils as putils
|
||||||
import playlist
|
import playlist
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
import shutil
|
import shutil
|
||||||
|
import embydb_functions as embydb
|
||||||
|
from database import DatabaseConn
|
||||||
from utils import window, settings, language as lang
|
from utils import window, settings, language as lang
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
@ -30,329 +32,147 @@ log = logging.getLogger("EMBY."+__name__)
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
class PlaybackUtils():
|
class PlaybackUtils(object):
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, item):
|
|
||||||
|
|
||||||
self.item = item
|
|
||||||
self.API = api.API(self.item)
|
|
||||||
|
|
||||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
def __init__(self, item=None, item_id=None):
|
||||||
|
|
||||||
self.userid = window('emby_currUser')
|
|
||||||
self.server = window('emby_server%s' % self.userid)
|
|
||||||
|
|
||||||
self.artwork = artwork.Artwork()
|
self.artwork = artwork.Artwork()
|
||||||
self.emby = embyserver.Read_EmbyServer()
|
self.emby = embyserver.Read_EmbyServer()
|
||||||
self.pl = playlist.Playlist()
|
|
||||||
|
|
||||||
|
self.item = item or self.emby.getItem(item_id)
|
||||||
|
self.API = api.API(self.item)
|
||||||
|
|
||||||
def play(self, itemid, dbid=None):
|
self.server = window('emby_server%s' % window('emby_currUser'))
|
||||||
|
|
||||||
|
self.stack = []
|
||||||
|
|
||||||
|
if self.item['Type'] == "Audio":
|
||||||
|
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
||||||
|
else:
|
||||||
|
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
|
||||||
|
def play(self, item_id, dbid=None, force_transcode=False):
|
||||||
|
|
||||||
listitem = xbmcgui.ListItem()
|
listitem = xbmcgui.ListItem()
|
||||||
playutils = putils.PlayUtils(self.item)
|
|
||||||
|
|
||||||
log.info("Play called.")
|
log.info("Play called: %s", self.item['Name'])
|
||||||
playurl = playutils.getPlayUrl()
|
|
||||||
if not playurl:
|
resume = window('emby.resume')
|
||||||
|
window('emby.resume', clear=True)
|
||||||
|
|
||||||
|
play_url = putils.PlayUtils(self.item, listitem).get_play_url(force_transcode)
|
||||||
|
|
||||||
|
if not play_url:
|
||||||
|
if play_url == False: # User backed-out of menu
|
||||||
|
self.playlist.clear()
|
||||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||||
|
|
||||||
if dbid is None:
|
seektime = 0 if resume == "true" else self.API.adjust_resume(self.API.get_userdata()['Resume'])
|
||||||
# Item is not in Kodi database
|
|
||||||
listitem.setPath(playurl)
|
|
||||||
self.setProperties(playurl, listitem)
|
|
||||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
|
||||||
|
|
||||||
# TODO: Review once Krypton is RC, no need for workaround.
|
if force_transcode:
|
||||||
|
log.info("Clear the playlist.")
|
||||||
|
self.playlist.clear()
|
||||||
|
|
||||||
############### ORGANIZE CURRENT PLAYLIST ################
|
self.set_playlist(play_url, item_id, listitem, seektime, dbid)
|
||||||
|
|
||||||
homeScreen = xbmc.getCondVisibility('Window.IsActive(home)')
|
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
|
||||||
startPos = max(playlist.getposition(), 0) # Can return -1
|
|
||||||
sizePlaylist = playlist.size()
|
|
||||||
currentPosition = startPos
|
|
||||||
|
|
||||||
propertiesPlayback = window('emby_playbackProps') == "true"
|
##### SETUP PLAYBACK
|
||||||
introsPlaylist = False
|
|
||||||
dummyPlaylist = False
|
|
||||||
|
|
||||||
log.debug("Playlist start position: %s" % startPos)
|
''' To get everything to work together, play the first item in the stack with setResolvedUrl,
|
||||||
log.debug("Playlist plugin position: %s" % currentPosition)
|
add the rest to the regular playlist.
|
||||||
log.debug("Playlist size: %s" % sizePlaylist)
|
'''
|
||||||
|
|
||||||
############### RESUME POINT ################
|
index = max(self.playlist.getposition(), 0) + 1 # Can return -1
|
||||||
|
force_play = False
|
||||||
userdata = self.API.get_userdata()
|
|
||||||
seektime = self.API.adjust_resume(userdata['Resume'])
|
|
||||||
|
|
||||||
# We need to ensure we add the intro and additional parts only once.
|
# Stack: [(url, listitem), (url, ...), ...]
|
||||||
# Otherwise we get a loop.
|
self.stack[0][1].setPath(self.stack[0][0])
|
||||||
if not propertiesPlayback:
|
|
||||||
|
|
||||||
window('emby_playbackProps', value="true")
|
|
||||||
log.info("Setting up properties in playlist.")
|
|
||||||
|
|
||||||
if not homeScreen and not seektime and window('emby_customPlaylist') != "true":
|
|
||||||
|
|
||||||
log.debug("Adding dummy file to playlist.")
|
|
||||||
dummyPlaylist = True
|
|
||||||
playlist.add(playurl, listitem, index=startPos)
|
|
||||||
# Remove the original item from playlist
|
|
||||||
self.pl.remove_from_playlist(startPos+1)
|
|
||||||
# Readd the original item to playlist - via jsonrpc so we have full metadata
|
|
||||||
self.pl.insert_to_playlist(currentPosition+1, dbid, self.item['Type'].lower())
|
|
||||||
currentPosition += 1
|
|
||||||
|
|
||||||
############### -- CHECK FOR INTROS ################
|
|
||||||
|
|
||||||
if settings('enableCinema') == "true" and not seektime:
|
|
||||||
# if we have any play them when the movie/show is not being resumed
|
|
||||||
url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid
|
|
||||||
intros = self.doUtils(url)
|
|
||||||
|
|
||||||
if intros['TotalRecordCount'] != 0:
|
|
||||||
getTrailers = True
|
|
||||||
|
|
||||||
if settings('askCinema') == "true":
|
|
||||||
resp = xbmcgui.Dialog().yesno("Emby for Kodi", lang(33016))
|
|
||||||
if not resp:
|
|
||||||
# User selected to not play trailers
|
|
||||||
getTrailers = False
|
|
||||||
log.info("Skip trailers.")
|
|
||||||
|
|
||||||
if getTrailers:
|
|
||||||
for intro in intros['Items']:
|
|
||||||
# The server randomly returns intros, process them.
|
|
||||||
introListItem = xbmcgui.ListItem()
|
|
||||||
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
|
|
||||||
log.info("Adding Intro: %s" % introPlayurl)
|
|
||||||
|
|
||||||
# Set listitem and properties for intros
|
|
||||||
pbutils = PlaybackUtils(intro)
|
|
||||||
pbutils.setProperties(introPlayurl, introListItem)
|
|
||||||
|
|
||||||
self.pl.insert_to_playlist(currentPosition, url=introPlayurl)
|
|
||||||
introsPlaylist = True
|
|
||||||
currentPosition += 1
|
|
||||||
|
|
||||||
|
|
||||||
############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ###############
|
|
||||||
|
|
||||||
if homeScreen and not seektime and not sizePlaylist:
|
|
||||||
# Extend our current playlist with the actual item to play
|
|
||||||
# only if there's no playlist first
|
|
||||||
log.info("Adding main item to playlist.")
|
|
||||||
self.pl.add_to_playlist(dbid, self.item['Type'].lower())
|
|
||||||
|
|
||||||
# Ensure that additional parts are played after the main item
|
|
||||||
currentPosition += 1
|
|
||||||
|
|
||||||
############### -- CHECK FOR ADDITIONAL PARTS ################
|
|
||||||
|
|
||||||
if self.item.get('PartCount'):
|
|
||||||
# Only add to the playlist after intros have played
|
|
||||||
partcount = self.item['PartCount']
|
|
||||||
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
|
|
||||||
parts = self.doUtils(url)
|
|
||||||
for part in parts['Items']:
|
|
||||||
|
|
||||||
additionalListItem = xbmcgui.ListItem()
|
|
||||||
additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
|
|
||||||
log.info("Adding additional part: %s" % partcount)
|
|
||||||
|
|
||||||
# Set listitem and properties for each additional parts
|
|
||||||
pbutils = PlaybackUtils(part)
|
|
||||||
pbutils.setProperties(additionalPlayurl, additionalListItem)
|
|
||||||
pbutils.setArtwork(additionalListItem)
|
|
||||||
|
|
||||||
playlist.add(additionalPlayurl, additionalListItem, index=currentPosition)
|
|
||||||
self.pl.verify_playlist()
|
|
||||||
currentPosition += 1
|
|
||||||
|
|
||||||
if dummyPlaylist:
|
|
||||||
# Added a dummy file to the playlist,
|
|
||||||
# because the first item is going to fail automatically.
|
|
||||||
log.info("Processed as a playlist. First item is skipped.")
|
|
||||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
|
||||||
|
|
||||||
|
|
||||||
# We just skipped adding properties. Reset flag for next time.
|
|
||||||
elif propertiesPlayback:
|
|
||||||
log.debug("Resetting properties playback flag.")
|
|
||||||
window('emby_playbackProps', clear=True)
|
|
||||||
|
|
||||||
#self.pl.verify_playlist()
|
|
||||||
########## SETUP MAIN ITEM ##########
|
|
||||||
|
|
||||||
# For transcoding only, ask for audio/subs pref
|
|
||||||
if window('emby_%s.playmethod' % playurl) == "Transcode":
|
|
||||||
# Filter ISO since Emby does not probe anymore
|
|
||||||
if self.item.get('VideoType') == "Iso":
|
|
||||||
log.info("Skipping audio/subs prompt, ISO detected.")
|
|
||||||
else:
|
|
||||||
playurl = playutils.audioSubsPref(playurl, listitem)
|
|
||||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
|
||||||
|
|
||||||
listitem.setPath(playurl)
|
|
||||||
self.setProperties(playurl, listitem)
|
|
||||||
|
|
||||||
############### PLAYBACK ################
|
|
||||||
|
|
||||||
if homeScreen and seektime and window('emby_customPlaylist') != "true":
|
|
||||||
log.info("Play as a widget item.")
|
|
||||||
self.setListItem(listitem, dbid)
|
|
||||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
|
||||||
|
|
||||||
elif ((introsPlaylist and window('emby_customPlaylist') == "true") or
|
|
||||||
(homeScreen and not sizePlaylist)):
|
|
||||||
# Playlist was created just now, play it.
|
|
||||||
log.info("Play playlist.")
|
|
||||||
xbmc.Player().play(playlist, startpos=startPos)
|
|
||||||
|
|
||||||
else:
|
|
||||||
log.info("Play as a regular item.")
|
|
||||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
|
||||||
|
|
||||||
def setProperties(self, playurl, listitem):
|
|
||||||
|
|
||||||
# Set all properties necessary for plugin path playback
|
|
||||||
itemid = self.item['Id']
|
|
||||||
itemtype = self.item['Type']
|
|
||||||
|
|
||||||
embyitem = "emby_%s" % playurl
|
|
||||||
window('%s.runtime' % embyitem, value=str(self.item.get('RunTimeTicks')))
|
|
||||||
window('%s.type' % embyitem, value=itemtype)
|
|
||||||
window('%s.itemid' % embyitem, value=itemid)
|
|
||||||
|
|
||||||
if itemtype == "Episode":
|
|
||||||
window('%s.refreshid' % embyitem, value=self.item.get('SeriesId'))
|
|
||||||
else:
|
|
||||||
window('%s.refreshid' % embyitem, value=itemid)
|
|
||||||
|
|
||||||
# Append external subtitles to stream
|
|
||||||
playmethod = window('%s.playmethod' % embyitem)
|
|
||||||
# Only for direct stream
|
|
||||||
if playmethod in ("DirectStream") and settings('enableExternalSubs') == "true":
|
|
||||||
# Direct play automatically appends external
|
|
||||||
subtitles = self.externalSubs(playurl)
|
|
||||||
listitem.setSubtitles(subtitles)
|
|
||||||
|
|
||||||
self.setArtwork(listitem)
|
|
||||||
|
|
||||||
def externalSubs(self, playurl):
|
|
||||||
|
|
||||||
externalsubs = []
|
|
||||||
mapping = {}
|
|
||||||
|
|
||||||
itemid = self.item['Id']
|
|
||||||
try:
|
try:
|
||||||
mediastreams = self.item['MediaSources'][0]['MediaStreams']
|
#if not xbmc.getCondVisibility('Window.IsVisible(MyVideoNav.xml)'): # Causes infinite loop with play from here
|
||||||
except (TypeError, KeyError, IndexError):
|
if xbmc.getCondVisibility('Window.IsVisible(10000).xml'):
|
||||||
return
|
# widgets do not fill artwork correctly
|
||||||
|
log.info("Detected widget.")
|
||||||
|
raise IndexError
|
||||||
|
|
||||||
temp = xbmc.translatePath(
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1])
|
||||||
"special://profile/addon_data/plugin.video.emby/temp/").decode('utf-8')
|
self.stack.pop(0) # remove the first item we just started.
|
||||||
|
except IndexError:
|
||||||
|
log.info("Playback activated via the context menu or widgets.")
|
||||||
|
force_play = True
|
||||||
|
self.stack[0][1].setProperty('StartOffset', str(seektime))
|
||||||
|
|
||||||
kodiindex = 0
|
for stack in self.stack:
|
||||||
for stream in mediastreams:
|
self.playlist.add(url=stack[0], listitem=stack[1], index=index)
|
||||||
|
index += 1
|
||||||
|
|
||||||
index = stream['Index']
|
if force_play:
|
||||||
# Since Emby returns all possible tracks together, have to pull only external subtitles.
|
xbmc.Player().play(self.playlist)
|
||||||
# IsTextSubtitleStream if true, is available to download from emby.
|
|
||||||
if (stream['Type'] == "Subtitle" and
|
|
||||||
stream['IsExternal'] and stream['IsTextSubtitleStream']):
|
|
||||||
|
|
||||||
# Direct stream
|
def set_playlist(self, play_url, item_id, listitem, seektime=None, db_id=None):
|
||||||
url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.%s"
|
|
||||||
% (self.server, itemid, itemid, index, stream['Codec']))
|
|
||||||
|
|
||||||
if "Language" in stream:
|
##### CHECK FOR INTROS
|
||||||
|
|
||||||
filename = "Stream.%s.%s" % (stream['Language'], stream['Codec'])
|
|
||||||
try:
|
|
||||||
path = self._download_external_subs(url, temp, filename)
|
|
||||||
externalsubs.append(path)
|
|
||||||
except Exception as e:
|
|
||||||
log.error(e)
|
|
||||||
externalsubs.append(url)
|
|
||||||
else:
|
|
||||||
externalsubs.append(url)
|
|
||||||
|
|
||||||
# map external subtitles for mapping
|
|
||||||
mapping[kodiindex] = index
|
|
||||||
kodiindex += 1
|
|
||||||
|
|
||||||
mapping = json.dumps(mapping)
|
|
||||||
window('emby_%s.indexMapping' % playurl, value=mapping)
|
|
||||||
|
|
||||||
return externalsubs
|
if settings('enableCinema') == "true" and not seektime:
|
||||||
|
self._set_intros(item_id)
|
||||||
|
|
||||||
def _download_external_subs(self, src, dst, filename):
|
##### ADD MAIN ITEM
|
||||||
|
|
||||||
if not xbmcvfs.exists(dst):
|
self.set_properties(play_url, listitem)
|
||||||
xbmcvfs.mkdir(dst)
|
self.set_listitem(listitem, db_id)
|
||||||
|
self.stack.append([play_url, listitem])
|
||||||
|
|
||||||
path = os.path.join(dst, filename)
|
##### ADD ADDITIONAL PARTS
|
||||||
|
|
||||||
try:
|
if self.item.get('PartCount'):
|
||||||
response = requests.get(src, stream=True)
|
self._set_additional_parts(item_id)
|
||||||
response.raise_for_status()
|
|
||||||
except Exception as e:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
response.encoding = 'utf-8'
|
|
||||||
with open(path, 'wb') as f:
|
|
||||||
f.write(response.content)
|
|
||||||
del response
|
|
||||||
|
|
||||||
return path
|
def _set_intros(self, item_id):
|
||||||
|
# if we have any play them when the movie/show is not being resumed
|
||||||
|
intros = self.emby.get_intros(item_id)
|
||||||
|
|
||||||
def setArtwork(self, listItem):
|
if intros['Items']:
|
||||||
# Set up item and item info
|
enabled = True
|
||||||
allartwork = self.artwork.get_all_artwork(self.item, parent_info=True)
|
|
||||||
# Set artwork for listitem
|
|
||||||
arttypes = {
|
|
||||||
|
|
||||||
'poster': "Primary",
|
if settings('askCinema') == "true":
|
||||||
'tvshow.poster': "Primary",
|
|
||||||
'clearart': "Art",
|
|
||||||
'tvshow.clearart': "Art",
|
|
||||||
'clearlogo': "Logo",
|
|
||||||
'tvshow.clearlogo': "Logo",
|
|
||||||
'discart': "Disc",
|
|
||||||
'fanart_image': "Backdrop",
|
|
||||||
'landscape': "Thumb"
|
|
||||||
}
|
|
||||||
for arttype in arttypes:
|
|
||||||
|
|
||||||
art = arttypes[arttype]
|
resp = xbmcgui.Dialog().yesno("Emby for Kodi", lang(33016))
|
||||||
if art == "Backdrop":
|
if not resp:
|
||||||
try: # Backdrop is a list, grab the first backdrop
|
# User selected to not play trailers
|
||||||
self.setArtProp(listItem, arttype, allartwork[art][0])
|
enabled = False
|
||||||
except: pass
|
log.info("Skip trailers.")
|
||||||
else:
|
|
||||||
self.setArtProp(listItem, arttype, allartwork[art])
|
|
||||||
|
|
||||||
def setArtProp(self, listItem, arttype, path):
|
if enabled:
|
||||||
|
for intro in intros['Items']:
|
||||||
if arttype in (
|
|
||||||
'thumb', 'fanart_image', 'small_poster', 'tiny_poster',
|
|
||||||
'medium_landscape', 'medium_poster', 'small_fanartimage',
|
|
||||||
'medium_fanartimage', 'fanart_noindicators'):
|
|
||||||
|
|
||||||
listItem.setProperty(arttype, path)
|
|
||||||
else:
|
|
||||||
listItem.setArt({arttype: path})
|
|
||||||
|
|
||||||
def setListItem(self, listItem, dbid=None):
|
listitem = xbmcgui.ListItem()
|
||||||
|
url = putils.PlayUtils(intro, listitem).get_play_url()
|
||||||
|
log.info("Adding Intro: %s" % url)
|
||||||
|
|
||||||
|
self.stack.append([url, listitem])
|
||||||
|
|
||||||
|
def _set_additional_parts(self, item_id):
|
||||||
|
|
||||||
|
parts = self.emby.get_additional_parts(item_id)
|
||||||
|
|
||||||
|
for part in parts['Items']:
|
||||||
|
|
||||||
|
listitem = xbmcgui.ListItem()
|
||||||
|
url = putils.PlayUtils(part, listitem).get_play_url()
|
||||||
|
log.info("Adding additional part: %s" % url)
|
||||||
|
|
||||||
|
# Set listitem and properties for each additional parts
|
||||||
|
pb = PlaybackUtils(part)
|
||||||
|
pb.set_properties(url, listitem)
|
||||||
|
pb.setArtwork(listitem)
|
||||||
|
|
||||||
|
self.stack.append([url, listitem])
|
||||||
|
|
||||||
|
def set_listitem(self, listitem, dbid=None):
|
||||||
|
|
||||||
people = self.API.get_people()
|
people = self.API.get_people()
|
||||||
studios = self.API.get_studios()
|
mediatype = self.item['Type']
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
|
|
||||||
'title': self.item.get('Name', "Missing name"),
|
'title': self.item.get('Name', "Missing name"),
|
||||||
'year': self.item.get('ProductionYear'),
|
'year': self.item.get('ProductionYear'),
|
||||||
'plot': self.API.get_overview(),
|
'plot': self.API.get_overview(),
|
||||||
|
@ -360,33 +180,151 @@ class PlaybackUtils():
|
||||||
'writer': people.get('Writer'),
|
'writer': people.get('Writer'),
|
||||||
'mpaa': self.API.get_mpaa(),
|
'mpaa': self.API.get_mpaa(),
|
||||||
'genre': " / ".join(self.item['Genres']),
|
'genre': " / ".join(self.item['Genres']),
|
||||||
'studio': " / ".join(studios),
|
'studio': " / ".join(self.API.get_studios()),
|
||||||
'aired': self.API.get_premiere_date(),
|
'aired': self.API.get_premiere_date(),
|
||||||
'rating': self.item.get('CommunityRating'),
|
'rating': self.item.get('CommunityRating'),
|
||||||
'votes': self.item.get('VoteCount')
|
'votes': self.item.get('VoteCount')
|
||||||
}
|
}
|
||||||
|
|
||||||
if "Episode" in self.item['Type']:
|
if mediatype == "Episode":
|
||||||
# Only for tv shows
|
# Only for tv shows
|
||||||
# For Kodi Krypton
|
|
||||||
metadata['mediatype'] = "episode"
|
metadata['mediatype'] = "episode"
|
||||||
metadata['dbid'] = dbid
|
metadata['TVShowTitle'] = self.item.get('SeriesName', "")
|
||||||
|
metadata['season'] = self.item.get('ParentIndexNumber', -1)
|
||||||
|
metadata['episode'] = self.item.get('IndexNumber', -1)
|
||||||
|
|
||||||
thumbId = self.item.get('SeriesId')
|
elif mediatype == "Movie":
|
||||||
season = self.item.get('ParentIndexNumber', -1)
|
|
||||||
episode = self.item.get('IndexNumber', -1)
|
|
||||||
show = self.item.get('SeriesName', "")
|
|
||||||
|
|
||||||
metadata['TVShowTitle'] = show
|
|
||||||
metadata['season'] = season
|
|
||||||
metadata['episode'] = episode
|
|
||||||
|
|
||||||
if "Movie" in self.item['Type']:
|
|
||||||
# For Kodi Krypton
|
|
||||||
metadata['mediatype'] = "movie"
|
metadata['mediatype'] = "movie"
|
||||||
|
|
||||||
|
elif mediatype == "MusicVideo":
|
||||||
|
metadata['mediatype'] = "musicvideo"
|
||||||
|
|
||||||
|
elif mediatype == "Audio":
|
||||||
|
metadata['mediatype'] = "song"
|
||||||
|
|
||||||
|
if dbid:
|
||||||
metadata['dbid'] = dbid
|
metadata['dbid'] = dbid
|
||||||
|
|
||||||
listItem.setProperty('IsPlayable', 'true')
|
listitem.setProperty('IsPlayable', 'true')
|
||||||
listItem.setProperty('IsFolder', 'false')
|
listitem.setProperty('IsFolder', 'false')
|
||||||
listItem.setLabel(metadata['title'])
|
listitem.setLabel(metadata['title'])
|
||||||
listItem.setInfo('video', infoLabels=metadata)
|
listitem.setInfo('Music' if mediatype == "Audio" else 'Video', infoLabels=metadata)
|
||||||
|
|
||||||
|
def set_properties(self, url, listitem):
|
||||||
|
|
||||||
|
# Set all properties necessary for plugin path playback
|
||||||
|
|
||||||
|
item_id = self.item['Id']
|
||||||
|
item_type = self.item['Type']
|
||||||
|
|
||||||
|
play_method = window('emby_%s.playmethod' % url)
|
||||||
|
window('emby_%s.playmethod' % url, clear=True)
|
||||||
|
window('emby_%s.json' % url, {
|
||||||
|
|
||||||
|
'url': url,
|
||||||
|
'runtime': str(self.item.get('RunTimeTicks')),
|
||||||
|
'type': item_type,
|
||||||
|
'id': item_id,
|
||||||
|
'refreshid': self.item.get('SeriesId') if item_type == "Episode" else item_id,
|
||||||
|
'playmethod': play_method
|
||||||
|
})
|
||||||
|
|
||||||
|
self.set_artwork(listitem, item_type)
|
||||||
|
listitem.setCast(self.API.get_actors())
|
||||||
|
|
||||||
|
def set_artwork(self, listitem, item_type):
|
||||||
|
|
||||||
|
all_artwork = self.artwork.get_all_artwork(self.item, parent_info=True)
|
||||||
|
# Set artwork for listitem
|
||||||
|
if item_type == "Episode":
|
||||||
|
art = {
|
||||||
|
'poster': "Series.Primary",
|
||||||
|
'tvshow.poster': "Series.Primary",
|
||||||
|
'clearart': "Art",
|
||||||
|
'tvshow.clearart': "Art",
|
||||||
|
'clearlogo': "Logo",
|
||||||
|
'tvshow.clearlogo': "Logo",
|
||||||
|
'discart': "Disc",
|
||||||
|
'fanart_image': "Backdrop",
|
||||||
|
'landscape': "Thumb",
|
||||||
|
'tvshow.landscape': "Thumb",
|
||||||
|
'thumb': "Primary"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
art = {
|
||||||
|
'poster': "Primary",
|
||||||
|
'clearart': "Art",
|
||||||
|
'clearlogo': "Logo",
|
||||||
|
'discart': "Disc",
|
||||||
|
'fanart_image': "Backdrop",
|
||||||
|
'landscape': "Thumb",
|
||||||
|
'thumb': "Primary"
|
||||||
|
}
|
||||||
|
|
||||||
|
for k_art, e_art in art.items():
|
||||||
|
|
||||||
|
if e_art == "Backdrop" and all_artwork[e_art]:
|
||||||
|
self._set_art(listitem, k_art, all_artwork[e_art][0])
|
||||||
|
else:
|
||||||
|
self._set_art(listitem, k_art, all_artwork.get(e_art))
|
||||||
|
|
||||||
|
def _set_art(self, listitem, art, path):
|
||||||
|
|
||||||
|
if path:
|
||||||
|
if art in ('fanart_image', 'small_poster', 'tiny_poster',
|
||||||
|
'medium_landscape', 'medium_poster', 'small_fanartimage',
|
||||||
|
'medium_fanartimage', 'fanart_noindicators'):
|
||||||
|
|
||||||
|
listitem.setProperty(art, path)
|
||||||
|
else:
|
||||||
|
listitem.setArt({art: path})
|
||||||
|
|
||||||
|
def play_all(self, item_ids, seektime=None, **kwargs):
|
||||||
|
|
||||||
|
self.playlist.clear()
|
||||||
|
started = False
|
||||||
|
|
||||||
|
for item_id in item_ids:
|
||||||
|
|
||||||
|
listitem = xbmcgui.ListItem()
|
||||||
|
db_id = None
|
||||||
|
|
||||||
|
item = self.emby.getItem(item_id)
|
||||||
|
play_url = putils.PlayUtils(item, listitem, **kwargs if item_ids.index(item_id) == 0 else {}).get_play_url()
|
||||||
|
|
||||||
|
if not play_url:
|
||||||
|
log.info("Failed to retrieve playurl")
|
||||||
|
continue
|
||||||
|
|
||||||
|
log.info("Playurl: %s", play_url)
|
||||||
|
|
||||||
|
with DatabaseConn('emby') as cursor:
|
||||||
|
item_db = embydb.Embydb_Functions(cursor).getItem_byId(item_id)
|
||||||
|
db_id = item_db[0] if item_db else None
|
||||||
|
|
||||||
|
pbutils = PlaybackUtils(item)
|
||||||
|
pbutils.set_playlist(play_url, item_id, listitem, seektime if item_ids.index(item_id) == 1 else None, db_id)
|
||||||
|
|
||||||
|
if item_ids.index(item_id) == 1 and seektime:
|
||||||
|
log.info("Seektime detected: %s", self.API.adjust_resume(seektime))
|
||||||
|
listitem.setProperty('StartOffset', str(self.API.adjust_resume(seektime)))
|
||||||
|
|
||||||
|
|
||||||
|
index = max(pbutils.playlist.getposition(), 0) + 1 # Can return -1
|
||||||
|
for stack in pbutils.stack:
|
||||||
|
pbutils.playlist.add(url=stack[0], listitem=stack[1], index=index)
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if not started:
|
||||||
|
started = True
|
||||||
|
|
||||||
|
item = window('emby_%s.json' % play_url)
|
||||||
|
item['forcedaudio'] = kwargs.get('AudioStreamIndex')
|
||||||
|
item['forcedsubs'] = kwargs.get('SubtitleStreamIndex')
|
||||||
|
window('emby_%s.json' % play_url, value=item)
|
||||||
|
|
||||||
|
player = xbmc.Player()
|
||||||
|
player.play(pbutils.playlist)
|
||||||
|
|
||||||
|
if started:
|
||||||
|
return True
|
||||||
|
|
|
@ -12,7 +12,7 @@ import xbmcgui
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
import websocket_client as wsc
|
import websocket_client as wsc
|
||||||
from utils import window, settings, language as lang
|
from utils import window, settings, language as lang, JSONRPC
|
||||||
from ga_client import GoogleAnalytics, log_error
|
from ga_client import GoogleAnalytics, log_error
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
@ -43,6 +43,39 @@ class Player(xbmc.Player):
|
||||||
log.debug("Starting playback monitor.")
|
log.debug("Starting playback monitor.")
|
||||||
xbmc.Player.__init__(self)
|
xbmc.Player.__init__(self)
|
||||||
|
|
||||||
|
def set_audio_subs(self, audio_index=None, subs_index=None):
|
||||||
|
|
||||||
|
''' Only for after playback started
|
||||||
|
'''
|
||||||
|
player = xbmc.Player()
|
||||||
|
log.info("Setting audio: %s subs: %s", audio_index, subs_index)
|
||||||
|
|
||||||
|
if audio_index and len(player.getAvailableAudioStreams()) > 1:
|
||||||
|
player.setAudioStream(audio_index - 1)
|
||||||
|
|
||||||
|
if subs_index:
|
||||||
|
mapping = window('emby_%s.indexMapping.json' % self.current_file)
|
||||||
|
|
||||||
|
if subs_index == -1:
|
||||||
|
player.showSubtitles(False)
|
||||||
|
|
||||||
|
elif mapping:
|
||||||
|
external_index = mapping
|
||||||
|
# If there's external subtitles added via playbackutils
|
||||||
|
for index in external_index:
|
||||||
|
if external_index[index] == subs_index:
|
||||||
|
player.setSubtitleStream(int(index))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# User selected internal subtitles
|
||||||
|
external = len(external_index)
|
||||||
|
audio_tracks = len(player.getAvailableAudioStreams())
|
||||||
|
player.setSubtitleStream(external + subs_index - audio_tracks - 1)
|
||||||
|
else:
|
||||||
|
# Emby merges audio and subtitle index together
|
||||||
|
audio_tracks = len(player.getAvailableAudioStreams())
|
||||||
|
player.setSubtitleStream(subs_index - audio_tracks - 1)
|
||||||
|
|
||||||
@log_error()
|
@log_error()
|
||||||
def onPlayBackStarted(self):
|
def onPlayBackStarted(self):
|
||||||
# Will be called when xbmc starts playing a file
|
# Will be called when xbmc starts playing a file
|
||||||
|
@ -74,28 +107,31 @@ class Player(xbmc.Player):
|
||||||
self.currentFile = currentFile
|
self.currentFile = currentFile
|
||||||
|
|
||||||
# We may need to wait for info to be set in kodi monitor
|
# We may need to wait for info to be set in kodi monitor
|
||||||
itemId = window("emby_%s.itemid" % currentFile)
|
item = window('emby_%s.json' % currentFile)
|
||||||
|
#itemId = window("emby_%s.itemid" % currentFile)
|
||||||
tryCount = 0
|
tryCount = 0
|
||||||
while not itemId:
|
while not item:
|
||||||
|
|
||||||
xbmc.sleep(200)
|
xbmc.sleep(200)
|
||||||
itemId = window("emby_%s.itemid" % currentFile)
|
item = window('emby_%s.json' % currentFile)
|
||||||
if tryCount == 20: # try 20 times or about 10 seconds
|
if tryCount == 20: # try 20 times or about 10 seconds
|
||||||
log.info("Could not find itemId, cancelling playback report...")
|
log.info("Could not find item, cancelling playback report...")
|
||||||
break
|
break
|
||||||
else: tryCount += 1
|
else: tryCount += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId))
|
item_id = item.get('id')
|
||||||
|
log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, item_id))
|
||||||
|
|
||||||
# Only proceed if an itemId was found.
|
# Only proceed if an itemId was found.
|
||||||
embyitem = "emby_%s" % currentFile
|
runtime = item.get('runtime')
|
||||||
runtime = window("%s.runtime" % embyitem)
|
refresh_id = item.get('refreshid')
|
||||||
refresh_id = window("%s.refreshid" % embyitem)
|
play_method = item.get('playmethod')
|
||||||
playMethod = window("%s.playmethod" % embyitem)
|
item_type = item.get('type')
|
||||||
itemType = window("%s.type" % embyitem)
|
|
||||||
window('emby_skipWatched%s' % itemId, value="true")
|
|
||||||
|
|
||||||
|
#self.set_audio_subs(item.get('forcedaudio'), item.get('forcedsubs'))
|
||||||
|
|
||||||
|
window('emby_skipWatched%s' % item_id, value="true")
|
||||||
customseek = window('emby_customPlaylist.seektime')
|
customseek = window('emby_customPlaylist.seektime')
|
||||||
if window('emby_customPlaylist') == "true" and customseek:
|
if window('emby_customPlaylist') == "true" and customseek:
|
||||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||||
|
@ -110,18 +146,7 @@ class Player(xbmc.Player):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get playback volume
|
# Get playback volume
|
||||||
volume_query = {
|
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
|
||||||
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "Application.GetProperties",
|
|
||||||
"params": {
|
|
||||||
|
|
||||||
"properties": ["volume", "muted"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
|
||||||
result = json.loads(result)
|
|
||||||
result = result.get('result')
|
result = result.get('result')
|
||||||
|
|
||||||
volume = result.get('volume')
|
volume = result.get('volume')
|
||||||
|
@ -133,33 +158,26 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
'QueueableMediaTypes': "Video",
|
'QueueableMediaTypes': "Video",
|
||||||
'CanSeek': True,
|
'CanSeek': True,
|
||||||
'ItemId': itemId,
|
'ItemId': item_id,
|
||||||
'MediaSourceId': itemId,
|
'MediaSourceId': item_id,
|
||||||
'PlayMethod': playMethod,
|
'PlayMethod': play_method,
|
||||||
'VolumeLevel': volume,
|
'VolumeLevel': volume,
|
||||||
'PositionTicks': int(seekTime * 10000000),
|
'PositionTicks': int(seekTime * 10000000),
|
||||||
'IsMuted': muted
|
'IsMuted': muted
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the current audio track and subtitles
|
# Get the current audio track and subtitles
|
||||||
if playMethod == "Transcode":
|
if play_method == "Transcode":
|
||||||
# property set in PlayUtils.py
|
# property set in PlayUtils.py
|
||||||
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
||||||
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
||||||
else:
|
else:
|
||||||
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
||||||
tracks_query = {
|
params = {
|
||||||
|
'playerid': 1,
|
||||||
"jsonrpc": "2.0",
|
'properties': ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||||
"id": 1,
|
|
||||||
"method": "Player.GetProperties",
|
|
||||||
"params": {
|
|
||||||
|
|
||||||
"playerid": 1,
|
|
||||||
"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result = xbmc.executeJSONRPC(json.dumps(tracks_query))
|
result = JSONRPC('Player.GetProperties').execute(params)
|
||||||
tracks_data = None
|
tracks_data = None
|
||||||
try:
|
try:
|
||||||
tracks_data = json.loads(result)
|
tracks_data = json.loads(result)
|
||||||
|
@ -190,7 +208,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
# Number of audiotracks to help get Emby Index
|
# Number of audiotracks to help get Emby Index
|
||||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||||
mapping = window("%s.indexMapping" % embyitem)
|
mapping = window("emby_%s.indexMapping" % currentFile)
|
||||||
|
|
||||||
if mapping: # Set in playbackutils.py
|
if mapping: # Set in playbackutils.py
|
||||||
|
|
||||||
|
@ -230,13 +248,13 @@ class Player(xbmc.Player):
|
||||||
data = {
|
data = {
|
||||||
|
|
||||||
'runtime': runtime,
|
'runtime': runtime,
|
||||||
'item_id': itemId,
|
'item_id': item_id,
|
||||||
'refresh_id': refresh_id,
|
'refresh_id': refresh_id,
|
||||||
'currentfile': currentFile,
|
'currentfile': currentFile,
|
||||||
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
||||||
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
||||||
'playmethod': playMethod,
|
'playmethod': play_method,
|
||||||
'Type': itemType,
|
'Type': item_type,
|
||||||
'currentPosition': int(seekTime)
|
'currentPosition': int(seekTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +262,8 @@ class Player(xbmc.Player):
|
||||||
log.info("ADDING_FILE: %s" % self.played_info)
|
log.info("ADDING_FILE: %s" % self.played_info)
|
||||||
|
|
||||||
ga = GoogleAnalytics()
|
ga = GoogleAnalytics()
|
||||||
ga.sendEventData("PlayAction", itemType, playMethod)
|
ga.sendEventData("PlayAction", item_type, play_method)
|
||||||
ga.sendScreenView(itemType)
|
ga.sendScreenView(item_type)
|
||||||
|
|
||||||
def reportPlayback(self):
|
def reportPlayback(self):
|
||||||
|
|
||||||
|
@ -267,18 +285,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
|
|
||||||
# Get playback volume
|
# Get playback volume
|
||||||
volume_query = {
|
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
|
||||||
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "Application.GetProperties",
|
|
||||||
"params": {
|
|
||||||
|
|
||||||
"properties": ["volume", "muted"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
|
||||||
result = json.loads(result)
|
|
||||||
result = result.get('result')
|
result = result.get('result')
|
||||||
|
|
||||||
volume = result.get('volume')
|
volume = result.get('volume')
|
||||||
|
@ -305,19 +312,11 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Get current audio and subtitles track
|
# Get current audio and subtitles track
|
||||||
tracks_query = {
|
params = {
|
||||||
|
'playerid': 1,
|
||||||
"jsonrpc": "2.0",
|
'properties': ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||||
"id": 1,
|
}
|
||||||
"method": "Player.GetProperties",
|
result = JSONRPC('Player.GetProperties').execute(params)
|
||||||
"params": {
|
|
||||||
|
|
||||||
"playerid": 1,
|
|
||||||
"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = xbmc.executeJSONRPC(json.dumps(tracks_query))
|
|
||||||
result = json.loads(result)
|
|
||||||
result = result.get('result')
|
result = result.get('result')
|
||||||
|
|
||||||
try: # Audio tracks
|
try: # Audio tracks
|
||||||
|
@ -413,10 +412,15 @@ class Player(xbmc.Player):
|
||||||
def onPlayBackStopped(self):
|
def onPlayBackStopped(self):
|
||||||
# Will be called when user stops xbmc playing a file
|
# Will be called when user stops xbmc playing a file
|
||||||
log.debug("ONPLAYBACK_STOPPED")
|
log.debug("ONPLAYBACK_STOPPED")
|
||||||
window('emby_customPlaylist', clear=True)
|
|
||||||
window('emby_customPlaylist.seektime', clear=True)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
window('emby_playbackProps', clear=True)
|
|
||||||
log.info("Clear playlist properties.")
|
if self.currentFile in self.played_info:
|
||||||
|
log.info("Clear playlist.")
|
||||||
|
if self.played_info[self.currentFile]['Type'] == "Audio":
|
||||||
|
xbmc.PlayList(xbmc.PLAYLIST_MUSIC).clear()
|
||||||
|
else:
|
||||||
|
xbmc.PlayList(xbmc.PLAYLIST_VIDEO).clear()
|
||||||
|
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
@log_error()
|
@log_error()
|
||||||
|
@ -456,10 +460,16 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
if currentPosition and runtime:
|
if currentPosition and runtime:
|
||||||
try:
|
try:
|
||||||
|
if window('emby.external'):
|
||||||
|
window('emby.external', clear=True)
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
percentComplete = (currentPosition * 10000000) / int(runtime)
|
percentComplete = (currentPosition * 10000000) / int(runtime)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
# Runtime is 0.
|
# Runtime is 0.
|
||||||
percentComplete = 0
|
percentComplete = 0
|
||||||
|
except ValueError:
|
||||||
|
percentComplete = 100
|
||||||
|
|
||||||
markPlayedAt = float(settings('markPlayed')) / 100
|
markPlayedAt = float(settings('markPlayed')) / 100
|
||||||
log.info("Percent complete: %s Mark played at: %s"
|
log.info("Percent complete: %s Mark played at: %s"
|
||||||
|
@ -486,6 +496,8 @@ class Player(xbmc.Player):
|
||||||
else:
|
else:
|
||||||
log.info("User skipped deletion.")
|
log.info("User skipped deletion.")
|
||||||
|
|
||||||
|
window('emby.external_check', clear=True)
|
||||||
|
|
||||||
# Stop transcoding
|
# Stop transcoding
|
||||||
if playMethod == "Transcode":
|
if playMethod == "Transcode":
|
||||||
log.info("Transcoding for %s terminated." % itemid)
|
log.info("Transcoding for %s terminated." % itemid)
|
||||||
|
|
|
@ -145,7 +145,7 @@ class Playlist(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def verify_playlist(cls):
|
def verify_playlist(cls):
|
||||||
log.debug(JSONRPC('Playlist.GetItems').execute({'playlistid': 1}))
|
log.info(JSONRPC('Playlist.GetItems').execute({'playlistid': 1}))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def remove_from_playlist(cls, position):
|
def remove_from_playlist(cls, position):
|
||||||
|
@ -156,3 +156,171 @@ class Playlist(object):
|
||||||
'position': position
|
'position': position
|
||||||
}
|
}
|
||||||
log.debug(JSONRPC('Playlist.Remove').execute(params))
|
log.debug(JSONRPC('Playlist.Remove').execute(params))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import xbmc
|
||||||
|
import xbmcgui
|
||||||
|
|
||||||
|
import playutils
|
||||||
|
import playbackutils
|
||||||
|
import embydb_functions as embydb
|
||||||
|
import read_embyserver as embyserver
|
||||||
|
from utils import window, JSONRPC
|
||||||
|
from database import DatabaseConn
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
log = logging.getLogger("EMBY."+__name__)
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class Playlist(object):
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.emby = embyserver.Read_EmbyServer()
|
||||||
|
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
|
||||||
|
|
||||||
|
def play_all(self, item_ids, start_at, **kwargs):
|
||||||
|
|
||||||
|
with DatabaseConn('emby') as cursor:
|
||||||
|
emby_db = embydb.Embydb_Functions(cursor)
|
||||||
|
|
||||||
|
player = xbmc.Player()
|
||||||
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
playlist.clear()
|
||||||
|
|
||||||
|
log.info("---*** PLAY ALL ***---")
|
||||||
|
log.info("Items: %s and start at: %s", item_ids, start_at)
|
||||||
|
|
||||||
|
started = False
|
||||||
|
window('emby_customplaylist', value="true")
|
||||||
|
|
||||||
|
if start_at:
|
||||||
|
# Seek to the starting position
|
||||||
|
window('emby_customplaylist.seektime', str(start_at))
|
||||||
|
|
||||||
|
for item_id in item_ids:
|
||||||
|
|
||||||
|
log.info("Adding %s to playlist", item_id)
|
||||||
|
item = emby_db.getItem_byId(item_id)
|
||||||
|
try:
|
||||||
|
db_id = item[0]
|
||||||
|
media_type = item[4]
|
||||||
|
|
||||||
|
except TypeError:
|
||||||
|
# Item is not found in our database, add item manually
|
||||||
|
log.info("Item was not found in the database, manually adding item")
|
||||||
|
item = self.emby.getItem(item_id)
|
||||||
|
self.add_to_xbmc_playlist(playlist, item, **kwargs)
|
||||||
|
|
||||||
|
else: # Add to playlist
|
||||||
|
#self.add_to_playlist(db_id, media_type)
|
||||||
|
item = self.emby.getItem(item_id)
|
||||||
|
self.add_to_xbmc_playlist(playlist, item, db_id, **kwargs)
|
||||||
|
|
||||||
|
if not started:
|
||||||
|
started = True
|
||||||
|
player.play(playlist)
|
||||||
|
|
||||||
|
self.verify_playlist()
|
||||||
|
|
||||||
|
def modify_playlist(self, item_ids):
|
||||||
|
|
||||||
|
with DatabaseConn('emby') as cursor:
|
||||||
|
emby_db = embydb.Embydb_Functions(cursor)
|
||||||
|
|
||||||
|
log.info("---*** ADD TO PLAYLIST ***---")
|
||||||
|
log.info("Items: %s", item_ids)
|
||||||
|
|
||||||
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
|
||||||
|
for item_id in item_ids:
|
||||||
|
|
||||||
|
log.info("Adding %s to playlist", item_id)
|
||||||
|
item = emby_db.getItem_byId(item_id)
|
||||||
|
try:
|
||||||
|
db_id = item[0]
|
||||||
|
media_type = item[4]
|
||||||
|
|
||||||
|
except TypeError:
|
||||||
|
# Item is not found in our database, add item manually
|
||||||
|
item = self.emby.getItem(item_id)
|
||||||
|
self.add_to_xbmc_playlist(playlist, item)
|
||||||
|
|
||||||
|
else: # Add to playlist
|
||||||
|
self.add_to_playlist(db_id, media_type)
|
||||||
|
|
||||||
|
self.verify_playlist()
|
||||||
|
|
||||||
|
return playlist
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_to_xbmc_playlist(cls, playlist, item, db_id, **kwargs):
|
||||||
|
|
||||||
|
listitem = xbmcgui.ListItem()
|
||||||
|
|
||||||
|
play_url = playutils.PlayUtils(item, listitem, **kwargs).get_play_url()
|
||||||
|
if not play_url:
|
||||||
|
log.info("Failed to retrieve playurl")
|
||||||
|
return
|
||||||
|
|
||||||
|
log.info("Playurl: %s", play_url)
|
||||||
|
|
||||||
|
#playbackutils.PlaybackUtils(item).set_playlist(play_url, item['Id'], listitem, seektime=None, db_id):
|
||||||
|
#playbackutils.PlaybackUtils(item).set_properties(playurl, listitem)
|
||||||
|
#playlist.add(playurl, listitem)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_to_playlist(cls, db_id, media_type):
|
||||||
|
|
||||||
|
params = {
|
||||||
|
|
||||||
|
'playlistid': 1,
|
||||||
|
'item': {
|
||||||
|
'%sid' % media_type: int(db_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info(JSONRPC('Playlist.Add').execute(params))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def insert_to_playlist(cls, position, db_id=None, media_type=None, url=None):
|
||||||
|
|
||||||
|
params = {
|
||||||
|
|
||||||
|
'playlistid': 1,
|
||||||
|
'position': position
|
||||||
|
}
|
||||||
|
if db_id is not None:
|
||||||
|
params['item'] = {'%sid' % media_type: int(db_id)}
|
||||||
|
else:
|
||||||
|
params['item'] = {'file': url}
|
||||||
|
|
||||||
|
log.debug(JSONRPC('Playlist.Insert').execute(params))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify_playlist(cls):
|
||||||
|
log.info(JSONRPC('Playlist.GetItems').execute({'playlistid': 1}))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_from_playlist(cls, position):
|
||||||
|
|
||||||
|
params = {
|
||||||
|
|
||||||
|
'playlistid': 1,
|
||||||
|
'position': position
|
||||||
|
}
|
||||||
|
log.debug(JSONRPC('Playlist.Remove').execute(params))
|
||||||
|
'''
|
|
@ -151,7 +151,7 @@ class Read_EmbyServer():
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||||
"MediaSources,VoteCount"
|
"MediaSources,VoteCount,ItemCounts"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
queue.put({'url': url, 'params': params})
|
queue.put({'url': url, 'params': params})
|
||||||
|
@ -182,7 +182,7 @@ class Read_EmbyServer():
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,ItemCounts"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return self.doUtils.downloadUrl("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
return self.doUtils.downloadUrl("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||||
|
@ -198,7 +198,7 @@ class Read_EmbyServer():
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,ItemCounts"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
url = "{server}/emby/LiveTv/Channels/?userid={UserId}&format=json"
|
url = "{server}/emby/LiveTv/Channels/?userid={UserId}&format=json"
|
||||||
|
@ -219,7 +219,7 @@ class Read_EmbyServer():
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,ItemCounts"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
url = "{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json"
|
url = "{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json"
|
||||||
|
@ -285,7 +285,7 @@ class Read_EmbyServer():
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||||
"MediaSources,VoteCount"
|
"MediaSources,VoteCount,ItemCounts"
|
||||||
)
|
)
|
||||||
queue.put({'url': url, 'params': params})
|
queue.put({'url': url, 'params': params})
|
||||||
if not self._add_worker_thread(queue, items['Items']):
|
if not self._add_worker_thread(queue, items['Items']):
|
||||||
|
@ -472,7 +472,7 @@ class Read_EmbyServer():
|
||||||
|
|
||||||
"Etag,Genres,SortName,Studios,Writer,ProductionYear,"
|
"Etag,Genres,SortName,Studios,Writer,ProductionYear,"
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,"
|
||||||
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview"
|
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview,ItemCounts"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
queue.put({'url': url, 'params': params})
|
queue.put({'url': url, 'params': params})
|
||||||
|
@ -614,7 +614,7 @@ class Read_EmbyServer():
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||||
"MediaSources,VoteCount"
|
"MediaSources,VoteCount,ItemCounts"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
return params
|
return params
|
||||||
|
@ -649,6 +649,39 @@ class Read_EmbyServer():
|
||||||
return library['LibraryOptions']
|
return library['LibraryOptions']
|
||||||
|
|
||||||
def get_server_transcoding_settings(self):
|
def get_server_transcoding_settings(self):
|
||||||
|
return self.doUtils.downloadUrl(self.get_emby_url('System/Configuration/encoding'))
|
||||||
|
|
||||||
url = self.get_emby_url('/System/Configuration/encoding')
|
def get_intros(self, item_id):
|
||||||
return self.doUtils.downloadUrl(url)
|
return self.doUtils.downloadUrl(self.get_emby_url('Users/{UserId}/Items/%s/Intros' % item_id))
|
||||||
|
|
||||||
|
def get_additional_parts(self, item_id):
|
||||||
|
return self.doUtils.downloadUrl(self.get_emby_url('Videos/%s/AdditionalParts' % item_id))
|
||||||
|
|
||||||
|
def get_playback_info(self, item_id, profile, offset=0, audio=None, subtitles=None):
|
||||||
|
|
||||||
|
url = self.get_emby_url('Items/%s/PlaybackInfo' % item_id)
|
||||||
|
return self.doUtils.downloadUrl(url, action_type="POST", postBody={
|
||||||
|
|
||||||
|
'UserId': self.userId,
|
||||||
|
'DeviceProfile': profile,
|
||||||
|
'StartTimeTicks': offset, #TODO
|
||||||
|
'AudioStreamIndex': audio, #TODO
|
||||||
|
'SubtitleStreamIndex': subtitles, #TODO
|
||||||
|
'MediaSourceId': None,
|
||||||
|
'LiveStreamId': None
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_live_stream(self, item_id, profile, session_id, token, offset=0, audio=None, subtitles=None):
|
||||||
|
|
||||||
|
url = self.get_emby_url('/LiveStreams/Open')
|
||||||
|
return self.doUtils.downloadUrl(url, action_type="POST", postBody={
|
||||||
|
|
||||||
|
'UserId': self.userId,
|
||||||
|
'DeviceProfile': profile,
|
||||||
|
'ItemId': item_id,
|
||||||
|
'PlaySessionId': session_id,
|
||||||
|
'OpenToken': token,
|
||||||
|
'StartTimeTicks': offset, #TODO
|
||||||
|
'AudioStreamIndex': audio, #TODO
|
||||||
|
'SubtitleStreamIndex': subtitles #TODO
|
||||||
|
})
|
||||||
|
|
|
@ -10,6 +10,7 @@ from datetime import datetime
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
|
import xbmcgui
|
||||||
|
|
||||||
import userclient
|
import userclient
|
||||||
import clientinfo
|
import clientinfo
|
||||||
|
@ -52,10 +53,11 @@ class Service(object):
|
||||||
self.addon_name = self.client_info.get_addon_name()
|
self.addon_name = self.client_info.get_addon_name()
|
||||||
log_level = settings('logLevel')
|
log_level = settings('logLevel')
|
||||||
|
|
||||||
|
# General settings which are used by other entrypoints
|
||||||
window('emby_logLevel', value=str(log_level))
|
window('emby_logLevel', value=str(log_level))
|
||||||
window('emby_kodiProfile', value=xbmc.translatePath('special://profile'))
|
window('emby_kodiProfile', value=xbmc.translatePath('special://profile'))
|
||||||
context_menu = "true" if settings('enableContext') == "true" else ""
|
window('emby_context', value="true" if settings('enableContext') == "true" else "")
|
||||||
window('emby_context', value=context_menu)
|
window('emby_context_transcode', value="true" if settings('enableContextTranscode') == "true" else "")
|
||||||
|
|
||||||
# Initial logging
|
# Initial logging
|
||||||
log.warn("======== START %s ========", self.addon_name)
|
log.warn("======== START %s ========", self.addon_name)
|
||||||
|
@ -72,7 +74,8 @@ class Service(object):
|
||||||
"emby_online", "emby_state.json", "emby_serverStatus", "emby_onWake",
|
"emby_online", "emby_state.json", "emby_serverStatus", "emby_onWake",
|
||||||
"emby_syncRunning", "emby_dbCheck", "emby_kodiScan",
|
"emby_syncRunning", "emby_dbCheck", "emby_kodiScan",
|
||||||
"emby_shouldStop", "emby_currUser", "emby_dbScan", "emby_sessionId",
|
"emby_shouldStop", "emby_currUser", "emby_dbScan", "emby_sessionId",
|
||||||
"emby_initialScan", "emby_customplaylist", "emby_playbackProps"
|
"emby_initialScan", "emby_customplaylist", "emby_playbackProps",
|
||||||
|
"emby.external_check", "emby.external", "emby.resume"
|
||||||
]
|
]
|
||||||
for prop in properties:
|
for prop in properties:
|
||||||
window(prop, clear=True)
|
window(prop, clear=True)
|
||||||
|
@ -319,6 +322,9 @@ class Service(object):
|
||||||
#ga = GoogleAnalytics()
|
#ga = GoogleAnalytics()
|
||||||
#ga.sendEventData("Application", "Shutdown")
|
#ga.sendEventData("Application", "Shutdown")
|
||||||
|
|
||||||
|
if self.monitor.special_monitor:
|
||||||
|
self.monitor.special_monitor.stop_monitor()
|
||||||
|
|
||||||
if self.userclient_running:
|
if self.userclient_running:
|
||||||
self.userclient_thread.stop_client()
|
self.userclient_thread.stop_client()
|
||||||
|
|
||||||
|
|
|
@ -313,7 +313,7 @@ class UserClient(threading.Thread):
|
||||||
log.info("Username found: %s", username)
|
log.info("Username found: %s", username)
|
||||||
self._auth = True
|
self._auth = True
|
||||||
|
|
||||||
if monitor.waitForAbort(1):
|
if monitor.waitForAbort(2):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ def dialog(type_, *args, **kwargs):
|
||||||
}
|
}
|
||||||
return types[type_](*args, **kwargs)
|
return types[type_](*args, **kwargs)
|
||||||
|
|
||||||
def plugin_path(plugin, params):
|
def urllib_path(plugin, params):
|
||||||
return "%s?%s" % (plugin, urllib.urlencode(params))
|
return "%s?%s" % (plugin, urllib.urlencode(params))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import xbmcvfs
|
||||||
|
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
import embydb_functions as embydb
|
import embydb_functions as embydb
|
||||||
from utils import window, language as lang, indent as xml_indent, plugin_path
|
from utils import window, language as lang, indent as xml_indent, urllib_path
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -641,7 +641,7 @@ class VideoNodes(object):
|
||||||
'mode': "browsecontent",
|
'mode': "browsecontent",
|
||||||
'type': mediatype
|
'type': mediatype
|
||||||
}
|
}
|
||||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||||
|
|
||||||
elif (mediatype == "homevideos" or mediatype == "photos"):
|
elif (mediatype == "homevideos" or mediatype == "photos"):
|
||||||
params = {
|
params = {
|
||||||
|
@ -651,7 +651,7 @@ class VideoNodes(object):
|
||||||
'type': mediatype,
|
'type': mediatype,
|
||||||
'folderid': nodetype
|
'folderid': nodetype
|
||||||
}
|
}
|
||||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||||
|
|
||||||
elif nodetype == "nextepisodes":
|
elif nodetype == "nextepisodes":
|
||||||
params = {
|
params = {
|
||||||
|
@ -660,7 +660,7 @@ class VideoNodes(object):
|
||||||
'mode': "nextup",
|
'mode': "nextup",
|
||||||
'limit': 25
|
'limit': 25
|
||||||
}
|
}
|
||||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||||
|
|
||||||
elif KODI == 14 and nodetype == "recentepisodes":
|
elif KODI == 14 and nodetype == "recentepisodes":
|
||||||
params = {
|
params = {
|
||||||
|
@ -669,7 +669,7 @@ class VideoNodes(object):
|
||||||
'mode': "recentepisodes",
|
'mode': "recentepisodes",
|
||||||
'limit': 25
|
'limit': 25
|
||||||
}
|
}
|
||||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||||
|
|
||||||
elif KODI == 14 and nodetype == "inprogressepisodes":
|
elif KODI == 14 and nodetype == "inprogressepisodes":
|
||||||
params = {
|
params = {
|
||||||
|
@ -678,7 +678,7 @@ class VideoNodes(object):
|
||||||
'mode': "inprogressepisodes",
|
'mode': "inprogressepisodes",
|
||||||
'limit': 25
|
'limit': 25
|
||||||
}
|
}
|
||||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||||
else:
|
else:
|
||||||
path = "library://video/emby/%s/%s.xml" % (viewid, nodetype)
|
path = "library://video/emby/%s/%s.xml" % (viewid, nodetype)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import downloadutils
|
||||||
import librarysync
|
import librarysync
|
||||||
import playlist
|
import playlist
|
||||||
import userclient
|
import userclient
|
||||||
|
import playbackutils
|
||||||
from utils import window, settings, dialog, language as lang, JSONRPC
|
from utils import window, settings, dialog, language as lang, JSONRPC
|
||||||
from ga_client import log_error
|
from ga_client import log_error
|
||||||
|
|
||||||
|
@ -112,19 +113,31 @@ class WebSocketClient(threading.Thread):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _play(cls, data):
|
def _play(cls, data):
|
||||||
|
|
||||||
|
''' {"Id":"e2c106a953bfc0d9c191a49cda6561de",
|
||||||
|
"ItemIds":["52f0c57e2133e1c11d36c59edcd835fc"],
|
||||||
|
"PlayCommand":"PlayNow","ControllingUserId":"d40000000000000000000000000000000",
|
||||||
|
"SubtitleStreamIndex":3,"AudioStreamIndex":1,
|
||||||
|
"MediaSourceId":"ba83c549ac5c0e4180ae33ebdf813c51"}}
|
||||||
|
'''
|
||||||
|
|
||||||
item_ids = data['ItemIds']
|
item_ids = data['ItemIds']
|
||||||
|
kwargs = {
|
||||||
|
|
||||||
|
'SubtitleStreamIndex': data.get('SubtitleStreamIndex'),
|
||||||
|
'AudioStreamIndex': data.get('AudioStreamIndex'),
|
||||||
|
'MediaSourceId': data.get('MediaSourceId')
|
||||||
|
}
|
||||||
command = data['PlayCommand']
|
command = data['PlayCommand']
|
||||||
|
|
||||||
playlist_ = playlist.Playlist()
|
playlist_ = playlist.Playlist()
|
||||||
|
|
||||||
if command == 'PlayNow':
|
if command == 'PlayNow':
|
||||||
startat = data.get('StartPositionTicks', 0)
|
if playbackutils.PlaybackUtils(None, item_ids[0]).play_all(item_ids, data.get('StartPositionTicks', 0), **kwargs):
|
||||||
playlist_.play_all(item_ids, startat)
|
dialog(type_="notification",
|
||||||
dialog(type_="notification",
|
heading="{emby}",
|
||||||
heading="{emby}",
|
message="%s %s" % (len(item_ids), lang(33004)),
|
||||||
message="%s %s" % (len(item_ids), lang(33004)),
|
icon="{emby}",
|
||||||
icon="{emby}",
|
sound=False)
|
||||||
sound=False)
|
|
||||||
|
|
||||||
elif command == 'PlayNext':
|
elif command == 'PlayNext':
|
||||||
new_playlist = playlist_.modify_playlist(item_ids)
|
new_playlist = playlist_.modify_playlist(item_ids)
|
||||||
|
@ -214,7 +227,7 @@ class WebSocketClient(threading.Thread):
|
||||||
elif command == 'SetSubtitleStreamIndex':
|
elif command == 'SetSubtitleStreamIndex':
|
||||||
emby_index = int(arguments['Index'])
|
emby_index = int(arguments['Index'])
|
||||||
current_file = player.getPlayingFile()
|
current_file = player.getPlayingFile()
|
||||||
mapping = window('emby_%s.indexMapping' % current_file)
|
mapping = window('emby_%s.indexMapping.json' % current_file)
|
||||||
|
|
||||||
if emby_index == -1:
|
if emby_index == -1:
|
||||||
player.showSubtitles(False)
|
player.showSubtitles(False)
|
||||||
|
|
|
@ -49,13 +49,17 @@
|
||||||
<setting id="deleteTV" type="bool" label="30115" visible="eq(-1,true)" default="false" subsetting="true" />
|
<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="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" />
|
||||||
|
<setting label="HTTP Playback (Direct play or transcode)" type="lsep"/>
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="playFromStream" type="bool" label="30002" default="true" />
|
<setting id="playFromStream" type="bool" label="30002" default="true" />
|
||||||
<setting id="videoBitrate" type="enum" label="30160" values="664 Kbps SD|996 Kbps HD|1.3 Mbps HD|2.0 Mbps HD|3.2 Mbps HD|4.7 Mbps HD|6.2 Mbps HD|7.7 Mbps HD|9.2 Mbps HD|10.7 Mbps HD|12.2 Mbps HD|13.7 Mbps HD|15.2 Mbps HD|16.7 Mbps HD|18.2 Mbps HD|20.0 Mbps HD|25.0 Mbps HD|30.0 Mbps HD|35.0 Mbps HD|40.0 Mbps HD|100.0 Mbps HD [default]|1000.0 Mbps HD" visible="true" default="20" subsetting="true" />
|
<setting id="videoBitrate" type="enum" label="30160" values="664 Kbps SD|996 Kbps HD|1.3 Mbps HD|2.0 Mbps HD|3.2 Mbps HD|4.7 Mbps HD|6.2 Mbps HD|7.7 Mbps HD|9.2 Mbps HD|10.7 Mbps HD|12.2 Mbps HD|13.7 Mbps HD|15.2 Mbps HD|16.7 Mbps HD|18.2 Mbps HD|20.0 Mbps HD|25.0 Mbps HD|30.0 Mbps HD|35.0 Mbps HD|40.0 Mbps HD|100.0 Mbps HD [default]|1000.0 Mbps HD" visible="true" default="20" />
|
||||||
<setting id="enableExternalSubs" type="bool" label="Enable external subs for direct stream" default="true" />
|
<setting label="Direct play (HTTP)" type="lsep"/>
|
||||||
<setting id="transcodeH265" type="enum" label="30522" default="0" values="Disabled|480p(and higher)|720p(and higher)|1080p" />
|
<setting id="enableExternalSubs" type="bool" label="Enable external subtitles" default="true" />
|
||||||
|
<setting label="Transcode" type="lsep"/>
|
||||||
|
<setting id="skipDialogTranscode" type="enum" label="Enable audio/subtitles selection" values="Enabled|Audio only|Subtitles only|Disabled" visible="true" default="0" />
|
||||||
|
<setting id="ignoreTranscode" type="text" label="Never transcode codecs (i.e. h264,h265,...)" visible="true" />
|
||||||
|
<setting id="transcode_h265" type="bool" label="30522" default="false" />
|
||||||
<setting id="transcodeHi10P" type="bool" label="30537" default="false"/>
|
<setting id="transcodeHi10P" type="bool" label="30537" default="false"/>
|
||||||
<setting id="limitResolution" type="bool" label="33096" default="false"/>
|
|
||||||
<setting id="markPlayed" type="number" visible="false" default="90" />
|
<setting id="markPlayed" type="number" visible="false" default="90" />
|
||||||
<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="" />
|
<setting id="networkCreds" type="text" visible="false" default="" />
|
||||||
|
@ -65,7 +69,8 @@
|
||||||
<setting id="enableCoverArt" type="bool" label="30157" default="true" />
|
<setting id="enableCoverArt" type="bool" label="30157" default="true" />
|
||||||
<setting id="ignoreSpecialsNextEpisodes" type="bool" label="30527" default="false" />
|
<setting id="ignoreSpecialsNextEpisodes" type="bool" label="30527" default="false" />
|
||||||
<setting id="enableContext" type="bool" label="Enable the Emby context menu" default="true" />
|
<setting id="enableContext" type="bool" label="Enable the Emby context menu" default="true" />
|
||||||
<setting id="skipContextMenu" type="bool" label="30520" default="false" visible="eq(-1,true)" subsetting="true" />
|
<setting id="enableContextTranscode" type="bool" label="Enable Force Transcode" visible="eq(-1,true)" default="true" subsetting="true" />
|
||||||
|
<setting id="skipContextMenu" type="bool" label="30520" default="false" visible="eq(-2,true)" subsetting="true" />
|
||||||
<setting id="additionalUsers" type="text" label="30528" default="" />
|
<setting id="additionalUsers" type="text" label="30528" default="" />
|
||||||
<setting type="lsep" label="30534" />
|
<setting type="lsep" label="30534" />
|
||||||
<setting id="connectMsg" type="bool" label="30249" default="true" />
|
<setting id="connectMsg" type="bool" label="30249" default="true" />
|
||||||
|
|
|
@ -1,142 +1,185 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<window>
|
<window>
|
||||||
<defaultcontrol always="true">200</defaultcontrol>
|
<defaultcontrol always="true">200</defaultcontrol>
|
||||||
<zorder>0</zorder>
|
|
||||||
<include>dialogeffect</include>
|
|
||||||
<controls>
|
<controls>
|
||||||
<control type="image">
|
|
||||||
<description>Background fade</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<texture>emby-bg-fade.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>600</width>
|
|
||||||
<left>35%</left>
|
|
||||||
<top>20%</top>
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<top>0</top>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<bottom>0</bottom>
|
||||||
<width>600</width>
|
<left>0</left>
|
||||||
<height>480</height>
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="group" id="202">
|
|
||||||
<top>485</top>
|
|
||||||
<visible>False</visible>
|
|
||||||
<control type="image">
|
|
||||||
<description>Error box</description>
|
|
||||||
<texture colordiffuse="ff222222">white.png</texture>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label" id="203">
|
|
||||||
<description>Error message</description>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<align>center</align>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Emby logo</description>
|
|
||||||
<texture>logo-white.png</texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>120</width>
|
|
||||||
<height>49</height>
|
|
||||||
<top>30</top>
|
|
||||||
<left>25</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>500</width>
|
<animation type="WindowOpen" reversible="false">
|
||||||
<left>50</left>
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
<control type="label">
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
<description>Please sign in</description>
|
</animation>
|
||||||
<label>$ADDON[plugin.video.emby 30612]</label>
|
<animation type="WindowClose" reversible="false">
|
||||||
<textcolor>white</textcolor>
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
<font>font12</font>
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
<aligny>top</aligny>
|
</animation>
|
||||||
<align>center</align>
|
<centerleft>50%</centerleft>
|
||||||
|
<centertop>50%</centertop>
|
||||||
|
<width>470</width>
|
||||||
|
<height>420</height>
|
||||||
|
<control type="group">
|
||||||
|
<top>-30</top>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<texture>logo-white.png</texture>
|
||||||
|
<aspectratio align="left">keep</aspectratio>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<top>100</top>
|
<height>420</height>
|
||||||
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<top>150</top>
|
<centerleft>50%</centerleft>
|
||||||
<control type="label">
|
<top>10</top>
|
||||||
<description>Username</description>
|
<width>460</width>
|
||||||
<label>$ADDON[plugin.video.emby 30024]</label>
|
<height>400</height>
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
<control type="grouplist" id="100">
|
||||||
<font>font10</font>
|
<orientation>vertical</orientation>
|
||||||
<aligny>top</aligny>
|
<itemgap>0</itemgap>
|
||||||
|
<control type="label">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>75</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>white</textcolor>
|
||||||
|
<textshadow>66000000</textshadow>
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30612][/B]</label>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="101">
|
||||||
|
<height>100</height>
|
||||||
|
<control type="label">
|
||||||
|
<label>$ADDON[plugin.video.emby 30024]</label>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<font>font10</font>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
</control>
|
||||||
|
<control id="204" type="edit">
|
||||||
|
<top>35</top>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF888888</textcolor>
|
||||||
|
<focusedcolor>FF52b54b</focusedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<ondown>205</ondown>
|
||||||
|
<texturefocus>-</texturefocus>
|
||||||
|
<texturenofocus>-</texturenofocus>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>1</height>
|
||||||
|
<top>80</top>
|
||||||
|
<texture colordiffuse="ff525252">white.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="102">
|
||||||
|
<height>100</height>
|
||||||
|
<control type="label">
|
||||||
|
<label>$ADDON[plugin.video.emby 30602]</label>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<textshadow>66000000</textshadow>
|
||||||
|
<font>font10</font>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
</control>
|
||||||
|
<control id="205" type="edit">
|
||||||
|
<top>35</top>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF888888</textcolor>
|
||||||
|
<focusedcolor>FF52b54b</focusedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<onup>204</onup>
|
||||||
|
<ondown>200</ondown>
|
||||||
|
<texturefocus>-</texturefocus>
|
||||||
|
<texturenofocus>-</texturenofocus>
|
||||||
|
<password>true</password>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<description>separator</description>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>1</height>
|
||||||
|
<top>80</top>
|
||||||
|
<texture colordiffuse="ff525252">white.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="200">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30605][/B]</label>
|
||||||
|
<width>426</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<onup>205</onup>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="201">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30606][/B]</label>
|
||||||
|
<width>426</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="202">
|
||||||
|
<top>420</top>
|
||||||
|
<visible>False</visible>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>separator</description>
|
<description>Error box</description>
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>66</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<description>Password</description>
|
|
||||||
<top>225</top>
|
|
||||||
<control type="label">
|
|
||||||
<description>Password label</description>
|
|
||||||
<label>$ADDON[plugin.video.emby 30602]</label>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>separator</description>
|
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>66</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<description>Buttons</description>
|
|
||||||
<top>335</top>
|
|
||||||
<control type="button" id="200">
|
|
||||||
<description>Sign in</description>
|
|
||||||
<texturenofocus border="5" colordiffuse="ff0b8628">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff13a134">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30605][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<height>50</height>
|
<height>70</height>
|
||||||
<ondown>201</ondown>
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
</control>
|
</control>
|
||||||
|
<control type="label" id="203">
|
||||||
<control type="button" id="201">
|
<top>10</top>
|
||||||
<description>Cancel</description>
|
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff525252">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30606][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
<top>55</top>
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
<onup>200</onup>
|
<scroll>true</scroll>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<font>font10</font>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
|
|
@ -1,176 +1,221 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<window>
|
<window>
|
||||||
<defaultcontrol always="true">200</defaultcontrol>
|
|
||||||
<zorder>0</zorder>
|
|
||||||
<include>dialogeffect</include>
|
|
||||||
<controls>
|
<controls>
|
||||||
<control type="image">
|
|
||||||
<description>Background fade</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<texture>emby-bg-fade.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>600</width>
|
|
||||||
<left>35%</left>
|
|
||||||
<top>15%</top>
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<top>0</top>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<bottom>0</bottom>
|
||||||
<width>600</width>
|
<left>0</left>
|
||||||
<height>700</height>
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="group" id="202">
|
|
||||||
<top>705</top>
|
|
||||||
<visible>False</visible>
|
|
||||||
<control type="image">
|
|
||||||
<description>Error box</description>
|
|
||||||
<texture colordiffuse="ff222222">white.png</texture>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label" id="203">
|
|
||||||
<description>Error message</description>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<align>center</align>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Emby logo</description>
|
|
||||||
<texture>logo-white.png</texture>
|
|
||||||
<width>160</width>
|
|
||||||
<height>49</height>
|
|
||||||
<top>30</top>
|
|
||||||
<left>25</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>500</width>
|
<animation type="WindowOpen" reversible="false">
|
||||||
<left>50</left>
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
<control type="label">
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
<description>Sign in emby connect</description>
|
</animation>
|
||||||
<label>$ADDON[plugin.video.emby 30600]</label>
|
<animation type="WindowClose" reversible="false">
|
||||||
<textcolor>white</textcolor>
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
<font>font12</font>
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
<aligny>top</aligny>
|
</animation>
|
||||||
<top>115</top>
|
<centerleft>50%</centerleft>
|
||||||
</control>
|
<centertop>50%</centertop>
|
||||||
|
<width>470</width>
|
||||||
|
<height>620</height>
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<top>190</top>
|
<top>-30</top>
|
||||||
<control type="label">
|
|
||||||
<description>Username email</description>
|
|
||||||
<label>$ADDON[plugin.video.emby 30543]</label>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>separator</description>
|
<left>20</left>
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>66</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<description>Password</description>
|
|
||||||
<top>275</top>
|
|
||||||
<control type="label">
|
|
||||||
<description>Password label</description>
|
|
||||||
<label>$ADDON[plugin.video.emby 30602]</label>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>separator</description>
|
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>66</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<description>Buttons</description>
|
|
||||||
<top>385</top>
|
|
||||||
<control type="button" id="200">
|
|
||||||
<description>Sign in</description>
|
|
||||||
<texturenofocus border="5" colordiffuse="ff0b8628">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff13a134">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30605][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<height>50</height>
|
<height>25</height>
|
||||||
<ondown>201</ondown>
|
<texture>logo-white.png</texture>
|
||||||
</control>
|
<aspectratio align="left">keep</aspectratio>
|
||||||
|
|
||||||
<control type="button" id="201">
|
|
||||||
<description>Cancel</description>
|
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff525252">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30606][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
<top>55</top>
|
|
||||||
<onup>200</onup>
|
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>610</height>
|
||||||
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
|
</control>
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<description>Disclaimer</description>
|
<centerleft>50%</centerleft>
|
||||||
<top>510</top>
|
<top>10</top>
|
||||||
<control type="label">
|
<width>460</width>
|
||||||
<description>Disclaimer label</description>
|
<height>590</height>
|
||||||
<label>$ADDON[plugin.video.emby 30603]</label>
|
<control type="grouplist" id="100">
|
||||||
<font>font10</font>
|
<orientation>vertical</orientation>
|
||||||
<textcolor>ff464646</textcolor>
|
<itemgap>0</itemgap>
|
||||||
<wrapmultiline>true</wrapmultiline>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
<width>340</width>
|
|
||||||
<height>100%</height>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<control type="label">
|
<control type="label">
|
||||||
<description>Scan me</description>
|
<width>100%</width>
|
||||||
<label>[UPPERCASE]$ADDON[plugin.video.emby 30604][/UPPERCASE]</label>
|
<height>75</height>
|
||||||
<font>font12</font>
|
<aligny>center</aligny>
|
||||||
<textcolor>ff0b8628</textcolor>
|
<textoffsetx>20</textoffsetx>
|
||||||
<aligny>top</aligny>
|
<font>font10</font>
|
||||||
<width>200</width>
|
<textcolor>white</textcolor>
|
||||||
<top>120</top>
|
<textshadow>66000000</textshadow>
|
||||||
<left>230</left>
|
<label>[B]$ADDON[plugin.video.emby 30612][/B]</label>
|
||||||
</control>
|
</control>
|
||||||
|
<control type="group" id="101">
|
||||||
<control type="image">
|
<height>100</height>
|
||||||
<description>qrcode</description>
|
<control type="label">
|
||||||
<texture>qrcode_disclaimer.png</texture>
|
<label>$ADDON[plugin.video.emby 30024]</label>
|
||||||
<width>140</width>
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
<height>140</height>
|
<shadowcolor>66000000</shadowcolor>
|
||||||
<top>10</top>
|
<font>font10</font>
|
||||||
<left>360</left>
|
<aligny>top</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
</control>
|
||||||
|
<control id="204" type="edit">
|
||||||
|
<top>35</top>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF888888</textcolor>
|
||||||
|
<focusedcolor>FF52b54b</focusedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<ondown>205</ondown>
|
||||||
|
<texturefocus>-</texturefocus>
|
||||||
|
<texturenofocus>-</texturenofocus>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>1</height>
|
||||||
|
<top>80</top>
|
||||||
|
<texture colordiffuse="ff525252">white.png</texture>
|
||||||
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
<control type="group" id="102">
|
||||||
|
<height>100</height>
|
||||||
|
<control type="label">
|
||||||
|
<label>$ADDON[plugin.video.emby 30602]</label>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<textshadow>66000000</textshadow>
|
||||||
|
<font>font10</font>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
</control>
|
||||||
|
<control id="205" type="edit">
|
||||||
|
<top>35</top>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF888888</textcolor>
|
||||||
|
<focusedcolor>FF52b54b</focusedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<onup>204</onup>
|
||||||
|
<ondown>200</ondown>
|
||||||
|
<texturefocus>-</texturefocus>
|
||||||
|
<texturenofocus>-</texturenofocus>
|
||||||
|
<password>true</password>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<description>separator</description>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>1</height>
|
||||||
|
<top>80</top>
|
||||||
|
<texture colordiffuse="ff525252">white.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="200">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30605][/B]</label>
|
||||||
|
<width>426</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="201">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30606][/B]</label>
|
||||||
|
<width>426</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<description>spacer</description>
|
||||||
|
<height>20</height>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<control type="label">
|
||||||
|
<label>$ADDON[plugin.video.emby 30603]</label>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ff464646</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<wrapmultiline>true</wrapmultiline>
|
||||||
|
<scroll>true</scroll>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
<height>130</height>
|
||||||
|
<left>20</left>
|
||||||
|
<right>160</right>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<top>10</top>
|
||||||
|
<right>20</right>
|
||||||
|
<width>130</width>
|
||||||
|
<control type="image">
|
||||||
|
<width>130</width>
|
||||||
|
<height>130</height>
|
||||||
|
<description>qrcode</description>
|
||||||
|
<texture>qrcode_disclaimer.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<top>135</top>
|
||||||
|
<align>center</align>
|
||||||
|
<label>[UPPERCASE]$ADDON[plugin.video.emby 30604][/UPPERCASE]</label>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF52b54b</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="202">
|
||||||
|
<top>610</top>
|
||||||
|
<visible>False</visible>
|
||||||
|
<control type="image">
|
||||||
|
<description>Error box</description>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>70</height>
|
||||||
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="label" id="203">
|
||||||
|
<top>10</top>
|
||||||
|
<height>50</height>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<scroll>true</scroll>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<font>font10</font>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
|
|
@ -2,150 +2,184 @@
|
||||||
<window>
|
<window>
|
||||||
<defaultcontrol always="true">200</defaultcontrol>
|
<defaultcontrol always="true">200</defaultcontrol>
|
||||||
<zorder>0</zorder>
|
<zorder>0</zorder>
|
||||||
<include>dialogeffect</include>
|
|
||||||
<controls>
|
<controls>
|
||||||
<control type="image">
|
|
||||||
<description>Background fade</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<texture>emby-bg-fade.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>600</width>
|
|
||||||
<left>35%</left>
|
|
||||||
<top>20%</top>
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<top>0</top>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<bottom>0</bottom>
|
||||||
<width>600</width>
|
<left>0</left>
|
||||||
<height>525</height>
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="group" id="202">
|
|
||||||
<top>530</top>
|
|
||||||
<visible>False</visible>
|
|
||||||
<control type="image">
|
|
||||||
<description>Error box</description>
|
|
||||||
<texture colordiffuse="ff222222">white.png</texture>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label" id="203">
|
|
||||||
<description>Error message</description>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<align>center</align>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Emby logo</description>
|
|
||||||
<texture>logo-white.png</texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>120</width>
|
|
||||||
<height>49</height>
|
|
||||||
<top>30</top>
|
|
||||||
<left>25</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>500</width>
|
<animation type="WindowOpen" reversible="false">
|
||||||
<left>50</left>
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
<control type="label">
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
<description>Connect to server</description>
|
</animation>
|
||||||
<label>$ADDON[plugin.video.emby 30614]</label>
|
<animation type="WindowClose" reversible="false">
|
||||||
<textcolor>white</textcolor>
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
<font>font12</font>
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
<aligny>top</aligny>
|
</animation>
|
||||||
<align>center</align>
|
<centerleft>50%</centerleft>
|
||||||
|
<centertop>50%</centertop>
|
||||||
|
<width>470</width>
|
||||||
|
<height>420</height>
|
||||||
|
<control type="group">
|
||||||
|
<top>-30</top>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<texture>logo-white.png</texture>
|
||||||
|
<aspectratio align="left">keep</aspectratio>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<top>100</top>
|
<height>420</height>
|
||||||
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<top>150</top>
|
<centerleft>50%</centerleft>
|
||||||
<control type="label">
|
<top>10</top>
|
||||||
<description>Host</description>
|
<width>460</width>
|
||||||
<label>$ADDON[plugin.video.emby 30615]</label>
|
<height>400</height>
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
<control type="grouplist" id="100">
|
||||||
<font>font10</font>
|
<orientation>vertical</orientation>
|
||||||
<aligny>top</aligny>
|
<itemgap>0</itemgap>
|
||||||
|
<control type="label">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>75</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>white</textcolor>
|
||||||
|
<textshadow>66000000</textshadow>
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30614][/B]</label>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="101">
|
||||||
|
<height>100</height>
|
||||||
|
<control type="label">
|
||||||
|
<label>$ADDON[plugin.video.emby 30615]</label>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<font>font10</font>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
</control>
|
||||||
|
<control id="204" type="edit">
|
||||||
|
<top>35</top>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF888888</textcolor>
|
||||||
|
<focusedcolor>FF52b54b</focusedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<ondown>205</ondown>
|
||||||
|
<hinttext>[COLOR ff464646]IP or https://myserver.com[/COLOR]</hinttext>
|
||||||
|
<texturefocus>-</texturefocus>
|
||||||
|
<texturenofocus>-</texturenofocus>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>1</height>
|
||||||
|
<top>80</top>
|
||||||
|
<texture colordiffuse="ff525252">white.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="102">
|
||||||
|
<height>100</height>
|
||||||
|
<control type="label">
|
||||||
|
<label>$ADDON[plugin.video.emby 30030]</label>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<textshadow>66000000</textshadow>
|
||||||
|
<font>font10</font>
|
||||||
|
<aligny>top</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
</control>
|
||||||
|
<control id="205" type="edit">
|
||||||
|
<top>35</top>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>FF888888</textcolor>
|
||||||
|
<focusedcolor>FF52b54b</focusedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<onup>204</onup>
|
||||||
|
<ondown>200</ondown>
|
||||||
|
<texturefocus>-</texturefocus>
|
||||||
|
<texturenofocus>-</texturenofocus>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<description>separator</description>
|
||||||
|
<left>20</left>
|
||||||
|
<right>20</right>
|
||||||
|
<height>1</height>
|
||||||
|
<top>80</top>
|
||||||
|
<texture colordiffuse="ff525252">white.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="200">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30616][/B]</label>
|
||||||
|
<width>426</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="201">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30606][/B]</label>
|
||||||
|
<width>426</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="202">
|
||||||
|
<top>420</top>
|
||||||
|
<visible>False</visible>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>separator</description>
|
<description>Error box</description>
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>66</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<description>Host example</description>
|
|
||||||
<label>192.168.1.100 or https://myserver.com</label>
|
|
||||||
<textcolor>ff464646</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
<top>70</top>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<description>Port</description>
|
|
||||||
<top>275</top>
|
|
||||||
<control type="label">
|
|
||||||
<description>Port label</description>
|
|
||||||
<label>$ADDON[plugin.video.emby 30030]</label>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>separator</description>
|
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>66</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<description>Buttons</description>
|
|
||||||
<top>380</top>
|
|
||||||
<control type="button" id="200">
|
|
||||||
<description>Connect</description>
|
|
||||||
<texturenofocus border="5" colordiffuse="ff0b8628">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff13a134">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30616][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<height>50</height>
|
<height>70</height>
|
||||||
<ondown>201</ondown>
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
</control>
|
</control>
|
||||||
|
<control type="label" id="203">
|
||||||
<control type="button" id="201">
|
<top>10</top>
|
||||||
<description>Cancel</description>
|
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff525252">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30606][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
<top>55</top>
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
<onup>200</onup>
|
<scroll>true</scroll>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<font>font10</font>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
|
|
@ -1,277 +1,239 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<window>
|
<window>
|
||||||
<defaultcontrol always="true">205</defaultcontrol>
|
<defaultcontrol always="true">205</defaultcontrol>
|
||||||
<zorder>0</zorder>
|
|
||||||
<include>dialogeffect</include>
|
|
||||||
<controls>
|
<controls>
|
||||||
<control type="image">
|
|
||||||
<description>Background fade</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<texture>emby-bg-fade.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>450</width>
|
|
||||||
<left>38%</left>
|
|
||||||
<top>15%</top>
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<top>0</top>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<bottom>0</bottom>
|
||||||
<width>450</width>
|
<left>0</left>
|
||||||
<height>710</height>
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Emby logo</description>
|
|
||||||
<texture>logo-white.png</texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>120</width>
|
|
||||||
<height>49</height>
|
|
||||||
<top>30</top>
|
|
||||||
<left>25</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<description>User info</description>
|
<animation type="WindowOpen" reversible="false">
|
||||||
<top>70</top>
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
<width>350</width>
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
<left>50</left>
|
</animation>
|
||||||
<control type="image" id="150">
|
<animation type="WindowClose" reversible="false">
|
||||||
<description>User image</description>
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
<texture diffuse="user_image.png">userflyoutdefault.png</texture>
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
<aspectratio>keep</aspectratio>
|
</animation>
|
||||||
<align>center</align>
|
<centerleft>50%</centerleft>
|
||||||
<width>100%</width>
|
<centertop>50%</centertop>
|
||||||
<height>70</height>
|
<width>470</width>
|
||||||
<top>40</top>
|
<height>480</height>
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image" id="204">
|
|
||||||
<description>Busy animation</description>
|
|
||||||
<align>center</align>
|
|
||||||
<top>23</top>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>105</height>
|
|
||||||
<visible>False</visible>
|
|
||||||
<texture colordiffuse="ff13a134">fading_circle.png</texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<animation effect="rotate" start="360" end="0" center="auto" time="2000" loop="true" condition="true">conditional</animation>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label" id="151">
|
|
||||||
<description>Welcome user</description>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<font>font12</font>
|
|
||||||
<align>center</align>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
<top>120</top>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>separator</description>
|
|
||||||
<width>102%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>165</top>
|
|
||||||
<left>-10</left>
|
|
||||||
<texture colordiffuse="ff525252" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<description>Select server</description>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<label>$ADDON[plugin.video.emby 30607]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<align>center</align>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
<top>170</top>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
|
||||||
<top>290</top>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>184</height>
|
|
||||||
<control type="list" id="155">
|
|
||||||
<description>Connect servers</description>
|
|
||||||
<focusposition>0</focusposition>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<top>10</top>
|
|
||||||
<left>55</left>
|
|
||||||
<onup>155</onup>
|
|
||||||
<ondown condition="Control.IsVisible(205)">205</ondown>
|
|
||||||
<ondown condition="!Control.IsVisible(205)">206</ondown>
|
|
||||||
<onleft condition="Control.IsVisible(205)">205</onleft>
|
|
||||||
<onleft condition="!Control.IsVisible(205)">206</onleft>
|
|
||||||
<onright>155</onright>
|
|
||||||
<pagecontrol>60</pagecontrol>
|
|
||||||
<scrolltime tween="sine" easing="out">250</scrolltime>
|
|
||||||
<itemlayout height="46">
|
|
||||||
<control type="group">
|
|
||||||
<width>45</width>
|
|
||||||
<height>45</height>
|
|
||||||
<control type="image">
|
|
||||||
<description>Network</description>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<texture>network.png</texture>
|
|
||||||
<visible>StringCompare(ListItem.Property(server_type),network)</visible>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<description>Wifi</description>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<texture>wifi.png</texture>
|
|
||||||
<visible>StringCompare(ListItem.Property(server_type),wifi)</visible>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<width>300</width>
|
|
||||||
<height>40</height>
|
|
||||||
<left>55</left>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<textcolor>ff838383</textcolor>
|
|
||||||
<info>ListItem.Label</info>
|
|
||||||
</control>
|
|
||||||
</itemlayout>
|
|
||||||
<focusedlayout height="46">
|
|
||||||
<control type="group">
|
|
||||||
<width>45</width>
|
|
||||||
<height>45</height>
|
|
||||||
<control type="image">
|
|
||||||
<description>Network</description>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<texture>network.png</texture>
|
|
||||||
<visible>StringCompare(ListItem.Property(server_type),network)</visible>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<description>Wifi</description>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<texture>wifi.png</texture>
|
|
||||||
<visible>StringCompare(ListItem.Property(server_type),wifi)</visible>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<width>300</width>
|
|
||||||
<height>40</height>
|
|
||||||
<left>55</left>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<info>ListItem.Label</info>
|
|
||||||
<visible>Control.HasFocus(155)</visible>
|
|
||||||
</control>
|
|
||||||
<control type="label">
|
|
||||||
<width>300</width>
|
|
||||||
<height>40</height>
|
|
||||||
<left>55</left>
|
|
||||||
<font>font10</font>
|
|
||||||
<aligny>center</aligny>
|
|
||||||
<textcolor>ff838383</textcolor>
|
|
||||||
<info>ListItem.Label</info>
|
|
||||||
<visible>!Control.HasFocus(155)</visible>
|
|
||||||
</control>
|
|
||||||
</focusedlayout>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="scrollbar" id="60">
|
|
||||||
<left>395</left>
|
|
||||||
<top>10</top>
|
|
||||||
<width>5</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<onleft>155</onleft>
|
|
||||||
<onup>60</onup>
|
|
||||||
<ondown>60</ondown>
|
|
||||||
<texturesliderbackground colordiffuse="ff000000" border="4">box.png</texturesliderbackground>
|
|
||||||
<texturesliderbar colordiffuse="ff222222" border="4">box.png</texturesliderbar>
|
|
||||||
<texturesliderbarfocus colordiffuse="ff222222" border="4">box.png</texturesliderbarfocus>
|
|
||||||
<showonepage>false</showonepage>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<top>100%</top>
|
<top>-30</top>
|
||||||
<height>220</height>
|
<control type="image">
|
||||||
<control type="group">
|
<left>20</left>
|
||||||
<top>45</top>
|
<width>100%</width>
|
||||||
<height>150</height>
|
<height>25</height>
|
||||||
|
<texture>logo-white.png</texture>
|
||||||
|
<aspectratio align="left">keep</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="image" id="150">
|
||||||
|
<right>20</right>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<aspectratio align="right">keep</aspectratio>
|
||||||
|
<texture diffuse="user_image.png">userflyoutdefault.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>480</height>
|
||||||
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<centerleft>50%</centerleft>
|
||||||
|
<top>10</top>
|
||||||
|
<width>460</width>
|
||||||
|
<height>460</height>
|
||||||
|
<control type="grouplist" id="100">
|
||||||
|
<orientation>vertical</orientation>
|
||||||
|
<itemgap>0</itemgap>
|
||||||
|
<control type="label">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>75</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>white</textcolor>
|
||||||
|
<textshadow>66000000</textshadow>
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30607][/B]</label>
|
||||||
|
</control>
|
||||||
|
<control type="group" id="101">
|
||||||
|
<height>200</height>
|
||||||
|
<control type="list" id="155">
|
||||||
|
<centerleft>50%</centerleft>
|
||||||
|
<width>460</width>
|
||||||
|
<height>200</height>
|
||||||
|
<onup>noop</onup>
|
||||||
|
<onleft>close</onleft>
|
||||||
|
<onright>close</onright>
|
||||||
|
<ondown>205</ondown>
|
||||||
|
<itemlayout width="460" height="50">
|
||||||
|
<control type="group">
|
||||||
|
<left>20</left>
|
||||||
|
<top>5</top>
|
||||||
|
<width>40</width>
|
||||||
|
<control type="image">
|
||||||
|
<aspectratio>keep</aspectratio>
|
||||||
|
<texture>network.png</texture>
|
||||||
|
<visible>String.IsEqual(ListItem.Property(server_type),network)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<aspectratio>keep</aspectratio>
|
||||||
|
<texture>wifi.png</texture>
|
||||||
|
<visible>String.IsEqual(ListItem.Property(server_type),wifi)</visible>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<left>50</left>
|
||||||
|
<height>50</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
</control>
|
||||||
|
</itemlayout>
|
||||||
|
<focusedlayout width="460" height="50">
|
||||||
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>50</height>
|
||||||
|
<texture colordiffuse="ff222326">white.png</texture>
|
||||||
|
<visible>!Control.HasFocus(155)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>50</height>
|
||||||
|
<texture colordiffuse="ff303034">white.png</texture>
|
||||||
|
<visible>Control.HasFocus(155)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<left>20</left>
|
||||||
|
<width>40</width>
|
||||||
|
<control type="image">
|
||||||
|
<description>Network</description>
|
||||||
|
<aspectratio>keep</aspectratio>
|
||||||
|
<texture>network.png</texture>
|
||||||
|
<visible>String.IsEqual(ListItem.Property(server_type),network)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<description>Wifi</description>
|
||||||
|
<aspectratio>keep</aspectratio>
|
||||||
|
<texture>wifi.png</texture>
|
||||||
|
<visible>String.IsEqual(ListItem.Property(server_type),wifi)</visible>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<left>50</left>
|
||||||
|
<height>50</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<scroll>true</scroll>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
</control>
|
||||||
|
</focusedlayout>
|
||||||
|
</control>
|
||||||
|
<control type="image" id="204">
|
||||||
|
<centerleft>50%</centerleft>
|
||||||
|
<description>Busy animation</description>
|
||||||
|
<align>center</align>
|
||||||
|
<width>120</width>
|
||||||
|
<height>200</height>
|
||||||
|
<visible>false</visible>
|
||||||
|
<texture colordiffuse="ff52b54b">spinner.gif</texture>
|
||||||
|
<aspectratio>keep</aspectratio>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="label" id="102">
|
||||||
|
<description>spacer</description>
|
||||||
|
<height>20</height>
|
||||||
|
</control>
|
||||||
<control type="button" id="205">
|
<control type="button" id="205">
|
||||||
<visible>True</visible>
|
<label>[B]$ADDON[plugin.video.emby 30600][/B]</label>
|
||||||
<description>Sign in Connect</description>
|
<width>426</width>
|
||||||
<texturenofocus border="5" colordiffuse="ff0b8628">box.png</texturenofocus>
|
|
||||||
<texturefocus border="5" colordiffuse="ff13a134">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30600][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<width>350</width>
|
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
<left>50</left>
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
<onup>155</onup>
|
<onup>155</onup>
|
||||||
<ondown>206</ondown>
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="button" id="206">
|
<control type="button" id="206">
|
||||||
<description>Manually add server</description>
|
<label>[B]$ADDON[plugin.video.emby 30611][/B]</label>
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
<width>426</width>
|
||||||
<texturefocus border="5" colordiffuse="ff525252">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30611][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<top>55</top>
|
|
||||||
<width>350</width>
|
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
<left>50</left>
|
<font>font10</font>
|
||||||
<onup condition="Control.IsVisible(205)">205</onup>
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
<onup condition="!Control.IsVisible(205)">155</onup>
|
<focusedcolor>white</focusedcolor>
|
||||||
<ondown>201</ondown>
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<onup>155</onup>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="button" id="201">
|
<control type="button" id="201">
|
||||||
<description>Cancel</description>
|
<label>[B]$ADDON[plugin.video.emby 30606][/B]</label>
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
<width>426</width>
|
||||||
<texturefocus border="5" colordiffuse="ff525252">box.png</texturefocus>
|
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30606][/B][/UPPERCASE]</label>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
|
||||||
<focusedcolor>white</focusedcolor>
|
|
||||||
<align>center</align>
|
|
||||||
<top>110</top>
|
|
||||||
<width>350</width>
|
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
<left>50</left>
|
<font>font10</font>
|
||||||
<onup>206</onup>
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<onup>155</onup>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
</control>
|
||||||
<control type="group" id="202">
|
<control type="group" id="202">
|
||||||
<top>100%</top>
|
<top>480</top>
|
||||||
<visible>False</visible>
|
<visible>False</visible>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Message box</description>
|
<width>100%</width>
|
||||||
<texture colordiffuse="ff222222">white.png</texture>
|
<height>70</height>
|
||||||
<width>100%</width>
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
<height>50</height>
|
</control>
|
||||||
<top>20</top>
|
<control type="label" id="203">
|
||||||
</control>
|
<top>10</top>
|
||||||
|
<height>50</height>
|
||||||
<control type="label" id="203">
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
<description>Message</description>
|
<scroll>true</scroll>
|
||||||
<textcolor>white</textcolor>
|
<shadowcolor>66000000</shadowcolor>
|
||||||
<font>font10</font>
|
<font>font10</font>
|
||||||
<aligny>center</aligny>
|
<textoffsetx>20</textoffsetx>
|
||||||
<align>center</align>
|
<aligny>center</aligny>
|
||||||
<height>50</height>
|
<align>center</align>>
|
||||||
<top>20</top>
|
|
||||||
</control>
|
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
|
|
@ -1,194 +1,226 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<window>
|
<window>
|
||||||
<defaultcontrol always="true">155</defaultcontrol>
|
<defaultcontrol always="true">155</defaultcontrol>
|
||||||
<zorder>0</zorder>
|
|
||||||
<include>dialogeffect</include>
|
|
||||||
<controls>
|
<controls>
|
||||||
<control type="image">
|
|
||||||
<description>Background fade</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<texture>emby-bg-fade.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>715</width>
|
|
||||||
<left>32%</left>
|
|
||||||
<top>20%</top>
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<top>0</top>
|
||||||
<texture border="6" colordiffuse="ff111111">white.png</texture>
|
<bottom>0</bottom>
|
||||||
<width>100%</width>
|
<left>0</left>
|
||||||
<height>525</height>
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Emby logo</description>
|
|
||||||
<texture>logo-white.png</texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>120</width>
|
|
||||||
<height>49</height>
|
|
||||||
<top>30</top>
|
|
||||||
<left>25</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<description>Please sign in</description>
|
|
||||||
<label>$ADDON[plugin.video.emby 30612]</label>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<font>font12</font>
|
|
||||||
<aligny>top</aligny>
|
|
||||||
<align>center</align>
|
|
||||||
<top>80</top>
|
|
||||||
<width>100%</width>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<top>100</top>
|
<animation type="WindowOpen" reversible="false">
|
||||||
<width>620</width>
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
<height>245</height>
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
<left>50</left>
|
</animation>
|
||||||
<control type="list" id="155">
|
<animation type="WindowClose" reversible="false">
|
||||||
<description>Select User</description>
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
<focusposition>0</focusposition>
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
<width>100%</width>
|
</animation>
|
||||||
<top>40</top>
|
<centerleft>50%</centerleft>
|
||||||
<onleft>155</onleft>
|
<centertop>50%</centertop>
|
||||||
<onright>155</onright>
|
<width>920</width>
|
||||||
<ondown>200</ondown>
|
<height>570</height>
|
||||||
<pagecontrol>60</pagecontrol>
|
|
||||||
<orientation>horizontal</orientation>
|
|
||||||
<scrolltime tween="sine" easing="out">250</scrolltime>
|
|
||||||
<itemlayout width="155">
|
|
||||||
<control type="group">
|
|
||||||
<width>150</width>
|
|
||||||
<control type="image">
|
|
||||||
<description>User image</description>
|
|
||||||
<colordiffuse>ff888888</colordiffuse>
|
|
||||||
<info>ListItem.Icon</info>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>150</height>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Background label</description>
|
|
||||||
<texture colordiffuse="ff222222">white.png</texture>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
<top>150</top>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<width>100%</width>
|
|
||||||
<align>center</align>
|
|
||||||
<height>50</height>
|
|
||||||
<top>150</top>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<info>ListItem.Label</info>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
</itemlayout>
|
|
||||||
<focusedlayout width="155">
|
|
||||||
<control type="group">
|
|
||||||
<width>150</width>
|
|
||||||
<control type="image">
|
|
||||||
<description>User image</description>
|
|
||||||
<info>ListItem.Icon</info>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>150</height>
|
|
||||||
<visible>Control.HasFocus(155)</visible>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<description>User image</description>
|
|
||||||
<colordiffuse>ff888888</colordiffuse>
|
|
||||||
<info>ListItem.Icon</info>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>150</height>
|
|
||||||
<visible>!Control.HasFocus(155)</visible>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Background label</description>
|
|
||||||
<texture colordiffuse="ff333333">white.png</texture>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
<top>150</top>
|
|
||||||
<visible>Control.HasFocus(155)</visible>
|
|
||||||
</control>
|
|
||||||
<control type="image">
|
|
||||||
<description>Background label</description>
|
|
||||||
<texture colordiffuse="ff222222">white.png</texture>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>50</height>
|
|
||||||
<top>150</top>
|
|
||||||
<visible>!Control.HasFocus(155)</visible>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="label">
|
|
||||||
<width>100%</width>
|
|
||||||
<align>center</align>
|
|
||||||
<height>50</height>
|
|
||||||
<top>150</top>
|
|
||||||
<font>font10</font>
|
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<info>ListItem.Label</info>
|
|
||||||
</control>
|
|
||||||
</control>
|
|
||||||
</focusedlayout>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="scrollbar" id="60">
|
|
||||||
<top>100%</top>
|
|
||||||
<width>615</width>
|
|
||||||
<height>5</height>
|
|
||||||
<onleft>155</onleft>
|
|
||||||
<onleft>60</onleft>
|
|
||||||
<onright>60</onright>
|
|
||||||
<texturesliderbackground colordiffuse="ff000000" border="4">box.png</texturesliderbackground>
|
|
||||||
<texturesliderbar colordiffuse="ff222222" border="4">box.png</texturesliderbar>
|
|
||||||
<texturesliderbarfocus colordiffuse="ff222222" border="4">box.png</texturesliderbarfocus>
|
|
||||||
<showonepage>false</showonepage>
|
|
||||||
<orientation>horizontal</orientation>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>615</width>
|
<top>-30</top>
|
||||||
<height>325</height>
|
<control type="image">
|
||||||
<top>100%</top>
|
<left>20</left>
|
||||||
<control type="group">
|
<width>100%</width>
|
||||||
<control type="button" id="200">
|
<height>25</height>
|
||||||
<description>Manual Login button</description>
|
<texture>logo-white.png</texture>
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
<aspectratio align="left">keep</aspectratio>
|
||||||
<texturefocus border="5" colordiffuse="ff585858">box.png</texturefocus>
|
</control>
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30540][/B][/UPPERCASE]</label>
|
</control>
|
||||||
<align>center</align>
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>570</height>
|
||||||
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<centerleft>50%</centerleft>
|
||||||
|
<top>10</top>
|
||||||
|
<width>908</width>
|
||||||
|
<height>550</height>
|
||||||
|
<control type="grouplist" id="100">
|
||||||
|
<orientation>vertical</orientation>
|
||||||
|
<itemgap>0</itemgap>
|
||||||
|
<control type="label">
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<height>50</height>
|
<height>75</height>
|
||||||
<top>35</top>
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
<font>font10</font>
|
<font>font10</font>
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
<textcolor>white</textcolor>
|
||||||
<focusedcolor>white</focusedcolor>
|
<textshadow>66000000</textshadow>
|
||||||
<ondown>201</ondown>
|
<label>[B]$ADDON[plugin.video.emby 30612][/B]</label>
|
||||||
<onup>155</onup>
|
|
||||||
</control>
|
</control>
|
||||||
|
<control type="list" id="155">
|
||||||
<control type="button" id="201">
|
<animation effect="slide" time="0" start="0,0" end="148,0" condition="Integer.IsEqual(Container(155).NumItems,2)">Conditional</animation>
|
||||||
<description>Cancel</description>
|
<animation effect="slide" time="0" start="0,0" end="296,0" condition="Integer.IsEqual(Container(155).NumItems,1)">Conditional</animation>
|
||||||
<texturenofocus border="5" colordiffuse="ff464646">box.png</texturenofocus>
|
<centerleft>50%</centerleft>
|
||||||
<texturefocus border="5" colordiffuse="ff585858">box.png</texturefocus>
|
<width>908</width>
|
||||||
<label>[UPPERCASE][B]$ADDON[plugin.video.emby 30606][/B][/UPPERCASE]</label>
|
<height>362</height>
|
||||||
<font>font10</font>
|
<onup>noop</onup>
|
||||||
<textcolor>ffa6a6a6</textcolor>
|
<onleft>noop</onleft>
|
||||||
<focusedcolor>white</focusedcolor>
|
<onright>noop</onright>
|
||||||
<align>center</align>
|
<ondown>200</ondown>
|
||||||
<width>100%</width>
|
<orientation>horizontal</orientation>
|
||||||
|
<itemlayout width="296">
|
||||||
|
<control type="group">
|
||||||
|
<left>20</left>
|
||||||
|
<top>10</top>
|
||||||
|
<control type="image">
|
||||||
|
<top>-2</top>
|
||||||
|
<left>-2</left>
|
||||||
|
<width>282</width>
|
||||||
|
<height>282</height>
|
||||||
|
<texture>items/shadow_square.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>276</width>
|
||||||
|
<height>322</height>
|
||||||
|
<texture colordiffuse="ff121314">white.png</texture>
|
||||||
|
<aspectratio scalediffuse="false">stretch</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>276</width>
|
||||||
|
<height>276</height>
|
||||||
|
<texture colordiffuse="ff0288d1" diffuse="items/mask_square.png">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<visible>String.IsEmpty(ListItem.Icon) | String.Contains(ListItem.Icon,logindefault.png)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>276</width>
|
||||||
|
<height>276</height>
|
||||||
|
<texture diffuse="items/mask_square.png" background="true">$INFO[ListItem.Icon]</texture>
|
||||||
|
<aspectratio scalediffuse="false">stretch</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<top>285</top>
|
||||||
|
<width>276</width>
|
||||||
|
<control type="label">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>30</height>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<scroll>Control.HasFocus(155)</scroll>
|
||||||
|
<align>center</align>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</itemlayout>
|
||||||
|
<focusedlayout width="296">
|
||||||
|
<control type="group">
|
||||||
|
<left>20</left>
|
||||||
|
<top>10</top>
|
||||||
|
<control type="image">
|
||||||
|
<top>-2</top>
|
||||||
|
<left>-2</left>
|
||||||
|
<width>282</width>
|
||||||
|
<height>282</height>
|
||||||
|
<texture>items/shadow_square.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>276</width>
|
||||||
|
<height>322</height>
|
||||||
|
<texture colordiffuse="ff121314">white.png</texture>
|
||||||
|
<aspectratio scalediffuse="false">stretch</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>276</width>
|
||||||
|
<height>276</height>
|
||||||
|
<texture colordiffuse="ff0288d1" diffuse="items/mask_square.png">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<visible>String.IsEmpty(ListItem.Icon) | String.Contains(ListItem.Icon,logindefault.png)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>276</width>
|
||||||
|
<height>276</height>
|
||||||
|
<texture diffuse="items/mask_square.png" background="true">$INFO[ListItem.Icon]</texture>
|
||||||
|
<aspectratio scalediffuse="false">stretch</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<left>-12</left>
|
||||||
|
<top>-7</top>
|
||||||
|
<width>300</width>
|
||||||
|
<height>300</height>
|
||||||
|
<texture colordiffuse="FF388e3c">items/focus_square.png</texture>
|
||||||
|
<aspectratio>scale</aspectratio>
|
||||||
|
<animation effect="fade" start="0" end="100" time="200" tween="sine">Focus</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="0">UnFocus</animation>
|
||||||
|
<visible>Control.HasFocus(155)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<top>285</top>
|
||||||
|
<width>276</width>
|
||||||
|
<control type="label">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>30</height>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>white</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<scroll>Control.HasFocus(155)</scroll>
|
||||||
|
<align>center</align>
|
||||||
|
<visible>Control.HasFocus(155)</visible>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>30</height>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<scroll>false</scroll>
|
||||||
|
<align>center</align>
|
||||||
|
<visible>!Control.HasFocus(155)</visible>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</focusedlayout>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="200">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30540][/B]</label>
|
||||||
|
<width>874</width>
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
<top>90</top>
|
<font>font10</font>
|
||||||
<onup>200</onup>
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<onup>155</onup>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="201">
|
||||||
|
<label>[B]$ADDON[plugin.video.emby 30606][/B]</label>
|
||||||
|
<width>874</width>
|
||||||
|
<height>50</height>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<focusedcolor>white</focusedcolor>
|
||||||
|
<selectedcolor>ffe1e1e1</selectedcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>center</align>
|
||||||
|
<texturefocus border="10" colordiffuse="ff52b54b">buttons/shadow_smallbutton.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff464646">buttons/shadow_smallbutton.png</texturenofocus>
|
||||||
|
<pulseonselect>no</pulseonselect>
|
||||||
|
<onup>155</onup>
|
||||||
|
<animation effect="slide" time="0" end="17,0" condition="true">Conditional</animation>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
</control>
|
</control>
|
||||||
|
|
|
@ -1,105 +1,97 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<window>
|
<window>
|
||||||
<defaultcontrol always="true">155</defaultcontrol>
|
<defaultcontrol always="true">155</defaultcontrol>
|
||||||
<zorder>0</zorder>
|
|
||||||
<include>dialogeffect</include>
|
|
||||||
<controls>
|
<controls>
|
||||||
<control type="image">
|
|
||||||
<description>Background fade</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>100%</height>
|
|
||||||
<texture>emby-bg-fade.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>450</width>
|
|
||||||
<left>38%</left>
|
|
||||||
<top>36%</top>
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<top>0</top>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<bottom>0</bottom>
|
||||||
<height>90</height>
|
<left>0</left>
|
||||||
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
</control>
|
</control>
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>Emby logo</description>
|
|
||||||
<texture>emby-icon.png</texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<height>30</height>
|
|
||||||
<top>20</top>
|
|
||||||
<left>370</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image" id="150">
|
|
||||||
<description>User image</description>
|
|
||||||
<texture diffuse="user_image.png"></texture>
|
|
||||||
<aspectratio>keep</aspectratio>
|
|
||||||
<height>34</height>
|
|
||||||
<top>20</top>
|
|
||||||
<left>285</left>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="image">
|
|
||||||
<description>separator</description>
|
|
||||||
<width>100%</width>
|
|
||||||
<height>0.5</height>
|
|
||||||
<top>70</top>
|
|
||||||
<left>-5</left>
|
|
||||||
<texture colordiffuse="ff484848" border="90,3,90,3">emby-separator.png</texture>
|
|
||||||
</control>
|
|
||||||
|
|
||||||
<control type="group">
|
<control type="group">
|
||||||
<width>450</width>
|
<animation type="WindowOpen" reversible="false">
|
||||||
<top>90</top>
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
<control type="list" id="155">
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
|
</animation>
|
||||||
|
<animation type="WindowClose" reversible="false">
|
||||||
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
|
</animation>
|
||||||
|
<centerleft>50%</centerleft>
|
||||||
|
<centertop>50%</centertop>
|
||||||
|
<width>370</width>
|
||||||
|
<height>220</height>
|
||||||
|
<control type="group">
|
||||||
|
<top>-30</top>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<texture>logo-white.png</texture>
|
||||||
|
<aspectratio align="left">keep</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="image" id="150">
|
||||||
|
<right>20</right>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<aspectratio align="right">keep</aspectratio>
|
||||||
|
<texture diffuse="user_image.png">userflyoutdefault.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
<width>100%</width>
|
<width>100%</width>
|
||||||
<height>100%</height>
|
<height>220</height>
|
||||||
<align>center</align>
|
<texture colordiffuse="ff222326" border="10">dialogs/dialog_back.png</texture>
|
||||||
<onup>155</onup>
|
</control>
|
||||||
<ondown>155</ondown>
|
<control type="list" id="155">
|
||||||
<onleft>155</onleft>
|
<centerleft>50%</centerleft>
|
||||||
<onright>155</onright>
|
<top>10</top>
|
||||||
<scrolltime>200</scrolltime>
|
<width>360</width>
|
||||||
<itemlayout height="55">
|
<height>200</height>
|
||||||
<control type="image">
|
<onup>noop</onup>
|
||||||
<description>Background box</description>
|
<onleft>close</onleft>
|
||||||
<width>450</width>
|
<onright>close</onright>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<ondown>noop</ondown>
|
||||||
</control>
|
<itemlayout width="360" height="50">
|
||||||
<control type="label">
|
<control type="label">
|
||||||
<width>400</width>
|
<width>100%</width>
|
||||||
<font>font11</font>
|
<height>50</height>
|
||||||
<textcolor>ff525252</textcolor>
|
|
||||||
<left>25</left>
|
|
||||||
<align>center</align>
|
|
||||||
<aligny>center</aligny>
|
<aligny>center</aligny>
|
||||||
<info>ListItem.Label</info>
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
</control>
|
</control>
|
||||||
</itemlayout>
|
</itemlayout>
|
||||||
|
<focusedlayout width="360" height="50">
|
||||||
<focusedlayout height="55">
|
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<description>Background box</description>
|
<width>100%</width>
|
||||||
<width>450</width>
|
<height>50</height>
|
||||||
<texture colordiffuse="ff111111">white.png</texture>
|
<texture colordiffuse="ff222326">white.png</texture>
|
||||||
|
<visible>!Control.HasFocus(155)</visible>
|
||||||
</control>
|
</control>
|
||||||
<control type="image">
|
<control type="image">
|
||||||
<width>400</width>
|
<width>100%</width>
|
||||||
<left>25</left>
|
<height>50</height>
|
||||||
<align>center</align>
|
<texture colordiffuse="ff303034">white.png</texture>
|
||||||
<texture border="5" colordiffuse="ff222222">white.png</texture>
|
|
||||||
<visible>Control.HasFocus(155)</visible>
|
<visible>Control.HasFocus(155)</visible>
|
||||||
<animation effect="fade" time="300">Visible</animation>
|
|
||||||
<animation effect="fade" time="300">Hidden</animation>
|
|
||||||
</control>
|
</control>
|
||||||
<control type="label">
|
<control type="label">
|
||||||
<width>400</width>
|
<width>100%</width>
|
||||||
<font>font11</font>
|
<height>50</height>
|
||||||
<textcolor>white</textcolor>
|
|
||||||
<left>25</left>
|
|
||||||
<align>center</align>
|
|
||||||
<aligny>center</aligny>
|
<aligny>center</aligny>
|
||||||
<info>ListItem.Label</info>
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<scroll>true</scroll>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
</control>
|
</control>
|
||||||
</focusedlayout>
|
</focusedlayout>
|
||||||
</control>
|
</control>
|
||||||
|
|
110
resources/skins/default/1080i/script-emby-resume.xml
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<window id="3301" type="dialog">
|
||||||
|
<defaultcontrol always="true">100</defaultcontrol>
|
||||||
|
<controls>
|
||||||
|
<control type="group">
|
||||||
|
<control type="image">
|
||||||
|
<top>0</top>
|
||||||
|
<bottom>0</bottom>
|
||||||
|
<left>0</left>
|
||||||
|
<right>0</right>
|
||||||
|
<texture colordiffuse="CC000000">white.png</texture>
|
||||||
|
<aspectratio>stretch</aspectratio>
|
||||||
|
<animation effect="fade" end="100" time="200">WindowOpen</animation>
|
||||||
|
<animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
|
||||||
|
</control>
|
||||||
|
<control type="group">
|
||||||
|
<animation effect="slide" time="0" end="0,-15" condition="true">Conditional</animation>
|
||||||
|
<animation type="WindowOpen" reversible="false">
|
||||||
|
<effect type="zoom" start="80" end="100" center="960,540" delay="160" tween="circle" easin="out" time="240" />
|
||||||
|
<effect type="fade" delay="160" end="100" time="240" />
|
||||||
|
</animation>
|
||||||
|
<animation type="WindowClose" reversible="false">
|
||||||
|
<effect type="zoom" start="100" end="80" center="960,540" easing="in" tween="circle" easin="out" time="240" />
|
||||||
|
<effect type="fade" start="100" end="0" time="240" />
|
||||||
|
</animation>
|
||||||
|
<centerleft>50%</centerleft>
|
||||||
|
<centertop>50%</centertop>
|
||||||
|
<width>20%</width>
|
||||||
|
<height>90%</height>
|
||||||
|
<control type="grouplist" id="100">
|
||||||
|
<orientation>vertical</orientation>
|
||||||
|
<left>0</left>
|
||||||
|
<right>0</right>
|
||||||
|
<height>auto</height>
|
||||||
|
<align>center</align>
|
||||||
|
<itemgap>0</itemgap>
|
||||||
|
<onright>close</onright>
|
||||||
|
<onleft>close</onleft>
|
||||||
|
<usecontrolcoords>true</usecontrolcoords>
|
||||||
|
<control type="group">
|
||||||
|
<height>30</height>
|
||||||
|
<control type="image">
|
||||||
|
<left>20</left>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<texture>logo-white.png</texture>
|
||||||
|
<aspectratio align="left">keep</aspectratio>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<right>20</right>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<aspectratio align="right">keep</aspectratio>
|
||||||
|
<texture diffuse="user_image.png">$INFO[Window(Home).Property(EmbyUserImage)]</texture>
|
||||||
|
<visible>!String.IsEmpty(Window(Home).Property(EmbyUserImage))</visible>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<right>20</right>
|
||||||
|
<width>100%</width>
|
||||||
|
<height>25</height>
|
||||||
|
<aspectratio align="right">keep</aspectratio>
|
||||||
|
<texture diffuse="user_image.png">userflyoutdefault.png</texture>
|
||||||
|
<visible>String.IsEmpty(Window(Home).Property(EmbyUserImage))</visible>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>10</height>
|
||||||
|
<texture border="5" colordiffuse="ff222326">dialogs/menu_top.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="3010">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>50</height>
|
||||||
|
<align>left</align>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<disabledcolor>FF404040</disabledcolor>
|
||||||
|
<texturefocus border="10" colordiffuse="ff303034">dialogs/menu_back.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff222326">dialogs/menu_back.png</texturenofocus>
|
||||||
|
<alttexturefocus border="10" colordiffuse="ff303034">dialogs/menu_back.png</alttexturefocus>
|
||||||
|
<alttexturenofocus border="10" colordiffuse="ff222326">dialogs/menu_back.png</alttexturenofocus>
|
||||||
|
</control>
|
||||||
|
<control type="button" id="3011">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>50</height>
|
||||||
|
<align>left</align>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<font>font10</font>
|
||||||
|
<textcolor>ffe1e1e1</textcolor>
|
||||||
|
<shadowcolor>66000000</shadowcolor>
|
||||||
|
<disabledcolor>FF404040</disabledcolor>
|
||||||
|
<texturefocus border="10" colordiffuse="ff303034">dialogs/menu_back.png</texturefocus>
|
||||||
|
<texturenofocus border="10" colordiffuse="ff222326">dialogs/menu_back.png</texturenofocus>
|
||||||
|
<alttexturefocus border="10" colordiffuse="ff303034">dialogs/menu_back.png</alttexturefocus>
|
||||||
|
<alttexturenofocus border="10" colordiffuse="ff222326">dialogs/menu_back.png</alttexturenofocus>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<width>100%</width>
|
||||||
|
<height>10</height>
|
||||||
|
<texture border="5" colordiffuse="ff222326">dialogs/menu_bottom.png</texture>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</controls>
|
||||||
|
</window>
|
BIN
resources/skins/default/media/buttons/shadow_smallbutton.png
Normal file
After Width: | Height: | Size: 407 B |
BIN
resources/skins/default/media/dialogs/dialog_back.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
resources/skins/default/media/dialogs/menu_back.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
resources/skins/default/media/dialogs/menu_bottom.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
resources/skins/default/media/dialogs/menu_top.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
resources/skins/default/media/dialogs/white.jpg
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
resources/skins/default/media/items/focus_square.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
resources/skins/default/media/items/logindefault.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
resources/skins/default/media/items/mask_square.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
resources/skins/default/media/items/shadow_square.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 3.7 KiB |
BIN
resources/skins/default/media/spinner.gif
Normal file
After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.9 KiB |