mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-05-09 19:05:09 +00:00
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)
This commit is contained in:
parent
6005555b37
commit
9ac37a1c40
56 changed files with 2679 additions and 2378 deletions
|
@ -7,6 +7,8 @@
|
|||
import logging
|
||||
from utils import settings
|
||||
|
||||
import artwork
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
|
@ -19,6 +21,7 @@ class API(object):
|
|||
def __init__(self, item):
|
||||
# item is the api response
|
||||
self.item = item
|
||||
self.artwork = artwork.Artwork()
|
||||
|
||||
def get_userdata(self):
|
||||
# Default
|
||||
|
@ -71,12 +74,8 @@ class API(object):
|
|||
writer = []
|
||||
cast = []
|
||||
|
||||
try:
|
||||
people = self.item['People']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
for person in people:
|
||||
if 'People' in self.item:
|
||||
for person in self.item['People']:
|
||||
|
||||
type_ = person['Type']
|
||||
name = person['Name']
|
||||
|
@ -95,6 +94,26 @@ class API(object):
|
|||
'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):
|
||||
|
||||
video_tracks = []
|
||||
|
|
|
@ -468,7 +468,7 @@ class Artwork(object):
|
|||
image = ""
|
||||
person_id = person['Id']
|
||||
|
||||
if "PrimaryImageTag" in person:
|
||||
if 'PrimaryImageTag' in person:
|
||||
image = (
|
||||
"%s/emby/Items/%s/Images/Primary?"
|
||||
"MaxWidth=400&MaxHeight=400&Index=0&Tag=%s"
|
||||
|
@ -518,14 +518,14 @@ class Artwork(object):
|
|||
tag, custom_query))
|
||||
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
|
||||
|
||||
artwork = ("%s/emby/Items/%s/Images/%s/0?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (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
|
||||
get_backdrops(item_id, backdrops)
|
||||
|
@ -554,6 +554,9 @@ class Artwork(object):
|
|||
get_artwork(item['Parent%sItemId' % parent_artwork], 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
|
||||
if not all_artwork['Primary']:
|
||||
|
||||
|
|
|
@ -78,22 +78,7 @@ class ClientInfo(object):
|
|||
|
||||
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):
|
||||
# Reset the file
|
||||
xbmcvfs.delete(emby_guid)
|
||||
|
||||
guid = xbmcvfs.File(emby_guid)
|
||||
|
|
|
@ -4,21 +4,26 @@
|
|||
|
||||
import logging
|
||||
import sys
|
||||
from datetime import timedelta
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
|
||||
import api
|
||||
import read_embyserver as embyserver
|
||||
import playbackutils as pbutils
|
||||
import embydb_functions as embydb
|
||||
import musicutils as musicutils
|
||||
from utils import settings, dialog, language as lang
|
||||
from dialogs import context
|
||||
from dialogs import context, resume
|
||||
from database import DatabaseConn
|
||||
|
||||
#################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
addon = xbmcaddon.Addon('plugin.video.emby')
|
||||
|
||||
XML_PATH = (addon.getAddonInfo('path'), "default", "1080i")
|
||||
OPTIONS = {
|
||||
|
||||
'Refresh': lang(30410),
|
||||
|
@ -38,7 +43,7 @@ class ContextMenu(object):
|
|||
_selected_option = None
|
||||
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, force_transcode=False):
|
||||
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
|
||||
|
@ -53,7 +58,10 @@ class ContextMenu(object):
|
|||
self.item = self.emby.getItem(self.item_id)
|
||||
self.api = api.API(self.item)
|
||||
|
||||
if self._select_menu():
|
||||
if force_transcode:
|
||||
self._force_transcode()
|
||||
|
||||
elif self._select_menu():
|
||||
self._action_menu()
|
||||
|
||||
if self._selected_option in (OPTIONS['Delete'], OPTIONS['AddFav'],
|
||||
|
@ -104,10 +112,6 @@ class ContextMenu(object):
|
|||
userdata = self.api.get_userdata()
|
||||
options = []
|
||||
|
||||
if self.item_type in ("movie", "episode", "song"):
|
||||
#options.append(OPTIONS['Transcode'])
|
||||
pass
|
||||
|
||||
if userdata['Favorite']:
|
||||
# Remove from emby favourites
|
||||
options.append(OPTIONS['RemoveFav'])
|
||||
|
@ -126,9 +130,7 @@ class ContextMenu(object):
|
|||
# Addon settings
|
||||
options.append(OPTIONS['Addon'])
|
||||
|
||||
addon = xbmcaddon.Addon('plugin.video.emby')
|
||||
context_menu = context.ContextMenu("script-emby-context.xml", addon.getAddonInfo('path'),
|
||||
"default", "1080i")
|
||||
context_menu = context.ContextMenu("script-emby-context.xml", *XML_PATH)
|
||||
context_menu.set_options(options)
|
||||
context_menu.doModal()
|
||||
|
||||
|
@ -141,10 +143,7 @@ class ContextMenu(object):
|
|||
|
||||
selected = self._selected_option.decode('utf-8')
|
||||
|
||||
if selected == OPTIONS['Transcode']:
|
||||
pass
|
||||
|
||||
elif selected == OPTIONS['Refresh']:
|
||||
if selected == OPTIONS['Refresh']:
|
||||
self.emby.refreshItem(self.item_id)
|
||||
|
||||
elif selected == OPTIONS['AddFav']:
|
||||
|
@ -198,3 +197,24 @@ class ContextMenu(object):
|
|||
if delete:
|
||||
log.info("Deleting request: %s", 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:
|
||||
self.list_.addItem(self._add_listitem(option))
|
||||
|
||||
self.background = self._add_editcontrol(730, height, 30, 450)
|
||||
self.setFocus(self.list_)
|
||||
|
||||
def onAction(self, action):
|
||||
|
|
|
@ -22,6 +22,8 @@ SIGN_IN = 200
|
|||
CANCEL = 201
|
||||
ERROR_TOGGLE = 202
|
||||
ERROR_MSG = 203
|
||||
USER = 204
|
||||
PASSWORD = 205
|
||||
ERROR = {
|
||||
'Invalid': 1,
|
||||
'Empty': 2
|
||||
|
@ -52,21 +54,14 @@ class LoginConnect(xbmcgui.WindowXMLDialog):
|
|||
|
||||
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.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.remind_button = self.getControl(CANCEL)
|
||||
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
||||
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):
|
||||
|
||||
if control == SIGN_IN:
|
||||
|
@ -97,23 +92,6 @@ class LoginConnect(xbmcgui.WindowXMLDialog):
|
|||
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||
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):
|
||||
|
||||
result = self.connect_manager.loginToConnect(username, password)
|
||||
|
|
|
@ -23,6 +23,8 @@ SIGN_IN = 200
|
|||
CANCEL = 201
|
||||
ERROR_TOGGLE = 202
|
||||
ERROR_MSG = 203
|
||||
USER = 204
|
||||
PASSWORD = 205
|
||||
ERROR = {
|
||||
'Invalid': 1,
|
||||
'Empty': 2
|
||||
|
@ -50,7 +52,7 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
|||
self.server = server
|
||||
|
||||
def set_user(self, user):
|
||||
self.username = user or {}
|
||||
self.username = user or None
|
||||
|
||||
def get_user(self):
|
||||
return self._user
|
||||
|
@ -61,8 +63,8 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
|||
self.cancel_button = self.getControl(CANCEL)
|
||||
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
||||
self.error_msg = self.getControl(ERROR_MSG)
|
||||
self.user_field = self._add_editcontrol(725, 400, 40, 500)
|
||||
self.password_field = self._add_editcontrol(725, 475, 40, 500, password=1)
|
||||
self.user_field = self.getControl(USER)
|
||||
self.password_field = self.getControl(PASSWORD)
|
||||
|
||||
if self.username:
|
||||
self.user_field.setText(self.username)
|
||||
|
@ -70,13 +72,6 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
|||
else:
|
||||
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):
|
||||
|
||||
if control == SIGN_IN:
|
||||
|
@ -106,23 +101,6 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
|||
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||
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):
|
||||
|
||||
try:
|
||||
|
|
60
resources/lib/dialogs/resume.py
Normal file
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_MOUSE_LEFT_CLICK = 100
|
||||
USER_IMAGE = 150
|
||||
USER_NAME = 151
|
||||
LIST = 155
|
||||
CANCEL = 201
|
||||
MESSAGE_BOX = 202
|
||||
|
@ -35,7 +34,6 @@ MANUAL_SERVER = 206
|
|||
|
||||
class ServerConnect(xbmcgui.WindowXMLDialog):
|
||||
|
||||
username = ""
|
||||
user_image = None
|
||||
servers = []
|
||||
|
||||
|
@ -49,7 +47,7 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
|
|||
xbmcgui.WindowXMLDialog.__init__(self, *args, **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():
|
||||
setattr(self, key, value)
|
||||
|
||||
|
@ -77,13 +75,11 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
|
|||
server_type = "wifi" if server.get('ExchangeToken') else "network"
|
||||
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:
|
||||
self.getControl(USER_IMAGE).setImage(self.user_image)
|
||||
|
||||
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:
|
||||
self.setFocus(self.list_)
|
||||
|
|
|
@ -24,6 +24,8 @@ CONNECT = 200
|
|||
CANCEL = 201
|
||||
ERROR_TOGGLE = 202
|
||||
ERROR_MSG = 203
|
||||
HOST = 204
|
||||
PORT = 205
|
||||
ERROR = {
|
||||
'Invalid': 1,
|
||||
'Empty': 2
|
||||
|
@ -57,19 +59,12 @@ class ServerManual(xbmcgui.WindowXMLDialog):
|
|||
self.cancel_button = self.getControl(CANCEL)
|
||||
self.error_toggle = self.getControl(ERROR_TOGGLE)
|
||||
self.error_msg = self.getControl(ERROR_MSG)
|
||||
self.host_field = self._add_editcontrol(725, 400, 40, 500)
|
||||
self.port_field = self._add_editcontrol(725, 525, 40, 500)
|
||||
self.host_field = self.getControl(HOST)
|
||||
self.port_field = self.getControl(PORT)
|
||||
|
||||
self.port_field.setText('8096')
|
||||
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):
|
||||
|
||||
if control == CONNECT:
|
||||
|
@ -99,22 +94,6 @@ class ServerManual(xbmcgui.WindowXMLDialog):
|
|||
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
|
||||
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):
|
||||
|
||||
server_address = "%s:%s" % (server, port) if port else server
|
||||
|
|
|
@ -54,7 +54,7 @@ class UsersConnect(xbmcgui.WindowXMLDialog):
|
|||
|
||||
self.list_ = self.getControl(LIST)
|
||||
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'))
|
||||
self.list_.addItem(self._add_listitem(user['Name'], user['Id'], user_image))
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ class DownloadUtils(object):
|
|||
"SetAudioStreamIndex,SetSubtitleStreamIndex,"
|
||||
"SetRepeatMode,"
|
||||
"Mute,Unmute,SetVolume,"
|
||||
"Play,Playstate,PlayNext"
|
||||
"Play,Playstate,PlayNext,PlayMediaSource"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,12 +30,15 @@ import playbackutils as pbutils
|
|||
import playutils
|
||||
import api
|
||||
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__)
|
||||
|
||||
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))
|
||||
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(33053), "plugin://plugin.video.emby/?mode=settings")
|
||||
addDirectoryItem(lang(33054), "plugin://plugin.video.emby/?mode=adduser")
|
||||
|
@ -123,6 +132,140 @@ def doMainListing():
|
|||
|
||||
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():
|
||||
|
||||
# Login user to emby connect
|
||||
|
@ -657,7 +800,7 @@ def BrowseContent(viewname, browse_type="", folderid=""):
|
|||
'type': browse_type,
|
||||
'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)
|
||||
else: #playable item, set plugin path and mediastreams
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'episodes' if folderid else 'files')
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
@ -26,11 +27,15 @@ KODI = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
|||
class KodiMonitor(xbmc.Monitor):
|
||||
|
||||
retry = True
|
||||
special_monitor = None
|
||||
|
||||
def __init__(self):
|
||||
|
||||
xbmc.Monitor.__init__(self)
|
||||
|
||||
if settings('useDirectPaths') == "0":
|
||||
self.special_monitor = SpecialMonitor().start()
|
||||
|
||||
self.download = downloadutils.DownloadUtils().downloadUrl
|
||||
log.info("Kodi monitor started")
|
||||
|
||||
|
@ -60,6 +65,11 @@ class KodiMonitor(xbmc.Monitor):
|
|||
log.info("New context setting: %s", 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()
|
||||
def onNotification(self, sender, method, data):
|
||||
|
||||
|
@ -142,7 +152,7 @@ class KodiMonitor(xbmc.Monitor):
|
|||
else:
|
||||
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||
# Set properties for player.py
|
||||
playback.setProperties(playurl, listitem)
|
||||
playback.set_properties(playurl, listitem)
|
||||
|
||||
def _video_update(self, data):
|
||||
# Manually marking as watched/unwatched
|
||||
|
@ -199,3 +209,58 @@ class KodiMonitor(xbmc.Monitor):
|
|||
log.info("Could not retrieve 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
|
||||
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
|
||||
|
||||
if manualrun:
|
||||
|
|
|
@ -63,18 +63,6 @@ class KodiMovies(KodiItems):
|
|||
return kodi_id
|
||||
|
||||
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
|
||||
query = (
|
||||
'''
|
||||
|
@ -88,17 +76,6 @@ class KodiMovies(KodiItems):
|
|||
self.cursor.execute(query, (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((
|
||||
|
||||
"UPDATE movie",
|
||||
|
@ -177,48 +154,16 @@ class KodiMovies(KodiItems):
|
|||
|
||||
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:
|
||||
country_id = self._get_country(country)
|
||||
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
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))
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (country_id, kodi_id, "movie"))
|
||||
|
||||
def _add_country(self, country):
|
||||
|
||||
|
|
|
@ -189,26 +189,6 @@ class KodiMusic(KodiItems):
|
|||
return album_id
|
||||
|
||||
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((
|
||||
|
||||
"UPDATE album",
|
||||
|
@ -218,27 +198,6 @@ class KodiMusic(KodiItems):
|
|||
))
|
||||
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):
|
||||
|
||||
query = ' '.join((
|
||||
|
@ -356,8 +315,8 @@ class KodiMusic(KodiItems):
|
|||
|
||||
"UPDATE song",
|
||||
"SET idAlbum = ?, strArtistDisp = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
|
||||
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
|
||||
"rating = ?, comment = ?",
|
||||
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
|
||||
"rating = ?, comment = ?",
|
||||
"WHERE idSong = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
|
|
@ -174,9 +174,8 @@ class KodiTVShows(KodiItems):
|
|||
except TypeError:
|
||||
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 = ?"
|
||||
self.cursor.execute(query, (name, season_id))
|
||||
query = "UPDATE seasons SET name = ? WHERE idSeason = ?"
|
||||
self.cursor.execute(query, (name, season_id))
|
||||
|
||||
return season_id
|
||||
|
||||
|
@ -189,18 +188,6 @@ class KodiTVShows(KodiItems):
|
|||
return season_id
|
||||
|
||||
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 = (
|
||||
'''
|
||||
INSERT INTO episode(
|
||||
|
@ -213,16 +200,6 @@ class KodiTVShows(KodiItems):
|
|||
self.cursor.execute(query, (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((
|
||||
|
||||
"UPDATE episode",
|
||||
|
|
|
@ -250,28 +250,18 @@ class Movies(Items):
|
|||
if update_item:
|
||||
log.info("UPDATE movie itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# update new ratings Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
ratingid = self.kodi_db.get_ratingid(movieid)
|
||||
# update ratings
|
||||
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 new uniqueid Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
uniqueid = self.kodi_db.get_uniqueid(movieid)
|
||||
|
||||
self.kodi_db.update_uniqueid(movieid, "movie", imdb, "imdb", uniqueid)
|
||||
# update uniqueid
|
||||
uniqueid = self.kodi_db.get_uniqueid(movieid)
|
||||
self.kodi_db.update_uniqueid(movieid, "movie", imdb, "imdb", uniqueid)
|
||||
|
||||
# Update the movie entry
|
||||
if self.kodi_version >= 17:
|
||||
self.kodi_db.update_movie_17(title, plot, shortplot, tagline, votecount, uniqueid,
|
||||
writer, year, uniqueid, sorttitle, runtime, mpaa, genre,
|
||||
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)
|
||||
self.kodi_db.update_movie(title, plot, shortplot, tagline, votecount, uniqueid,
|
||||
writer, year, uniqueid, sorttitle, runtime, mpaa, genre,
|
||||
director, title, studio, trailer, country, year, movieid)
|
||||
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
@ -280,17 +270,13 @@ class Movies(Items):
|
|||
else:
|
||||
log.info("ADD movie itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# add new ratings Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
ratingid = self.kodi_db.create_entry_rating()
|
||||
# Add ratings
|
||||
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 new uniqueid Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
|
||||
self.kodi_db.add_uniqueid(uniqueid, movieid, "movie", imdb, "imdb")
|
||||
# Add uniqueid
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
self.kodi_db.add_uniqueid(uniqueid, movieid, "movie", imdb, "imdb")
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.add_path(path)
|
||||
|
@ -298,16 +284,10 @@ class Movies(Items):
|
|||
fileid = self.kodi_db.add_file(filename, pathid)
|
||||
|
||||
# Create the movie entry
|
||||
if self.kodi_version >= 17:
|
||||
self.kodi_db.add_movie_17(movieid, fileid, title, plot, shortplot, tagline,
|
||||
votecount, uniqueid, writer, year, uniqueid, sorttitle,
|
||||
runtime, mpaa, genre, director, title, studio, trailer,
|
||||
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)
|
||||
self.kodi_db.add_movie(movieid, fileid, title, plot, shortplot, tagline,
|
||||
votecount, uniqueid, writer, year, uniqueid, sorttitle,
|
||||
runtime, mpaa, genre, director, title, studio, trailer,
|
||||
country, year)
|
||||
|
||||
# Create the reference in emby table
|
||||
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)
|
||||
|
||||
# Process the album info
|
||||
if self.kodi_version in [17,18]:
|
||||
# Kodi Krypton/Leia
|
||||
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)
|
||||
self.kodi_db.update_album(artistname, year, genre, bio, thumb, rating, lastScraped,
|
||||
"album", albumid)
|
||||
|
||||
# Assign main artists to album
|
||||
for artist in item['AlbumArtists']:
|
||||
|
|
|
@ -9,7 +9,7 @@ import api
|
|||
import embydb_functions as embydb
|
||||
import _kodi_tvshows
|
||||
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
|
||||
API = api.API(item)
|
||||
|
||||
# Server api changed or something? RecursiveItemCount always returns 0
|
||||
"""if settings('syncEmptyShows') == "false" and not item.get('RecursiveItemCount'):
|
||||
if item.get('Name', None) is not None:
|
||||
log.info("Skipping empty show: %s", item['Name'])
|
||||
return"""
|
||||
# If the show is empty, try to remove it.
|
||||
if settings('syncEmptyShows') == "false" and not item.get('RecursiveItemCount'):
|
||||
log.info("Skipping empty show: %s", item.get('Name', item['Id']))
|
||||
return self.remove(item['Id'])
|
||||
|
||||
# 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
|
||||
update_item = True
|
||||
|
@ -308,12 +308,8 @@ class TVShows(Items):
|
|||
|
||||
if self.emby_db.get_view_grouped_series(viewid) and tvdb:
|
||||
# search kodi db for same provider id
|
||||
if self.kodi_version > 16:
|
||||
query = "SELECT idShow FROM tvshow_view WHERE uniqueid_value = ?"
|
||||
kodicursor.execute(query, (tvdb,))
|
||||
else:
|
||||
query = "SELECT idShow FROM tvshow WHERE C12 = ?"
|
||||
kodicursor.execute(query, (tvdb,))
|
||||
query = "SELECT idShow FROM tvshow_view WHERE uniqueid_value = ?"
|
||||
kodicursor.execute(query, (tvdb,))
|
||||
|
||||
try:
|
||||
temps_showid = kodicursor.fetchall()
|
||||
|
@ -342,51 +338,22 @@ class TVShows(Items):
|
|||
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 #####
|
||||
if update_item:
|
||||
log.info("UPDATE tvshow itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# update new ratings Kodi 17
|
||||
if self.kodi_version > 16:
|
||||
ratingid = self.kodi_db.get_ratingid("tvshow", showid)
|
||||
# update ratings
|
||||
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 new uniqueid Kodi 17
|
||||
if self.kodi_version > 16:
|
||||
uniqueid = self.kodi_db.get_uniqueid("tvshow", showid)
|
||||
|
||||
self.kodi_db.update_uniqueid(showid, "tvshow", tvdb, "unknown", uniqueid)
|
||||
# update uniqueid
|
||||
uniqueid = self.kodi_db.get_uniqueid("tvshow", showid)
|
||||
self.kodi_db.update_uniqueid(showid, "tvshow", tvdb, "unknown", uniqueid)
|
||||
|
||||
# Update the tvshow entry
|
||||
if self.kodi_version > 16:
|
||||
self.kodi_db.update_tvshow(title, plot, uniqueid, premieredate, genre, title,
|
||||
uniqueid, mpaa, studio, sorttitle, showid)
|
||||
else:
|
||||
self.kodi_db.update_tvshow(title, plot, rating, premieredate, genre, title,
|
||||
tvdb, mpaa, studio, sorttitle, showid)
|
||||
self.kodi_db.update_tvshow(title, plot, uniqueid, premieredate, genre, title,
|
||||
uniqueid, mpaa, studio, sorttitle, showid)
|
||||
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
|
@ -394,17 +361,13 @@ class TVShows(Items):
|
|||
else:
|
||||
log.info("ADD tvshow itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# add new ratings Kodi 17
|
||||
if self.kodi_version > 16:
|
||||
ratingid = self.kodi_db.create_entry_rating()
|
||||
# add ratings
|
||||
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 new uniqueid Kodi 17
|
||||
if self.kodi_version > 16:
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
|
||||
self.kodi_db.add_uniqueid(uniqueid, showid, "tvshow", tvdb, "unknown")
|
||||
# add uniqueid
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
self.kodi_db.add_uniqueid(uniqueid, showid, "tvshow", tvdb, "unknown")
|
||||
|
||||
# Add top path
|
||||
toppathid = self.kodi_db.add_path(toplevelpath)
|
||||
|
@ -414,12 +377,8 @@ class TVShows(Items):
|
|||
pathid = self.kodi_db.add_path(path)
|
||||
|
||||
# Create the tvshow entry
|
||||
if self.kodi_version > 16:
|
||||
self.kodi_db.add_tvshow(showid, title, plot, uniqueid, premieredate, genre,
|
||||
title, uniqueid, mpaa, studio, sorttitle)
|
||||
else:
|
||||
self.kodi_db.add_tvshow(showid, title, plot, rating, premieredate, genre,
|
||||
title, tvdb, mpaa, studio, sorttitle)
|
||||
self.kodi_db.add_tvshow(showid, title, plot, uniqueid, premieredate, genre,
|
||||
title, uniqueid, mpaa, studio, sorttitle)
|
||||
|
||||
# Create the reference in emby table
|
||||
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
|
||||
|
@ -629,39 +588,24 @@ class TVShows(Items):
|
|||
'dbid': episodeid,
|
||||
'mode': "play"
|
||||
}
|
||||
filename = plugin_path(path, params)
|
||||
filename = urllib_path(path, params)
|
||||
|
||||
##### UPDATE THE EPISODE #####
|
||||
if update_item:
|
||||
log.info("UPDATE episode itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# update new ratings Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
ratingid = self.kodi_db.get_ratingid("episode", episodeid)
|
||||
# update ratings
|
||||
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 new uniqueid Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
uniqueid = self.kodi_db.get_uniqueid("episode", episodeid)
|
||||
|
||||
self.kodi_db.update_uniqueid(episodeid, "episode", tvdb, "tvdb",uniqueid)
|
||||
# update uniqueid
|
||||
uniqueid = self.kodi_db.get_uniqueid("episode", episodeid)
|
||||
self.kodi_db.update_uniqueid(episodeid, "episode", tvdb, "tvdb", uniqueid)
|
||||
|
||||
# Update the episode entry
|
||||
if self.kodi_version >= 17:
|
||||
# Kodi Krypton
|
||||
self.kodi_db.update_episode_16(title, plot, uniqueid, writer, premieredate, runtime,
|
||||
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)
|
||||
self.kodi_db.update_episode(title, plot, uniqueid, writer, premieredate, runtime,
|
||||
director, season, episode, title, airsBeforeSeason,
|
||||
airsBeforeEpisode, seasonid, showid, episodeid)
|
||||
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
@ -672,17 +616,13 @@ class TVShows(Items):
|
|||
else:
|
||||
log.info("ADD episode itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# add new ratings Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
ratingid = self.kodi_db.create_entry_rating()
|
||||
# add ratings
|
||||
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 new uniqueid Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
|
||||
self.kodi_db.add_uniqueid(uniqueid, episodeid, "episode", tvdb, "tvdb")
|
||||
# add uniqueid
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
self.kodi_db.add_uniqueid(uniqueid, episodeid, "episode", tvdb, "tvdb")
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.add_path(path)
|
||||
|
@ -690,20 +630,9 @@ class TVShows(Items):
|
|||
fileid = self.kodi_db.add_file(filename, pathid)
|
||||
|
||||
# Create the episode entry
|
||||
if self.kodi_version >= 17:
|
||||
# Kodi Krypton
|
||||
self.kodi_db.add_episode_16(episodeid, fileid, title, plot, uniqueid, writer,
|
||||
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)
|
||||
self.kodi_db.add_episode(episodeid, fileid, title, plot, uniqueid, writer,
|
||||
premieredate, runtime, director, season, episode, title,
|
||||
showid, airsBeforeSeason, airsBeforeEpisode, seasonid)
|
||||
|
||||
# Create the reference in emby table
|
||||
emby_db.addReference(itemid, episodeid, "Episode", "episode", fileid, pathid,
|
||||
|
|
|
@ -21,6 +21,8 @@ import playutils as putils
|
|||
import playlist
|
||||
import read_embyserver as embyserver
|
||||
import shutil
|
||||
import embydb_functions as embydb
|
||||
from database import DatabaseConn
|
||||
from utils import window, settings, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
@ -30,329 +32,147 @@ log = logging.getLogger("EMBY."+__name__)
|
|||
#################################################################################################
|
||||
|
||||
|
||||
class PlaybackUtils():
|
||||
|
||||
|
||||
def __init__(self, item):
|
||||
class PlaybackUtils(object):
|
||||
|
||||
self.item = item
|
||||
self.API = api.API(self.item)
|
||||
|
||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||
|
||||
self.userid = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userid)
|
||||
def __init__(self, item=None, item_id=None):
|
||||
|
||||
self.artwork = artwork.Artwork()
|
||||
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()
|
||||
playutils = putils.PlayUtils(self.item)
|
||||
|
||||
log.info("Play called.")
|
||||
playurl = playutils.getPlayUrl()
|
||||
if not playurl:
|
||||
log.info("Play called: %s", self.item['Name'])
|
||||
|
||||
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)
|
||||
|
||||
if dbid is None:
|
||||
# Item is not in Kodi database
|
||||
listitem.setPath(playurl)
|
||||
self.setProperties(playurl, listitem)
|
||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||
seektime = 0 if resume == "true" else self.API.adjust_resume(self.API.get_userdata()['Resume'])
|
||||
|
||||
# TODO: Review once Krypton is RC, no need for workaround.
|
||||
if force_transcode:
|
||||
log.info("Clear the playlist.")
|
||||
self.playlist.clear()
|
||||
|
||||
############### ORGANIZE CURRENT PLAYLIST ################
|
||||
|
||||
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
|
||||
self.set_playlist(play_url, item_id, listitem, seektime, dbid)
|
||||
|
||||
propertiesPlayback = window('emby_playbackProps') == "true"
|
||||
introsPlaylist = False
|
||||
dummyPlaylist = False
|
||||
##### SETUP PLAYBACK
|
||||
|
||||
log.debug("Playlist start position: %s" % startPos)
|
||||
log.debug("Playlist plugin position: %s" % currentPosition)
|
||||
log.debug("Playlist size: %s" % sizePlaylist)
|
||||
''' To get everything to work together, play the first item in the stack with setResolvedUrl,
|
||||
add the rest to the regular playlist.
|
||||
'''
|
||||
|
||||
############### RESUME POINT ################
|
||||
|
||||
userdata = self.API.get_userdata()
|
||||
seektime = self.API.adjust_resume(userdata['Resume'])
|
||||
index = max(self.playlist.getposition(), 0) + 1 # Can return -1
|
||||
force_play = False
|
||||
|
||||
# We need to ensure we add the intro and additional parts only once.
|
||||
# Otherwise we get a loop.
|
||||
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']
|
||||
# Stack: [(url, listitem), (url, ...), ...]
|
||||
self.stack[0][1].setPath(self.stack[0][0])
|
||||
try:
|
||||
mediastreams = self.item['MediaSources'][0]['MediaStreams']
|
||||
except (TypeError, KeyError, IndexError):
|
||||
return
|
||||
#if not xbmc.getCondVisibility('Window.IsVisible(MyVideoNav.xml)'): # Causes infinite loop with play from here
|
||||
if xbmc.getCondVisibility('Window.IsVisible(10000).xml'):
|
||||
# widgets do not fill artwork correctly
|
||||
log.info("Detected widget.")
|
||||
raise IndexError
|
||||
|
||||
temp = xbmc.translatePath(
|
||||
"special://profile/addon_data/plugin.video.emby/temp/").decode('utf-8')
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1])
|
||||
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 stream in mediastreams:
|
||||
for stack in self.stack:
|
||||
self.playlist.add(url=stack[0], listitem=stack[1], index=index)
|
||||
index += 1
|
||||
|
||||
index = stream['Index']
|
||||
# Since Emby returns all possible tracks together, have to pull only external subtitles.
|
||||
# IsTextSubtitleStream if true, is available to download from emby.
|
||||
if (stream['Type'] == "Subtitle" and
|
||||
stream['IsExternal'] and stream['IsTextSubtitleStream']):
|
||||
if force_play:
|
||||
xbmc.Player().play(self.playlist)
|
||||
|
||||
# Direct stream
|
||||
url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.%s"
|
||||
% (self.server, itemid, itemid, index, stream['Codec']))
|
||||
def set_playlist(self, play_url, item_id, listitem, seektime=None, db_id=None):
|
||||
|
||||
if "Language" in stream:
|
||||
|
||||
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)
|
||||
##### CHECK FOR INTROS
|
||||
|
||||
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):
|
||||
xbmcvfs.mkdir(dst)
|
||||
self.set_properties(play_url, listitem)
|
||||
self.set_listitem(listitem, db_id)
|
||||
self.stack.append([play_url, listitem])
|
||||
|
||||
path = os.path.join(dst, filename)
|
||||
##### ADD ADDITIONAL PARTS
|
||||
|
||||
try:
|
||||
response = requests.get(src, stream=True)
|
||||
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
|
||||
if self.item.get('PartCount'):
|
||||
self._set_additional_parts(item_id)
|
||||
|
||||
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):
|
||||
# Set up item and item info
|
||||
allartwork = self.artwork.get_all_artwork(self.item, parent_info=True)
|
||||
# Set artwork for listitem
|
||||
arttypes = {
|
||||
if intros['Items']:
|
||||
enabled = True
|
||||
|
||||
'poster': "Primary",
|
||||
'tvshow.poster': "Primary",
|
||||
'clearart': "Art",
|
||||
'tvshow.clearart': "Art",
|
||||
'clearlogo': "Logo",
|
||||
'tvshow.clearlogo': "Logo",
|
||||
'discart': "Disc",
|
||||
'fanart_image': "Backdrop",
|
||||
'landscape': "Thumb"
|
||||
}
|
||||
for arttype in arttypes:
|
||||
if settings('askCinema') == "true":
|
||||
|
||||
art = arttypes[arttype]
|
||||
if art == "Backdrop":
|
||||
try: # Backdrop is a list, grab the first backdrop
|
||||
self.setArtProp(listItem, arttype, allartwork[art][0])
|
||||
except: pass
|
||||
else:
|
||||
self.setArtProp(listItem, arttype, allartwork[art])
|
||||
resp = xbmcgui.Dialog().yesno("Emby for Kodi", lang(33016))
|
||||
if not resp:
|
||||
# User selected to not play trailers
|
||||
enabled = False
|
||||
log.info("Skip trailers.")
|
||||
|
||||
def setArtProp(self, listItem, arttype, path):
|
||||
|
||||
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})
|
||||
if enabled:
|
||||
for intro in intros['Items']:
|
||||
|
||||
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()
|
||||
studios = self.API.get_studios()
|
||||
mediatype = self.item['Type']
|
||||
|
||||
metadata = {
|
||||
|
||||
'title': self.item.get('Name', "Missing name"),
|
||||
'year': self.item.get('ProductionYear'),
|
||||
'plot': self.API.get_overview(),
|
||||
|
@ -360,33 +180,151 @@ class PlaybackUtils():
|
|||
'writer': people.get('Writer'),
|
||||
'mpaa': self.API.get_mpaa(),
|
||||
'genre': " / ".join(self.item['Genres']),
|
||||
'studio': " / ".join(studios),
|
||||
'studio': " / ".join(self.API.get_studios()),
|
||||
'aired': self.API.get_premiere_date(),
|
||||
'rating': self.item.get('CommunityRating'),
|
||||
'votes': self.item.get('VoteCount')
|
||||
}
|
||||
|
||||
if "Episode" in self.item['Type']:
|
||||
if mediatype == "Episode":
|
||||
# Only for tv shows
|
||||
# For Kodi Krypton
|
||||
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')
|
||||
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
|
||||
elif mediatype == "Movie":
|
||||
metadata['mediatype'] = "movie"
|
||||
|
||||
elif mediatype == "MusicVideo":
|
||||
metadata['mediatype'] = "musicvideo"
|
||||
|
||||
elif mediatype == "Audio":
|
||||
metadata['mediatype'] = "song"
|
||||
|
||||
if dbid:
|
||||
metadata['dbid'] = dbid
|
||||
|
||||
listItem.setProperty('IsPlayable', 'true')
|
||||
listItem.setProperty('IsFolder', 'false')
|
||||
listItem.setLabel(metadata['title'])
|
||||
listItem.setInfo('video', infoLabels=metadata)
|
||||
listitem.setProperty('IsPlayable', 'true')
|
||||
listitem.setProperty('IsFolder', 'false')
|
||||
listitem.setLabel(metadata['title'])
|
||||
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 downloadutils
|
||||
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
|
||||
|
||||
#################################################################################################
|
||||
|
@ -43,6 +43,39 @@ class Player(xbmc.Player):
|
|||
log.debug("Starting playback monitor.")
|
||||
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()
|
||||
def onPlayBackStarted(self):
|
||||
# Will be called when xbmc starts playing a file
|
||||
|
@ -74,28 +107,31 @@ class Player(xbmc.Player):
|
|||
self.currentFile = currentFile
|
||||
|
||||
# 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
|
||||
while not itemId:
|
||||
while not item:
|
||||
|
||||
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
|
||||
log.info("Could not find itemId, cancelling playback report...")
|
||||
log.info("Could not find item, cancelling playback report...")
|
||||
break
|
||||
else: tryCount += 1
|
||||
|
||||
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.
|
||||
embyitem = "emby_%s" % currentFile
|
||||
runtime = window("%s.runtime" % embyitem)
|
||||
refresh_id = window("%s.refreshid" % embyitem)
|
||||
playMethod = window("%s.playmethod" % embyitem)
|
||||
itemType = window("%s.type" % embyitem)
|
||||
window('emby_skipWatched%s' % itemId, value="true")
|
||||
runtime = item.get('runtime')
|
||||
refresh_id = item.get('refreshid')
|
||||
play_method = item.get('playmethod')
|
||||
item_type = item.get('type')
|
||||
|
||||
#self.set_audio_subs(item.get('forcedaudio'), item.get('forcedsubs'))
|
||||
|
||||
window('emby_skipWatched%s' % item_id, value="true")
|
||||
customseek = window('emby_customPlaylist.seektime')
|
||||
if window('emby_customPlaylist') == "true" and customseek:
|
||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||
|
@ -110,18 +146,7 @@ class Player(xbmc.Player):
|
|||
return
|
||||
|
||||
# Get playback volume
|
||||
volume_query = {
|
||||
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Application.GetProperties",
|
||||
"params": {
|
||||
|
||||
"properties": ["volume", "muted"]
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
||||
result = json.loads(result)
|
||||
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
|
||||
result = result.get('result')
|
||||
|
||||
volume = result.get('volume')
|
||||
|
@ -133,33 +158,26 @@ class Player(xbmc.Player):
|
|||
|
||||
'QueueableMediaTypes': "Video",
|
||||
'CanSeek': True,
|
||||
'ItemId': itemId,
|
||||
'MediaSourceId': itemId,
|
||||
'PlayMethod': playMethod,
|
||||
'ItemId': item_id,
|
||||
'MediaSourceId': item_id,
|
||||
'PlayMethod': play_method,
|
||||
'VolumeLevel': volume,
|
||||
'PositionTicks': int(seekTime * 10000000),
|
||||
'IsMuted': muted
|
||||
}
|
||||
|
||||
# Get the current audio track and subtitles
|
||||
if playMethod == "Transcode":
|
||||
if play_method == "Transcode":
|
||||
# property set in PlayUtils.py
|
||||
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
||||
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
||||
else:
|
||||
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
||||
tracks_query = {
|
||||
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Player.GetProperties",
|
||||
"params": {
|
||||
|
||||
"playerid": 1,
|
||||
"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||
}
|
||||
params = {
|
||||
'playerid': 1,
|
||||
'properties': ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(tracks_query))
|
||||
result = JSONRPC('Player.GetProperties').execute(params)
|
||||
tracks_data = None
|
||||
try:
|
||||
tracks_data = json.loads(result)
|
||||
|
@ -190,7 +208,7 @@ class Player(xbmc.Player):
|
|||
|
||||
# Number of audiotracks to help get Emby Index
|
||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||
mapping = window("%s.indexMapping" % embyitem)
|
||||
mapping = window("emby_%s.indexMapping" % currentFile)
|
||||
|
||||
if mapping: # Set in playbackutils.py
|
||||
|
||||
|
@ -230,13 +248,13 @@ class Player(xbmc.Player):
|
|||
data = {
|
||||
|
||||
'runtime': runtime,
|
||||
'item_id': itemId,
|
||||
'item_id': item_id,
|
||||
'refresh_id': refresh_id,
|
||||
'currentfile': currentFile,
|
||||
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
||||
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
||||
'playmethod': playMethod,
|
||||
'Type': itemType,
|
||||
'playmethod': play_method,
|
||||
'Type': item_type,
|
||||
'currentPosition': int(seekTime)
|
||||
}
|
||||
|
||||
|
@ -244,8 +262,8 @@ class Player(xbmc.Player):
|
|||
log.info("ADDING_FILE: %s" % self.played_info)
|
||||
|
||||
ga = GoogleAnalytics()
|
||||
ga.sendEventData("PlayAction", itemType, playMethod)
|
||||
ga.sendScreenView(itemType)
|
||||
ga.sendEventData("PlayAction", item_type, play_method)
|
||||
ga.sendScreenView(item_type)
|
||||
|
||||
def reportPlayback(self):
|
||||
|
||||
|
@ -267,18 +285,7 @@ class Player(xbmc.Player):
|
|||
|
||||
|
||||
# Get playback volume
|
||||
volume_query = {
|
||||
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Application.GetProperties",
|
||||
"params": {
|
||||
|
||||
"properties": ["volume", "muted"]
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
||||
result = json.loads(result)
|
||||
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
|
||||
result = result.get('result')
|
||||
|
||||
volume = result.get('volume')
|
||||
|
@ -305,19 +312,11 @@ class Player(xbmc.Player):
|
|||
|
||||
else:
|
||||
# Get current audio and subtitles track
|
||||
tracks_query = {
|
||||
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Player.GetProperties",
|
||||
"params": {
|
||||
|
||||
"playerid": 1,
|
||||
"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(tracks_query))
|
||||
result = json.loads(result)
|
||||
params = {
|
||||
'playerid': 1,
|
||||
'properties': ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||
}
|
||||
result = JSONRPC('Player.GetProperties').execute(params)
|
||||
result = result.get('result')
|
||||
|
||||
try: # Audio tracks
|
||||
|
@ -413,10 +412,15 @@ class Player(xbmc.Player):
|
|||
def onPlayBackStopped(self):
|
||||
# Will be called when user stops xbmc playing a file
|
||||
log.debug("ONPLAYBACK_STOPPED")
|
||||
window('emby_customPlaylist', 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()
|
||||
|
||||
@log_error()
|
||||
|
@ -456,10 +460,16 @@ class Player(xbmc.Player):
|
|||
|
||||
if currentPosition and runtime:
|
||||
try:
|
||||
if window('emby.external'):
|
||||
window('emby.external', clear=True)
|
||||
raise ValueError
|
||||
|
||||
percentComplete = (currentPosition * 10000000) / int(runtime)
|
||||
except ZeroDivisionError:
|
||||
# Runtime is 0.
|
||||
percentComplete = 0
|
||||
except ValueError:
|
||||
percentComplete = 100
|
||||
|
||||
markPlayedAt = float(settings('markPlayed')) / 100
|
||||
log.info("Percent complete: %s Mark played at: %s"
|
||||
|
@ -486,6 +496,8 @@ class Player(xbmc.Player):
|
|||
else:
|
||||
log.info("User skipped deletion.")
|
||||
|
||||
window('emby.external_check', clear=True)
|
||||
|
||||
# Stop transcoding
|
||||
if playMethod == "Transcode":
|
||||
log.info("Transcoding for %s terminated." % itemid)
|
||||
|
|
|
@ -145,7 +145,7 @@ class Playlist(object):
|
|||
|
||||
@classmethod
|
||||
def verify_playlist(cls):
|
||||
log.debug(JSONRPC('Playlist.GetItems').execute({'playlistid': 1}))
|
||||
log.info(JSONRPC('Playlist.GetItems').execute({'playlistid': 1}))
|
||||
|
||||
@classmethod
|
||||
def remove_from_playlist(cls, position):
|
||||
|
@ -156,3 +156,171 @@ class Playlist(object):
|
|||
'position': position
|
||||
}
|
||||
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))
|
||||
'''
|
File diff suppressed because it is too large
Load diff
|
@ -151,7 +151,7 @@ class Read_EmbyServer():
|
|||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||
"MediaSources,VoteCount"
|
||||
"MediaSources,VoteCount,ItemCounts"
|
||||
)
|
||||
}
|
||||
queue.put({'url': url, 'params': params})
|
||||
|
@ -182,7 +182,7 @@ class Read_EmbyServer():
|
|||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"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)
|
||||
|
@ -198,7 +198,7 @@ class Read_EmbyServer():
|
|||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"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"
|
||||
|
@ -219,7 +219,7 @@ class Read_EmbyServer():
|
|||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"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"
|
||||
|
@ -285,7 +285,7 @@ class Read_EmbyServer():
|
|||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||
"MediaSources,VoteCount"
|
||||
"MediaSources,VoteCount,ItemCounts"
|
||||
)
|
||||
queue.put({'url': url, 'params': params})
|
||||
if not self._add_worker_thread(queue, items['Items']):
|
||||
|
@ -472,7 +472,7 @@ class Read_EmbyServer():
|
|||
|
||||
"Etag,Genres,SortName,Studios,Writer,ProductionYear,"
|
||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,Metascore,"
|
||||
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview"
|
||||
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview,ItemCounts"
|
||||
)
|
||||
}
|
||||
queue.put({'url': url, 'params': params})
|
||||
|
@ -614,7 +614,7 @@ class Read_EmbyServer():
|
|||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||
"MediaSources,VoteCount"
|
||||
"MediaSources,VoteCount,ItemCounts"
|
||||
)
|
||||
})
|
||||
return params
|
||||
|
@ -649,6 +649,39 @@ class Read_EmbyServer():
|
|||
return library['LibraryOptions']
|
||||
|
||||
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')
|
||||
return self.doUtils.downloadUrl(url)
|
||||
def get_intros(self, item_id):
|
||||
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 xbmc
|
||||
import xbmcgui
|
||||
|
||||
import userclient
|
||||
import clientinfo
|
||||
|
@ -52,10 +53,11 @@ class Service(object):
|
|||
self.addon_name = self.client_info.get_addon_name()
|
||||
log_level = settings('logLevel')
|
||||
|
||||
# General settings which are used by other entrypoints
|
||||
window('emby_logLevel', value=str(log_level))
|
||||
window('emby_kodiProfile', value=xbmc.translatePath('special://profile'))
|
||||
context_menu = "true" if settings('enableContext') == "true" else ""
|
||||
window('emby_context', value=context_menu)
|
||||
window('emby_context', value="true" if settings('enableContext') == "true" else "")
|
||||
window('emby_context_transcode', value="true" if settings('enableContextTranscode') == "true" else "")
|
||||
|
||||
# Initial logging
|
||||
log.warn("======== START %s ========", self.addon_name)
|
||||
|
@ -72,7 +74,8 @@ class Service(object):
|
|||
"emby_online", "emby_state.json", "emby_serverStatus", "emby_onWake",
|
||||
"emby_syncRunning", "emby_dbCheck", "emby_kodiScan",
|
||||
"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:
|
||||
window(prop, clear=True)
|
||||
|
@ -319,6 +322,9 @@ class Service(object):
|
|||
#ga = GoogleAnalytics()
|
||||
#ga.sendEventData("Application", "Shutdown")
|
||||
|
||||
if self.monitor.special_monitor:
|
||||
self.monitor.special_monitor.stop_monitor()
|
||||
|
||||
if self.userclient_running:
|
||||
self.userclient_thread.stop_client()
|
||||
|
||||
|
|
|
@ -313,7 +313,7 @@ class UserClient(threading.Thread):
|
|||
log.info("Username found: %s", username)
|
||||
self._auth = True
|
||||
|
||||
if monitor.waitForAbort(1):
|
||||
if monitor.waitForAbort(2):
|
||||
# Abort was requested while waiting. We should exit
|
||||
break
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ def dialog(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))
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import xbmcvfs
|
|||
|
||||
import read_embyserver as embyserver
|
||||
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",
|
||||
'type': mediatype
|
||||
}
|
||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
||||
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||
|
||||
elif (mediatype == "homevideos" or mediatype == "photos"):
|
||||
params = {
|
||||
|
@ -651,7 +651,7 @@ class VideoNodes(object):
|
|||
'type': mediatype,
|
||||
'folderid': nodetype
|
||||
}
|
||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
||||
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||
|
||||
elif nodetype == "nextepisodes":
|
||||
params = {
|
||||
|
@ -660,7 +660,7 @@ class VideoNodes(object):
|
|||
'mode': "nextup",
|
||||
'limit': 25
|
||||
}
|
||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
||||
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||
|
||||
elif KODI == 14 and nodetype == "recentepisodes":
|
||||
params = {
|
||||
|
@ -669,7 +669,7 @@ class VideoNodes(object):
|
|||
'mode': "recentepisodes",
|
||||
'limit': 25
|
||||
}
|
||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
||||
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||
|
||||
elif KODI == 14 and nodetype == "inprogressepisodes":
|
||||
params = {
|
||||
|
@ -678,7 +678,7 @@ class VideoNodes(object):
|
|||
'mode': "inprogressepisodes",
|
||||
'limit': 25
|
||||
}
|
||||
path = plugin_path("plugin://plugin.video.emby/", params)
|
||||
path = urllib_path("plugin://plugin.video.emby/", params)
|
||||
else:
|
||||
path = "library://video/emby/%s/%s.xml" % (viewid, nodetype)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import downloadutils
|
|||
import librarysync
|
||||
import playlist
|
||||
import userclient
|
||||
import playbackutils
|
||||
from utils import window, settings, dialog, language as lang, JSONRPC
|
||||
from ga_client import log_error
|
||||
|
||||
|
@ -112,19 +113,31 @@ class WebSocketClient(threading.Thread):
|
|||
@classmethod
|
||||
def _play(cls, data):
|
||||
|
||||
''' {"Id":"e2c106a953bfc0d9c191a49cda6561de",
|
||||
"ItemIds":["52f0c57e2133e1c11d36c59edcd835fc"],
|
||||
"PlayCommand":"PlayNow","ControllingUserId":"d40000000000000000000000000000000",
|
||||
"SubtitleStreamIndex":3,"AudioStreamIndex":1,
|
||||
"MediaSourceId":"ba83c549ac5c0e4180ae33ebdf813c51"}}
|
||||
'''
|
||||
|
||||
item_ids = data['ItemIds']
|
||||
kwargs = {
|
||||
|
||||
'SubtitleStreamIndex': data.get('SubtitleStreamIndex'),
|
||||
'AudioStreamIndex': data.get('AudioStreamIndex'),
|
||||
'MediaSourceId': data.get('MediaSourceId')
|
||||
}
|
||||
command = data['PlayCommand']
|
||||
|
||||
playlist_ = playlist.Playlist()
|
||||
|
||||
if command == 'PlayNow':
|
||||
startat = data.get('StartPositionTicks', 0)
|
||||
playlist_.play_all(item_ids, startat)
|
||||
dialog(type_="notification",
|
||||
heading="{emby}",
|
||||
message="%s %s" % (len(item_ids), lang(33004)),
|
||||
icon="{emby}",
|
||||
sound=False)
|
||||
if playbackutils.PlaybackUtils(None, item_ids[0]).play_all(item_ids, data.get('StartPositionTicks', 0), **kwargs):
|
||||
dialog(type_="notification",
|
||||
heading="{emby}",
|
||||
message="%s %s" % (len(item_ids), lang(33004)),
|
||||
icon="{emby}",
|
||||
sound=False)
|
||||
|
||||
elif command == 'PlayNext':
|
||||
new_playlist = playlist_.modify_playlist(item_ids)
|
||||
|
@ -214,7 +227,7 @@ class WebSocketClient(threading.Thread):
|
|||
elif command == 'SetSubtitleStreamIndex':
|
||||
emby_index = int(arguments['Index'])
|
||||
current_file = player.getPlayingFile()
|
||||
mapping = window('emby_%s.indexMapping' % current_file)
|
||||
mapping = window('emby_%s.indexMapping.json' % current_file)
|
||||
|
||||
if emby_index == -1:
|
||||
player.showSubtitles(False)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue