This commit is contained in:
angelblue05 2016-09-08 22:13:25 -05:00 committed by GitHub
parent e941674e74
commit b945459dfb
8 changed files with 447 additions and 419 deletions

View file

@ -5,171 +5,204 @@
import logging import logging
import os import os
import sys import sys
import urlparse
import xbmc import xbmc
import xbmcaddon import xbmcaddon
import xbmcgui
################################################################################################# #################################################################################################
_addon = xbmcaddon.Addon(id='plugin.video.emby') _ADDON = xbmcaddon.Addon(id='plugin.video.emby')
_addon_path = _addon.getAddonInfo('path').decode('utf-8') _CWD = _ADDON.getAddonInfo('path').decode('utf-8')
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8') _BASE_LIB = xbmc.translatePath(os.path.join(_CWD, 'resources', 'lib')).decode('utf-8')
sys.path.append(_base_resource) sys.path.append(_BASE_LIB)
################################################################################################# #################################################################################################
import api import api
import artwork import loghandler
import downloadutils
import librarysync
import read_embyserver as embyserver import read_embyserver as embyserver
import embydb_functions as embydb import embydb_functions as embydb
import kodidb_functions as kodidb
import musicutils as musicutils import musicutils as musicutils
from utils import settings, language as lang, kodiSQL from utils import settings, dialog, language as lang, kodiSQL
################################################################################################# #################################################################################################
import loghandler
loghandler.config() loghandler.config()
log = logging.getLogger("EMBY.contextmenu") log = logging.getLogger("EMBY.contextmenu")
################################################################################################# #################################################################################################
OPTIONS = {
'Refresh': lang(30410),
'Delete': lang(30409),
'Addon': lang(30408),
'AddFav': lang(30405),
'RemoveFav': lang(30406),
'RateSong': lang(30407)
}
class ContextMenu(object):
_selected_option = None
def __init__(self):
self.emby = embyserver.Read_EmbyServer()
self.kodi_id = xbmc.getInfoLabel('ListItem.DBID').decode('utf-8')
self.item_type = self._get_item_type()
self.item_id = self._get_item_id(self.kodi_id, self.item_type)
log.info("Found item_id: %s item_type: %s", self.item_id, self.item_type)
if self.item_id:
self.item = self.emby.getItem(self.item_id)
self.api = api.API(self.item)
if self._select_menu():
self._action_menu()
xbmc.sleep(500)
xbmc.executebuiltin('Container.Refresh')
@classmethod
def _get_item_type(cls):
item_type = xbmc.getInfoLabel('ListItem.DBTYPE').decode('utf-8')
if not item_type:
if xbmc.getCondVisibility('Container.Content(albums)'):
item_type = "album"
elif xbmc.getCondVisibility('Container.Content(artists)'):
item_type = "artist"
elif xbmc.getCondVisibility('Container.Content(songs)'):
item_type = "song"
elif xbmc.getCondVisibility('Container.Content(pictures)'):
item_type = "picture"
else:
log.info("item_type is unknown")
return item_type
@classmethod
def _get_item_id(cls, kodi_id, item_type):
item_id = xbmc.getInfoLabel('ListItem.Property(embyid)')
if not item_id and kodi_id and item_type:
conn = kodiSQL('emby')
cursor = conn.cursor()
emby_db = embydb.Embydb_Functions(cursor)
item = emby_db.getItem_byKodiId(kodi_id, item_type)
cursor.close()
try:
item_id = item[0]
except TypeError:
pass
return item_id
def _select_menu(self):
# Display select dialog
userdata = self.api.getUserData()
options = []
if userdata['Favorite']:
# Remove from emby favourites
options.append(OPTIONS['RemoveFav'])
else:
# Add to emby favourites
options.append(OPTIONS['AddFav'])
if self.item_type == "song":
# Set custom song rating
options.append(OPTIONS['RateSong'])
# Refresh item
options.append(OPTIONS['Refresh'])
# Delete item
options.append(OPTIONS['Delete'])
# Addon settings
options.append(OPTIONS['Addon'])
resp = dialog(type_="select", heading=lang(30401), list=options)
if resp > -1:
self._selected_option = options[resp]
return self._selected_option
def _action_menu(self):
selected = self._selected_option
if selected == OPTIONS['Refresh']:
self.emby.refreshItem(self.item_id)
elif selected == OPTIONS['AddFav']:
self.emby.updateUserRating(self.item_id, favourite=True)
elif selected == OPTIONS['RemoveFav']:
self.emby.updateUserRating(self.item_id, favourite=False)
elif selected == OPTIONS['RateSong']:
self._rate_song()
elif selected == OPTIONS['Addon']:
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
elif selected == OPTIONS['Delete']:
self._delete_item()
def _rate_song(self):
conn = kodiSQL('music')
cursor = conn.cursor()
query = "SELECT rating FROM song WHERE idSong = ?"
cursor.execute(query, (self.kodi_id,))
try:
value = cursor.fetchone()[0]
current_value = int(round(float(value), 0))
except TypeError:
pass
else:
new_value = dialog("numeric", 0, lang(30411), str(current_value))
if new_value > -1:
new_value = int(new_value)
if new_value > 5:
new_value = 5
if settings('enableUpdateSongRating') == "true":
musicutils.updateRatingToFile(new_value, self.api.getFilePath())
query = "UPDATE song SET rating = ? WHERE idSong = ?"
cursor.execute(query, (new_value, self.kodi_id,))
conn.commit()
finally:
cursor.close()
def _delete_item(self):
delete = True
if settings('skipContextMenu') != "true":
if not dialog(type_="yesno", heading="{emby}", line1=lang(33041)):
log.info("User skipped deletion for: %s", self.item_id)
delete = False
if delete:
log.info("Deleting request: %s", self.item_id)
self.emby.deleteItem(self.item_id)
# Kodi contextmenu item to configure the emby settings # Kodi contextmenu item to configure the emby settings
if __name__ == '__main__': if __name__ == '__main__':
kodiId = xbmc.getInfoLabel('ListItem.DBID').decode('utf-8') log.info("plugin.video.emby contextmenu started")
itemType = xbmc.getInfoLabel('ListItem.DBTYPE').decode('utf-8') ContextMenu()
itemId = "" log.info("plugin.video.emby contextmenu stopped")
if not itemType:
if xbmc.getCondVisibility("Container.Content(albums)"):
itemType = "album"
elif xbmc.getCondVisibility("Container.Content(artists)"):
itemType = "artist"
elif xbmc.getCondVisibility("Container.Content(songs)"):
itemType = "song"
elif xbmc.getCondVisibility("Container.Content(pictures)"):
itemType = "picture"
else:
log.info("ItemType is unknown.")
if (not kodiId or kodiId == "-1") and xbmc.getInfoLabel("ListItem.Property(embyid)"):
itemId = xbmc.getInfoLabel("ListItem.Property(embyid)")
elif kodiId and itemType:
embyconn = kodiSQL('emby')
embycursor = embyconn.cursor()
emby_db = embydb.Embydb_Functions(embycursor)
item = emby_db.getItem_byKodiId(kodiId, itemType)
embycursor.close()
try:
itemId = item[0]
except TypeError:
pass
log.info("Found ItemId: %s ItemType: %s" % (itemId, itemType))
if itemId:
dialog = xbmcgui.Dialog()
emby = embyserver.Read_EmbyServer()
item = emby.getItem(itemId)
API = api.API(item)
userdata = API.getUserData()
likes = userdata['Likes']
favourite = userdata['Favorite']
options = []
if favourite:
# Remove from emby favourites
options.append(lang(30406))
else:
# Add to emby favourites
options.append(lang(30405))
if itemType == "song":
# Set custom song rating
options.append(lang(30407))
# Refresh item
options.append(lang(30410))
# Delete item
options.append(lang(30409))
# Addon settings
options.append(lang(30408))
# Display select dialog and process results
resp = xbmcgui.Dialog().select(lang(30401), options)
if resp > -1:
selected = options[resp]
if selected == lang(30410):
# Refresh item
emby.refreshItem(itemId)
elif selected == lang(30405):
# Add favourite
emby.updateUserRating(itemId, favourite=True)
elif selected == lang(30406):
# Delete favourite
emby.updateUserRating(itemId, favourite=False)
elif selected == lang(30407):
# Update song rating
kodiconn = kodiSQL('music')
kodicursor = kodiconn.cursor()
query = "SELECT rating FROM song WHERE idSong = ?"
kodicursor.execute(query, (kodiId,))
try:
value = kodicursor.fetchone()[0]
current_value = int(round(float(value),0))
except TypeError:
pass
else:
new_value = dialog.numeric(0, lang(30411), str(current_value))
if new_value > -1:
new_value = int(new_value)
if new_value > 5:
new_value = 5
if settings('enableUpdateSongRating') == "true":
musicutils.updateRatingToFile(new_value, API.getFilePath())
query = "UPDATE song SET rating = ? WHERE idSong = ?"
kodicursor.execute(query, (new_value, kodiId,))
kodiconn.commit()
'''if settings('enableExportSongRating') == "true":
like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(new_value)
emby.updateUserRating(itemId, like, favourite, deletelike)'''
finally:
kodicursor.close()
elif selected == lang(30408):
# Open addon settings
xbmc.executebuiltin("Addon.OpenSettings(plugin.video.emby)")
elif selected == lang(30409):
# delete item from the server
delete = True
if settings('skipContextMenu') != "true":
resp = dialog.yesno(
heading=lang(29999),
line1=lang(33041))
if not resp:
log.info("User skipped deletion for: %s." % itemId)
delete = False
if delete:
log.info("Deleting request: %s" % itemId)
emby.deleteItem(itemId)
xbmc.sleep(500)
xbmc.executebuiltin('Container.Refresh')

View file

@ -9,52 +9,74 @@ import urlparse
import xbmc import xbmc
import xbmcaddon import xbmcaddon
import xbmcgui
################################################################################################# #################################################################################################
_addon = xbmcaddon.Addon(id='plugin.video.emby') _ADDON = xbmcaddon.Addon(id='plugin.video.emby')
_addon_path = _addon.getAddonInfo('path').decode('utf-8') _CWD = _ADDON.getAddonInfo('path').decode('utf-8')
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8') _BASE_LIB = xbmc.translatePath(os.path.join(_CWD, 'resources', 'lib')).decode('utf-8')
sys.path.append(_base_resource) sys.path.append(_BASE_LIB)
################################################################################################# #################################################################################################
import entrypoint import entrypoint
import utils import loghandler
from utils import window, language as lang from utils import window, dialog, language as lang
################################################################################################# #################################################################################################
import loghandler
loghandler.config() loghandler.config()
log = logging.getLogger("EMBY.default") log = logging.getLogger("EMBY.default")
################################################################################################# #################################################################################################
class Main(): class Main(object):
# MAIN ENTRY POINT # MAIN ENTRY POINT
#@utils.profiling() #@utils.profiling()
def __init__(self): def __init__(self):
# Parse parameters # Parse parameters
base_url = sys.argv[0] base_url = sys.argv[0]
params = urlparse.parse_qs(sys.argv[2][1:]) path = sys.argv[2]
log.warn("Parameter string: %s" % sys.argv[2]) params = urlparse.parse_qs(path[1:])
log.warn("Parameter string: %s", path)
try: try:
mode = params['mode'][0] mode = params['mode'][0]
itemid = params.get('id') except (IndexError, KeyError):
if itemid:
itemid = itemid[0]
except:
params = {}
mode = "" mode = ""
if "/extrafanart" in base_url:
emby_path = path[1:]
emby_id = params.get('id', [""])[0]
entrypoint.getExtraFanArt(emby_id, emby_path)
elif "/Extras" in base_url or "/VideoFiles" in base_url:
emby_path = path[1:]
emby_id = params.get('id', [""])[0]
entrypoint.getVideoFiles(emby_id, emby_path)
elif not self._modes(mode, params):
# Other functions
if mode == 'settings':
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
elif mode in ('manualsync', 'fastsync', 'repair'):
self._library_sync(mode)
elif mode == 'texturecache':
import artwork
artwork.Artwork().fullTextureCacheSync()
else:
entrypoint.doMainListing()
@classmethod
def _modes(cls, mode, params):
import utils
modes = { modes = {
'reset': utils.reset, 'reset': utils.reset,
@ -75,76 +97,63 @@ class Main():
'delete': entrypoint.deleteItem, 'delete': entrypoint.deleteItem,
'connect': entrypoint.emby_connect 'connect': entrypoint.emby_connect
} }
if mode in modes:
if "/extrafanart" in sys.argv[0]:
embypath = sys.argv[2][1:]
embyid = params.get('id',[""])[0]
entrypoint.getExtraFanArt(embyid,embypath)
return
if "/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0]:
embypath = sys.argv[2][1:]
embyid = params.get('id',[""])[0]
entrypoint.getVideoFiles(embyid, embypath)
return
if modes.get(mode):
# Simple functions # Simple functions
if mode == "play": action = modes[mode]
dbid = params.get('dbid') item_id = params.get('id')
modes[mode](itemid, dbid) if item_id:
item_id = item_id[0]
elif mode in ("nextup", "inprogressepisodes", "recentepisodes"): if mode == 'play':
database_id = params.get('dbid')
action(item_id, database_id)
elif mode in ('nextup', 'inprogressepisodes', 'recentepisodes'):
limit = int(params['limit'][0]) limit = int(params['limit'][0])
modes[mode](itemid, limit) action(item_id, limit)
elif mode in ("channels","getsubfolders"):
modes[mode](itemid)
elif mode == "browsecontent":
modes[mode](itemid, params.get('type',[""])[0], params.get('folderid',[""])[0])
elif mode == "channelsfolder": elif mode in ('channels', 'getsubfolders'):
action(item_id)
elif mode == 'browsecontent':
action(item_id, params.get('type', [""])[0], params.get('folderid', [""])[0])
elif mode == 'channelsfolder':
folderid = params['folderid'][0] folderid = params['folderid'][0]
modes[mode](itemid, folderid) action(item_id, folderid)
else: else:
modes[mode]() action()
else:
# Other functions return True
if mode == "settings":
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)') return False
elif mode in ("manualsync", "fastsync", "repair"): @classmethod
def _library_sync(cls, mode):
if window('emby_online') != "true":
# Server is not online, do not run the sync if window('emby_online') != "true":
xbmcgui.Dialog().ok(heading=lang(29999), # Server is not online, do not run the sync
line1=lang(33034)) dialog(type_="ok",
log.warn("Not connected to the emby server.") heading="{emby}",
return line1=lang(33034))
log.warn("Not connected to the emby server.")
if window('emby_dbScan') != "true":
import librarysync elif window('emby_dbScan') != "true":
lib = librarysync.LibrarySync() import librarysync
if mode == "manualsync": library_sync = librarysync.LibrarySync()
librarysync.ManualSync().sync()
elif mode == "fastsync": if mode == 'manualsync':
lib.startSync() librarysync.ManualSync().sync()
else: elif mode == 'fastsync':
lib.fullSync(repair=True) library_sync.startSync()
else: else:
log.warn("Database scan is already running.") library_sync.fullSync(repair=True)
else:
elif mode == "texturecache": log.warn("Database scan is already running.")
import artwork
artwork.Artwork().fullTextureCacheSync()
else:
entrypoint.doMainListing()
if __name__ == "__main__": if __name__ == "__main__":
log.info('plugin.video.emby started')
log.info("plugin.video.emby started")
Main() Main()
log.info('plugin.video.emby stopped') log.info("plugin.video.emby stopped")

View file

@ -98,9 +98,9 @@ class PlaybackUtils():
dummyPlaylist = True dummyPlaylist = True
playlist.add(playurl, listitem, index=startPos) playlist.add(playurl, listitem, index=startPos)
# Remove the original item from playlist # Remove the original item from playlist
self.pl.removefromPlaylist(startPos+1) self.pl.remove_from_playlist(startPos+1)
# Readd the original item to playlist - via jsonrpc so we have full metadata # Readd the original item to playlist - via jsonrpc so we have full metadata
self.pl.insertintoPlaylist(currentPosition+1, dbid, self.item['Type'].lower()) self.pl.insert_to_playlist(currentPosition+1, dbid, self.item['Type'].lower())
currentPosition += 1 currentPosition += 1
############### -- CHECK FOR INTROS ################ ############### -- CHECK FOR INTROS ################
@ -131,7 +131,7 @@ class PlaybackUtils():
pbutils = PlaybackUtils(intro) pbutils = PlaybackUtils(intro)
pbutils.setProperties(introPlayurl, introListItem) pbutils.setProperties(introPlayurl, introListItem)
self.pl.insertintoPlaylist(currentPosition, url=introPlayurl) self.pl.insert_to_playlist(currentPosition, url=introPlayurl)
introsPlaylist = True introsPlaylist = True
currentPosition += 1 currentPosition += 1
@ -142,7 +142,7 @@ class PlaybackUtils():
# Extend our current playlist with the actual item to play # Extend our current playlist with the actual item to play
# only if there's no playlist first # only if there's no playlist first
log.info("Adding main item to playlist.") log.info("Adding main item to playlist.")
self.pl.addtoPlaylist(dbid, self.item['Type'].lower()) self.pl.add_to_playlist(dbid, self.item['Type'].lower())
# Ensure that additional parts are played after the main item # Ensure that additional parts are played after the main item
currentPosition += 1 currentPosition += 1
@ -166,7 +166,7 @@ class PlaybackUtils():
pbutils.setArtwork(additionalListItem) pbutils.setArtwork(additionalListItem)
playlist.add(additionalPlayurl, additionalListItem, index=currentPosition) playlist.add(additionalPlayurl, additionalListItem, index=currentPosition)
self.pl.verifyPlaylist() self.pl.verify_playlist()
currentPosition += 1 currentPosition += 1
if dummyPlaylist: if dummyPlaylist:
@ -181,7 +181,7 @@ class PlaybackUtils():
log.debug("Resetting properties playback flag.") log.debug("Resetting properties playback flag.")
window('emby_playbackProps', clear=True) window('emby_playbackProps', clear=True)
#self.pl.verifyPlaylist() #self.pl.verify_playlist()
########## SETUP MAIN ITEM ########## ########## SETUP MAIN ITEM ##########
# For transcoding only, ask for audio/subs pref # For transcoding only, ask for audio/subs pref

View file

@ -38,7 +38,7 @@ class Player(xbmc.Player):
self.clientInfo = clientinfo.ClientInfo() self.clientInfo = clientinfo.ClientInfo()
self.doUtils = downloadutils.DownloadUtils().downloadUrl self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.ws = wsc.WebSocket_Client() self.ws = wsc.WebSocketClient()
self.xbmcplayer = xbmc.Player() self.xbmcplayer = xbmc.Player()
log.debug("Starting playback monitor.") log.debug("Starting playback monitor.")

View file

@ -2,18 +2,16 @@
################################################################################################# #################################################################################################
import json
import logging import logging
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcplugin
import playutils import playutils
import playbackutils import playbackutils
import embydb_functions as embydb import embydb_functions as embydb
import read_embyserver as embyserver import read_embyserver as embyserver
from utils import window, settings, language as lang, kodiSQL from utils import window, kodiSQL, JSONRPC
################################################################################################# #################################################################################################
@ -22,169 +20,141 @@ log = logging.getLogger("EMBY."+__name__)
################################################################################################# #################################################################################################
class Playlist(): class Playlist(object):
def __init__(self): def __init__(self):
self.userid = window('emby_currUser')
self.server = window('emby_server%s' % self.userid)
self.emby = embyserver.Read_EmbyServer() self.emby = embyserver.Read_EmbyServer()
def playAll(self, itemids, startat): def play_all(self, item_ids, start_at):
embyconn = kodiSQL('emby') conn = kodiSQL('emby')
embycursor = embyconn.cursor() cursor = conn.cursor()
emby_db = embydb.Embydb_Functions(embycursor) emby_db = embydb.Embydb_Functions(cursor)
player = xbmc.Player() player = xbmc.Player()
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear() playlist.clear()
log.info("---*** PLAY ALL ***---") log.info("---*** PLAY ALL ***---")
log.info("Items: %s and start at: %s" % (itemids, startat)) log.info("Items: %s and start at: %s", item_ids, start_at)
started = False started = False
window('emby_customplaylist', value="true") window('emby_customplaylist', value="true")
if startat != 0: if start_at:
# Seek to the starting position # Seek to the starting position
window('emby_customplaylist.seektime', str(startat)) window('emby_customplaylist.seektime', str(start_at))
for itemid in itemids: for item_id in item_ids:
embydb_item = emby_db.getItem_byId(itemid)
log.info("Adding %s to playlist", item_id)
item = emby_db.getItem_byId(item_id)
try: try:
dbid = embydb_item[0] db_id = item[0]
mediatype = embydb_item[4] media_type = item[4]
except TypeError: except TypeError:
# Item is not found in our database, add item manually # Item is not found in our database, add item manually
log.info("Item was not found in the database, manually adding item.") log.info("Item was not found in the database, manually adding item")
item = self.emby.getItem(itemid) item = self.emby.getItem(item_id)
self.addtoPlaylist_xbmc(playlist, item) self.add_to_xbmc_playlist(playlist, item)
else:
# Add to playlist
self.addtoPlaylist(dbid, mediatype)
log.info("Adding %s to playlist." % itemid) else: # Add to playlist
self.add_to_playlist(db_id, media_type)
if not started: if not started:
started = True started = True
player.play(playlist) player.play(playlist)
self.verifyPlaylist() self.verify_playlist()
embycursor.close() cursor.close()
def modifyPlaylist(self, itemids): def modify_playlist(self, item_ids):
embyconn = kodiSQL('emby') conn = kodiSQL('emby')
embycursor = embyconn.cursor() cursor = conn.cursor()
emby_db = embydb.Embydb_Functions(embycursor) emby_db = embydb.Embydb_Functions(cursor)
log.info("---*** ADD TO PLAYLIST ***---") log.info("---*** ADD TO PLAYLIST ***---")
log.info("Items: %s" % itemids) log.info("Items: %s", item_ids)
player = xbmc.Player()
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
for itemid in itemids: for item_id in item_ids:
embydb_item = emby_db.getItem_byId(itemid)
log.info("Adding %s to playlist", item_id)
item = emby_db.getItem_byId(item_id)
try: try:
dbid = embydb_item[0] db_id = item[0]
mediatype = embydb_item[4] media_type = item[4]
except TypeError: except TypeError:
# Item is not found in our database, add item manually # Item is not found in our database, add item manually
item = self.emby.getItem(itemid) item = self.emby.getItem(item_id)
self.addtoPlaylist_xbmc(playlist, item) self.add_to_xbmc_playlist(playlist, item)
else:
# Add to playlist
self.addtoPlaylist(dbid, mediatype)
log.info("Adding %s to playlist." % itemid) else: # Add to playlist
self.add_to_playlist(db_id, media_type)
self.verifyPlaylist() self.verify_playlist()
embycursor.close() cursor.close()
return playlist return playlist
def addtoPlaylist(self, dbid=None, mediatype=None, url=None):
pl = { @classmethod
def add_to_xbmc_playlist(cls, playlist, item):
'jsonrpc': "2.0",
'id': 1,
'method': "Playlist.Add",
'params': {
'playlistid': 1
}
}
if dbid is not None:
pl['params']['item'] = {'%sid' % mediatype: int(dbid)}
else:
pl['params']['item'] = {'file': url}
log.debug(xbmc.executeJSONRPC(json.dumps(pl)))
def addtoPlaylist_xbmc(self, playlist, item):
playurl = playutils.PlayUtils(item).getPlayUrl() playurl = playutils.PlayUtils(item).getPlayUrl()
if not playurl: if not playurl:
# Playurl failed log.info("Failed to retrieve playurl")
log.info("Failed to retrieve playurl.")
return return
log.info("Playurl: %s" % playurl) log.info("Playurl: %s", playurl)
listitem = xbmcgui.ListItem() listitem = xbmcgui.ListItem()
playbackutils.PlaybackUtils(item).setProperties(playurl, listitem) playbackutils.PlaybackUtils(item).setProperties(playurl, listitem)
playlist.add(playurl, listitem) playlist.add(playurl, listitem)
def insertintoPlaylist(self, position, dbid=None, mediatype=None, url=None): @classmethod
def add_to_playlist(cls, db_id=None, media_type=None, url=None):
pl = { params = {
'jsonrpc': "2.0", 'playlistid': 1
'id': 1,
'method': "Playlist.Insert",
'params': {
'playlistid': 1,
'position': position
}
} }
if dbid is not None: if db_id is not None:
pl['params']['item'] = {'%sid' % mediatype: int(dbid)} params['item'] = {'%sid' % media_type: int(db_id)}
else: else:
pl['params']['item'] = {'file': url} params['item'] = {'file': url}
log.debug(xbmc.executeJSONRPC(json.dumps(pl))) log.debug(JSONRPC('Playlist.Add').execute(params))
def verifyPlaylist(self): @classmethod
def insert_to_playlist(cls, position, db_id=None, media_type=None, url=None):
pl = { params = {
'jsonrpc': "2.0", 'playlistid': 1,
'id': 1, 'position': position
'method': "Playlist.GetItems",
'params': {
'playlistid': 1
}
} }
log.debug(xbmc.executeJSONRPC(json.dumps(pl))) if db_id is not None:
params['item'] = {'%sid' % media_type: int(db_id)}
else:
params['item'] = {'file': url}
def removefromPlaylist(self, position): log.debug(JSONRPC('Playlist.Insert').execute(params))
pl = { @classmethod
def verify_playlist(cls):
log.debug(JSONRPC('Playlist.GetItems').execute({'playlistid': 1}))
'jsonrpc': "2.0", @classmethod
'id': 1, def remove_from_playlist(cls, position):
'method': "Playlist.Remove",
'params': {
'playlistid': 1, params = {
'position': position
} 'playlistid': 1,
'position': position
} }
log.debug(xbmc.executeJSONRPC(json.dumps(pl))) log.debug(JSONRPC('Playlist.Remove').execute(params))

View file

@ -57,12 +57,16 @@ def dialog(type_, **kwargs):
if "icon" in kwargs: if "icon" in kwargs:
kwargs['icon'] = kwargs['icon'].replace("{emby}", kwargs['icon'] = kwargs['icon'].replace("{emby}",
"special://home/addons/plugin.video.emby/icon.png") "special://home/addons/plugin.video.emby/icon.png")
if "heading" in kwargs:
kwargs['heading'] = kwargs['heading'].replace("{emby}", language(29999))
types = { types = {
'yesno': d.yesno, 'yesno': d.yesno,
'ok': d.ok, 'ok': d.ok,
'notification': d.notification, 'notification': d.notification,
'input': d.input 'input': d.input,
'select': d.select,
'numeric': d.numeric
} }
return types[type_](**kwargs) return types[type_](**kwargs)

View file

@ -23,7 +23,7 @@ log = logging.getLogger("EMBY."+__name__)
################################################################################################## ##################################################################################################
class WebSocket_Client(threading.Thread): class WebSocketClient(threading.Thread):
_shared_state = {} _shared_state = {}
@ -35,12 +35,12 @@ class WebSocket_Client(threading.Thread):
self.__dict__ = self._shared_state self.__dict__ = self._shared_state
self.monitor = xbmc.Monitor() self.monitor = xbmc.Monitor()
self.doutils = downloadutils.DownloadUtils() self.doutils = downloadutils.DownloadUtils()
self.client_info = clientinfo.ClientInfo() self.client_info = clientinfo.ClientInfo()
self.device_id = self.client_info.get_device_id() self.device_id = self.client_info.get_device_id()
self.library_sync = librarysync.LibrarySync() self.library_sync = librarysync.LibrarySync()
threading.Thread.__init__(self) threading.Thread.__init__(self)
@ -66,15 +66,15 @@ class WebSocket_Client(threading.Thread):
message_type = result['MessageType'] message_type = result['MessageType']
data = result['Data'] data = result['Data']
if message_type not in ('SessionEnded'): if message_type not in ('NotificationAdded', 'SessionEnded'):
# Mute certain events # Mute certain events
log.info("Message: %s" % message) log.info("Message: %s", message)
if message_type == "Play": if message_type == 'Play':
# A remote control play command has been sent from the server. # A remote control play command has been sent from the server.
self._play_(data) self._play_(data)
elif message_type == "Playstate": elif message_type == 'Playstate':
# A remote control update playstate command has been sent from the server. # A remote control update playstate command has been sent from the server.
self._playstate_(data) self._playstate_(data)
@ -84,16 +84,7 @@ class WebSocket_Client(threading.Thread):
self.library_sync.triage_items("userdata", userdata_list) self.library_sync.triage_items("userdata", userdata_list)
elif message_type == "LibraryChanged": elif message_type == "LibraryChanged":
self._library_changed(data)
librarySync = self.library_sync
processlist = {
'added': data['ItemsAdded'],
'update': data['ItemsUpdated'],
'remove': data['ItemsRemoved']
}
for action in processlist:
librarySync.triage_items(action, processlist[action])
elif message_type == "GeneralCommand": elif message_type == "GeneralCommand":
self._general_commands(data) self._general_commands(data)
@ -118,26 +109,26 @@ class WebSocket_Client(threading.Thread):
playlist_ = playlist.Playlist() playlist_ = playlist.Playlist()
if command == "PlayNow": if command == 'PlayNow':
startat = data.get('StartPositionTicks', 0) startat = data.get('StartPositionTicks', 0)
playlist_.playAll(item_ids, startat) playlist_.play_all(item_ids, startat)
dialog(type_="notification", dialog(type_="notification",
heading=lang(29999), heading="{emby}",
message="%s %s" % (len(item_ids), lang(33004)), message="%s %s" % (len(item_ids), lang(33004)),
icon="{emby}", icon="{emby}",
sound=False) sound=False)
elif command == "PlayNext": elif command == 'PlayNext':
newplaylist = playlist_.modifyPlaylist(item_ids) new_playlist = playlist_.modify_playlist(item_ids)
dialog(type_="notification", dialog(type_="notification",
heading=lang(29999), heading="{emby}",
message="%s %s" % (len(item_ids), lang(33005)), message="%s %s" % (len(item_ids), lang(33005)),
icon="{emby}", icon="{emby}",
sound=False) sound=False)
player = xbmc.Player() player = xbmc.Player()
if not player.isPlaying(): if not player.isPlaying():
# Only start the playlist if nothing is playing # Only start the playlist if nothing is playing
player.play(newplaylist) player.play(new_playlist)
@classmethod @classmethod
def _playstate_(cls, data): def _playstate_(cls, data):
@ -151,21 +142,36 @@ class WebSocket_Client(threading.Thread):
'Unpause': player.pause, 'Unpause': player.pause,
'Pause': player.pause, 'Pause': player.pause,
'NextTrack': player.playnext, 'NextTrack': player.playnext,
'PreviousTrack': player.playprevious, 'PreviousTrack': player.playprevious
'Seek': player.seekTime
} }
action = actions[command] if command == 'Seek':
if command == "Seek":
seekto = data['SeekPositionTicks'] seek_to = data['SeekPositionTicks']
seektime = seekto / 10000000.0 seek_time = seek_to / 10000000.0
action(seektime) player.seekTime(seek_time)
log.info("Seek to %s", seektime) log.info("Seek to %s", seek_time)
else:
action() elif command in actions:
actions[command]()
log.info("Command: %s completed", command) log.info("Command: %s completed", command)
else:
log.info("Unknown command: %s", command)
return
window('emby_command', value="true") window('emby_command', value="true")
def _library_changed(self, data):
process_list = {
'added': data['ItemsAdded'],
'update': data['ItemsUpdated'],
'remove': data['ItemsRemoved']
}
for action in process_list:
self.library_sync.triage_items(action, process_list[action])
@classmethod @classmethod
def _general_commands(cls, data): def _general_commands(cls, data):
@ -177,42 +183,46 @@ class WebSocket_Client(threading.Thread):
player = xbmc.Player() player = xbmc.Player()
# These commands need to be reported back # These commands need to be reported back
if command == "Mute": if command == 'Mute':
xbmc.executebuiltin('Mute') xbmc.executebuiltin('Mute')
elif command == "Unmute":
elif command == 'Unmute':
xbmc.executebuiltin('Mute') xbmc.executebuiltin('Mute')
elif command == "SetVolume":
elif command == 'SetVolume':
volume = arguments['Volume'] volume = arguments['Volume']
xbmc.executebuiltin('SetVolume(%s[,showvolumebar])' % volume) xbmc.executebuiltin('SetVolume(%s[,showvolumebar])' % volume)
elif command == "SetAudioStreamIndex":
elif command == 'SetAudioStreamIndex':
index = int(arguments['Index']) index = int(arguments['Index'])
player.setAudioStream(index - 1) player.setAudioStream(index - 1)
elif command == "SetSubtitleStreamIndex":
embyindex = int(arguments['Index'])
currentFile = player.getPlayingFile()
mapping = window('emby_%s.indexMapping' % currentFile) elif command == 'SetSubtitleStreamIndex':
emby_index = int(arguments['Index'])
current_file = player.getPlayingFile()
mapping = window('emby_%s.indexMapping' % current_file)
if mapping: if mapping:
externalIndex = json.loads(mapping) external_index = json.loads(mapping)
# If there's external subtitles added via playbackutils # If there's external subtitles added via playbackutils
for index in externalIndex: for index in external_index:
if externalIndex[index] == embyindex: if external_index[index] == emby_index:
player.setSubtitleStream(int(index)) player.setSubtitleStream(int(index))
break break
else: else:
# User selected internal subtitles # User selected internal subtitles
external = len(externalIndex) external = len(external_index)
audioTracks = len(player.getAvailableAudioStreams()) audio_tracks = len(player.getAvailableAudioStreams())
player.setSubtitleStream(external + embyindex - audioTracks - 1) player.setSubtitleStream(external + emby_index - audio_tracks - 1)
else: else:
# Emby merges audio and subtitle index together # Emby merges audio and subtitle index together
audioTracks = len(player.getAvailableAudioStreams()) audio_tracks = len(player.getAvailableAudioStreams())
player.setSubtitleStream(index - audioTracks - 1) player.setSubtitleStream(index - audio_tracks - 1)
# Let service know # Let service know
window('emby_command', value="true") window('emby_command', value="true")
elif command == "DisplayMessage": elif command == 'DisplayMessage':
header = arguments['Header'] header = arguments['Header']
text = arguments['Text'] text = arguments['Text']
@ -222,26 +232,28 @@ class WebSocket_Client(threading.Thread):
icon="{emby}", icon="{emby}",
time=4000) time=4000)
elif command == "SendString": elif command == 'SendString':
params = { params = {
'text': arguments['String'], 'text': arguments['String'],
'done': False 'done': False
} }
result = JSONRPC("Input.SendText").execute(params) JSONRPC('Input.SendText').execute(params)
elif command in ("MoveUp", "MoveDown", "MoveRight", "MoveLeft"): elif command in ('MoveUp', 'MoveDown', 'MoveRight', 'MoveLeft'):
# Commands that should wake up display # Commands that should wake up display
actions = { actions = {
'MoveUp': "Input.Up", 'MoveUp': "Input.Up",
'MoveDown': "Input.Down", 'MoveDown': "Input.Down",
'MoveRight': "Input.Right", 'MoveRight': "Input.Right",
'MoveLeft': "Input.Left" 'MoveLeft': "Input.Left"
} }
result = JSONRPC(actions[command]).execute() JSONRPC(actions[command]).execute()
elif command == "GoHome": elif command == 'GoHome':
result = JSONRPC("GUI.ActivateWindow").execute({"window":"home"}) JSONRPC('GUI.ActivateWindow').execute({'window': "home"})
else: else:
builtin = { builtin = {
@ -262,16 +274,15 @@ class WebSocket_Client(threading.Thread):
'VolumeUp': 'Action(VolumeUp)', 'VolumeUp': 'Action(VolumeUp)',
'VolumeDown': 'Action(VolumeDown)', 'VolumeDown': 'Action(VolumeDown)',
} }
action = builtin.get(command) if command in builtin:
if action: xbmc.executebuiltin(builtin[command])
xbmc.executebuiltin(action)
@classmethod @classmethod
def _server_restarting(cls): def _server_restarting(cls):
if settings('supressRestartMsg') == "true": if settings('supressRestartMsg') == "true":
dialog(type_="notification", dialog(type_="notification",
heading=lang(29999), heading="{emby}",
message=lang(33006), message=lang(33006),
icon="{emby}") icon="{emby}")
@ -282,6 +293,7 @@ class WebSocket_Client(threading.Thread):
self.doutils.postCapabilities(self.device_id) self.doutils.postCapabilities(self.device_id)
def on_error(self, ws, error): def on_error(self, ws, error):
if "10061" in str(error): if "10061" in str(error):
# Server is offline # Server is offline
pass pass
@ -291,7 +303,6 @@ class WebSocket_Client(threading.Thread):
def run(self): def run(self):
# websocket.enableTrace(True) # websocket.enableTrace(True)
user_id = window('emby_currUser') user_id = window('emby_currUser')
server = window('emby_server%s' % user_id) server = window('emby_server%s' % user_id)
token = window('emby_accessToken%s' % user_id) token = window('emby_accessToken%s' % user_id)
@ -305,9 +316,9 @@ class WebSocket_Client(threading.Thread):
log.info("websocket url: %s", websocket_url) log.info("websocket url: %s", websocket_url)
self._client = websocket.WebSocketApp(websocket_url, self._client = websocket.WebSocketApp(websocket_url,
on_message=self.on_message, on_message=self.on_message,
on_error=self.on_error, on_error=self.on_error,
on_close=self.on_close) on_close=self.on_close)
self._client.on_open = self.on_open self._client.on_open = self.on_open
log.warn("----===## Starting WebSocketClient ##===----") log.warn("----===## Starting WebSocketClient ##===----")

View file

@ -25,6 +25,7 @@ import clientinfo
import initialsetup import initialsetup
import kodimonitor import kodimonitor
import librarysync import librarysync
import loghandler
import player import player
import videonodes import videonodes
import websocket_client as wsc import websocket_client as wsc
@ -32,8 +33,6 @@ from utils import window, settings, dialog, language as lang
################################################################################################# #################################################################################################
import loghandler
loghandler.config() loghandler.config()
log = logging.getLogger("EMBY.service") log = logging.getLogger("EMBY.service")
@ -101,7 +100,7 @@ class Service(object):
# Initialize important threads # Initialize important threads
user = userclient.UserClient() user = userclient.UserClient()
ws = wsc.WebSocket_Client() ws = wsc.WebSocketClient()
library = librarysync.LibrarySync() library = librarysync.LibrarySync()
kplayer = player.Player() kplayer = player.Player()
# Sync and progress report # Sync and progress report
@ -168,10 +167,10 @@ class Service(object):
else: else:
add = "" add = ""
dialog(type_="notification", dialog(type_="notification",
heading=lang(29999), heading="{emby}",
message=("%s %s%s!" message=("%s %s%s!"
% (lang(33000), user.get_username().decode('utf-8'), % (lang(33000), user.get_username().decode('utf-8'),
add.decode('utf-8'))), add.decode('utf-8'))),
icon="{emby}", icon="{emby}",
time=2000, time=2000,
sound=False) sound=False)
@ -236,7 +235,7 @@ class Service(object):
# device going to sleep # device going to sleep
if self.websocket_running: if self.websocket_running:
ws.stop_client() ws.stop_client()
ws = wsc.WebSocket_Client() ws = wsc.WebSocketClient()
self.websocket_running = False self.websocket_running = False
if self.library_running: if self.library_running:
@ -254,7 +253,7 @@ class Service(object):
break break
# Alert the user that server is online. # Alert the user that server is online.
dialog(type_="notification", dialog(type_="notification",
heading=lang(29999), heading="{emby}",
message=lang(33003), message=lang(33003),
icon="{emby}", icon="{emby}",
time=2000, time=2000,
@ -292,12 +291,14 @@ class Service(object):
log.warn("======== STOP %s ========", self.addon_name) log.warn("======== STOP %s ========", self.addon_name)
# Delay option
DELAY = int(settings('startupDelay'))
log.warn("Delaying emby startup by: %s sec...", DELAY)
if DELAY and xbmc.Monitor().waitForAbort(DELAY): if __name__ == "__main__":
# Start the service # Delay option
log.warn("Abort requested while waiting. Emby for kodi not started.") DELAY = int(settings('startupDelay'))
else: log.warn("Delaying emby startup by: %s sec...", DELAY)
Service().service_entry_point()
if DELAY and xbmc.Monitor().waitForAbort(DELAY):
# Start the service
log.warn("Abort requested while waiting. Emby for kodi not started.")
else:
Service().service_entry_point()