Centralize Kodi json rpc
This commit is contained in:
parent
f6b666e892
commit
f2bc95813a
9 changed files with 472 additions and 500 deletions
|
@ -1,15 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
###############################################################################
|
||||
import logging
|
||||
from json import dumps, loads
|
||||
from logging import getLogger
|
||||
import requests
|
||||
from shutil import rmtree
|
||||
from urllib import quote_plus, unquote
|
||||
from threading import Thread
|
||||
from Queue import Queue, Empty
|
||||
import json_rpc as js
|
||||
|
||||
from xbmc import executeJSONRPC, sleep, translatePath
|
||||
from xbmc import sleep, translatePath
|
||||
from xbmcvfs import exists
|
||||
|
||||
from utils import window, settings, language as lang, kodiSQL, tryEncode, \
|
||||
|
@ -20,7 +20,7 @@ import requests.packages.urllib3
|
|||
requests.packages.urllib3.disable_warnings()
|
||||
###############################################################################
|
||||
|
||||
log = logging.getLogger("PLEX."+__name__)
|
||||
LOG = getLogger("PLEX." + __name__)
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
@ -34,87 +34,16 @@ def setKodiWebServerDetails():
|
|||
xbmc_port = None
|
||||
xbmc_username = None
|
||||
xbmc_password = None
|
||||
web_query = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Settings.GetSettingValue",
|
||||
"params": {
|
||||
"setting": "services.webserver"
|
||||
}
|
||||
}
|
||||
result = executeJSONRPC(dumps(web_query))
|
||||
result = loads(result)
|
||||
try:
|
||||
xbmc_webserver_enabled = result['result']['value']
|
||||
except (KeyError, TypeError):
|
||||
xbmc_webserver_enabled = False
|
||||
if not xbmc_webserver_enabled:
|
||||
if js.get_setting('services.webserver') in (None, False):
|
||||
# Enable the webserver, it is disabled
|
||||
web_port = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Settings.SetSettingValue",
|
||||
"params": {
|
||||
"setting": "services.webserverport",
|
||||
"value": 8080
|
||||
}
|
||||
}
|
||||
result = executeJSONRPC(dumps(web_port))
|
||||
xbmc_port = 8080
|
||||
web_user = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Settings.SetSettingValue",
|
||||
"params": {
|
||||
"setting": "services.webserver",
|
||||
"value": True
|
||||
}
|
||||
}
|
||||
result = executeJSONRPC(dumps(web_user))
|
||||
xbmc_username = "kodi"
|
||||
js.set_setting('services.webserverport', xbmc_port)
|
||||
js.set_setting('services.webserver', True)
|
||||
# Webserver already enabled
|
||||
web_port = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Settings.GetSettingValue",
|
||||
"params": {
|
||||
"setting": "services.webserverport"
|
||||
}
|
||||
}
|
||||
result = executeJSONRPC(dumps(web_port))
|
||||
result = loads(result)
|
||||
try:
|
||||
xbmc_port = result['result']['value']
|
||||
except (TypeError, KeyError):
|
||||
pass
|
||||
web_user = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Settings.GetSettingValue",
|
||||
"params": {
|
||||
"setting": "services.webserverusername"
|
||||
}
|
||||
}
|
||||
result = executeJSONRPC(dumps(web_user))
|
||||
result = loads(result)
|
||||
try:
|
||||
xbmc_username = result['result']['value']
|
||||
except (TypeError, KeyError):
|
||||
pass
|
||||
web_pass = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "Settings.GetSettingValue",
|
||||
"params": {
|
||||
"setting": "services.webserverpassword"
|
||||
}
|
||||
}
|
||||
result = executeJSONRPC(dumps(web_pass))
|
||||
result = loads(result)
|
||||
try:
|
||||
xbmc_password = result['result']['value']
|
||||
except TypeError:
|
||||
pass
|
||||
xbmc_port = js.get_setting('services.webserverport')
|
||||
xbmc_username = js.get_setting('services.webserverusername')
|
||||
xbmc_password = js.get_setting('services.webserverpassword')
|
||||
return (xbmc_port, xbmc_username, xbmc_password)
|
||||
|
||||
|
||||
|
@ -152,7 +81,7 @@ class Image_Cache_Thread(Thread):
|
|||
# Set in service.py
|
||||
if thread_stopped():
|
||||
# Abort was requested while waiting. We should exit
|
||||
log.info("---===### Stopped Image_Cache_Thread ###===---")
|
||||
LOG.info("---===### Stopped Image_Cache_Thread ###===---")
|
||||
return
|
||||
sleep(1000)
|
||||
try:
|
||||
|
@ -179,10 +108,10 @@ class Image_Cache_Thread(Thread):
|
|||
# Server thinks its a DOS attack, ('error 10053')
|
||||
# Wait before trying again
|
||||
if sleeptime > 5:
|
||||
log.error('Repeatedly got ConnectionError for url %s'
|
||||
LOG.error('Repeatedly got ConnectionError for url %s'
|
||||
% double_urldecode(url))
|
||||
break
|
||||
log.debug('Were trying too hard to download art, server '
|
||||
LOG.debug('Were trying too hard to download art, server '
|
||||
'over-loaded. Sleep %s seconds before trying '
|
||||
'again to download %s'
|
||||
% (2**sleeptime, double_urldecode(url)))
|
||||
|
@ -190,18 +119,18 @@ class Image_Cache_Thread(Thread):
|
|||
sleeptime += 1
|
||||
continue
|
||||
except Exception as e:
|
||||
log.error('Unknown exception for url %s: %s'
|
||||
LOG.error('Unknown exception for url %s: %s'
|
||||
% (double_urldecode(url), e))
|
||||
import traceback
|
||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
||||
LOG.error("Traceback:\n%s" % traceback.format_exc())
|
||||
break
|
||||
# We did not even get a timeout
|
||||
break
|
||||
queue.task_done()
|
||||
log.debug('Cached art: %s' % double_urldecode(url))
|
||||
LOG.debug('Cached art: %s' % double_urldecode(url))
|
||||
# Sleep for a bit to reduce CPU strain
|
||||
sleep(sleep_between)
|
||||
log.info("---===### Stopped Image_Cache_Thread ###===---")
|
||||
LOG.info("---===### Stopped Image_Cache_Thread ###===---")
|
||||
|
||||
|
||||
class Artwork():
|
||||
|
@ -217,11 +146,11 @@ class Artwork():
|
|||
if not dialog('yesno', "Image Texture Cache", lang(39250)):
|
||||
return
|
||||
|
||||
log.info("Doing Image Cache Sync")
|
||||
LOG.info("Doing Image Cache Sync")
|
||||
|
||||
# ask to rest all existing or not
|
||||
if dialog('yesno', "Image Texture Cache", lang(39251)):
|
||||
log.info("Resetting all cache data first")
|
||||
LOG.info("Resetting all cache data first")
|
||||
# Remove all existing textures first
|
||||
path = tryDecode(translatePath("special://thumbnails/"))
|
||||
if exists_dir(path):
|
||||
|
@ -248,7 +177,7 @@ class Artwork():
|
|||
cursor.execute(query, ('actor', ))
|
||||
result = cursor.fetchall()
|
||||
total = len(result)
|
||||
log.info("Image cache sync about to process %s video images" % total)
|
||||
LOG.info("Image cache sync about to process %s video images" % total)
|
||||
connection.close()
|
||||
|
||||
for url in result:
|
||||
|
@ -259,7 +188,7 @@ class Artwork():
|
|||
cursor.execute("SELECT url FROM art")
|
||||
result = cursor.fetchall()
|
||||
total = len(result)
|
||||
log.info("Image cache sync about to process %s music images" % total)
|
||||
LOG.info("Image cache sync about to process %s music images" % total)
|
||||
connection.close()
|
||||
for url in result:
|
||||
self.cacheTexture(url[0])
|
||||
|
@ -364,7 +293,7 @@ class Artwork():
|
|||
url = cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Add the artwork
|
||||
log.debug("Adding Art Link for kodiId: %s (%s)"
|
||||
LOG.debug("Adding Art Link for kodiId: %s (%s)"
|
||||
% (kodiId, imageUrl))
|
||||
query = (
|
||||
'''
|
||||
|
@ -382,7 +311,7 @@ class Artwork():
|
|||
imageType in ("fanart", "poster")):
|
||||
# Delete current entry before updating with the new one
|
||||
self.deleteCachedArtwork(url)
|
||||
log.debug("Updating Art url for %s kodiId %s %s -> (%s)"
|
||||
LOG.debug("Updating Art url for %s kodiId %s %s -> (%s)"
|
||||
% (imageType, kodiId, url, imageUrl))
|
||||
query = ' '.join((
|
||||
"UPDATE art",
|
||||
|
@ -418,11 +347,11 @@ class Artwork():
|
|||
(url,))
|
||||
cachedurl = cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
log.info("Could not find cached url.")
|
||||
LOG.info("Could not find cached url.")
|
||||
else:
|
||||
# Delete thumbnail as well as the entry
|
||||
path = translatePath("special://thumbnails/%s" % cachedurl)
|
||||
log.debug("Deleting cached thumbnail: %s" % path)
|
||||
LOG.debug("Deleting cached thumbnail: %s" % path)
|
||||
if exists(path):
|
||||
rmtree(tryDecode(path), ignore_errors=True)
|
||||
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
||||
|
|
|
@ -12,12 +12,13 @@ from xbmc import sleep, executebuiltin, translatePath
|
|||
from xbmcgui import ListItem
|
||||
|
||||
from utils import window, settings, language as lang, dialog, tryEncode, \
|
||||
CatchExceptions, JSONRPC, exists_dir, plex_command, tryDecode
|
||||
CatchExceptions, exists_dir, plex_command, tryDecode
|
||||
import downloadutils
|
||||
|
||||
from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \
|
||||
GetMachineIdentifier
|
||||
from PlexAPI import API
|
||||
import json_rpc as js
|
||||
import variables as v
|
||||
|
||||
###############################################################################
|
||||
|
@ -268,7 +269,6 @@ def createListItem(item, appendShowTitle=False, appendSxxExx=False):
|
|||
|
||||
##### GET NEXTUP EPISODES FOR TAGNAME #####
|
||||
def getNextUpEpisodes(tagname, limit):
|
||||
|
||||
count = 0
|
||||
# if the addon is called with nextup parameter,
|
||||
# we return the nextepisodes list of the given tagname
|
||||
|
@ -283,15 +283,7 @@ def getNextUpEpisodes(tagname, limit):
|
|||
]},
|
||||
'properties': ['title', 'studio', 'mpaa', 'file', 'art']
|
||||
}
|
||||
result = JSONRPC('VideoLibrary.GetTVShows').execute(params)
|
||||
|
||||
# If we found any, find the oldest unwatched show for each one.
|
||||
try:
|
||||
items = result['result']['tvshows']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
for item in items:
|
||||
for item in js.get_tv_shows(params):
|
||||
if settings('ignoreSpecialsNextEpisodes') == "true":
|
||||
params = {
|
||||
'tvshowid': item['tvshowid'],
|
||||
|
@ -328,23 +320,13 @@ def getNextUpEpisodes(tagname, limit):
|
|||
],
|
||||
'limits': {"end": 1}
|
||||
}
|
||||
|
||||
result = JSONRPC('VideoLibrary.GetEpisodes').execute(params)
|
||||
try:
|
||||
episodes = result['result']['episodes']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
for episode in episodes:
|
||||
li = createListItem(episode)
|
||||
for episode in js.get_episodes(params):
|
||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||
url=episode['file'],
|
||||
listitem=li)
|
||||
listitem=createListItem(episode))
|
||||
count += 1
|
||||
|
||||
if count == limit:
|
||||
break
|
||||
|
||||
xbmcplugin.endOfDirectory(handle=HANDLE)
|
||||
|
||||
|
||||
|
@ -364,14 +346,7 @@ def getInProgressEpisodes(tagname, limit):
|
|||
]},
|
||||
'properties': ['title', 'studio', 'mpaa', 'file', 'art']
|
||||
}
|
||||
result = JSONRPC('VideoLibrary.GetTVShows').execute(params)
|
||||
# If we found any, find the oldest unwatched show for each one.
|
||||
try:
|
||||
items = result['result']['tvshows']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
for item in items:
|
||||
for item in js.get_tv_shows(params):
|
||||
params = {
|
||||
'tvshowid': item['tvshowid'],
|
||||
'sort': {'method': "episode"},
|
||||
|
@ -384,22 +359,13 @@ def getInProgressEpisodes(tagname, limit):
|
|||
"tvshowid", "art", "cast", "streamdetails", "firstaired",
|
||||
"runtime", "writer", "dateadded", "lastplayed"]
|
||||
}
|
||||
result = JSONRPC('VideoLibrary.GetEpisodes').execute(params)
|
||||
try:
|
||||
episodes = result['result']['episodes']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
for episode in episodes:
|
||||
li = createListItem(episode)
|
||||
for episode in js.get_episodes(params):
|
||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||
url=episode['file'],
|
||||
listitem=li)
|
||||
listitem=createListItem(episode))
|
||||
count += 1
|
||||
|
||||
if count == limit:
|
||||
break
|
||||
|
||||
xbmcplugin.endOfDirectory(handle=HANDLE)
|
||||
|
||||
##### GET RECENT EPISODES FOR TAGNAME #####
|
||||
|
@ -412,22 +378,13 @@ def getRecentEpisodes(viewid, mediatype, tagname, limit):
|
|||
appendShowTitle = settings('RecentTvAppendShow') == 'true'
|
||||
appendSxxExx = settings('RecentTvAppendSeason') == 'true'
|
||||
# First we get a list of all the TV shows - filtered by tag
|
||||
allshowsIds = set()
|
||||
params = {
|
||||
'sort': {'order': "descending", 'method': "dateadded"},
|
||||
'filter': {'operator': "is", 'field': "tag", 'value': "%s" % tagname},
|
||||
}
|
||||
result = JSONRPC('VideoLibrary.GetTVShows').execute(params)
|
||||
# If we found any, find the oldest unwatched show for each one.
|
||||
try:
|
||||
items = result['result'][mediatype]
|
||||
except (KeyError, TypeError):
|
||||
# No items, empty folder
|
||||
xbmcplugin.endOfDirectory(handle=HANDLE)
|
||||
return
|
||||
|
||||
allshowsIds = set()
|
||||
for item in items:
|
||||
allshowsIds.add(item['tvshowid'])
|
||||
for tv_show in js.get_tv_shows(params):
|
||||
allshowsIds.add(tv_show['tvshowid'])
|
||||
params = {
|
||||
'sort': {'order': "descending", 'method': "dateadded"},
|
||||
'properties': ["title", "playcount", "season", "episode", "showtitle",
|
||||
|
@ -442,26 +399,18 @@ def getRecentEpisodes(viewid, mediatype, tagname, limit):
|
|||
'field': "playcount",
|
||||
'value': "1"
|
||||
}
|
||||
result = JSONRPC('VideoLibrary.GetEpisodes').execute(params)
|
||||
try:
|
||||
episodes = result['result']['episodes']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
for episode in episodes:
|
||||
for episode in js.get_episodes(params):
|
||||
if episode['tvshowid'] in allshowsIds:
|
||||
li = createListItem(episode,
|
||||
listitem = createListItem(episode,
|
||||
appendShowTitle=appendShowTitle,
|
||||
appendSxxExx=appendSxxExx)
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=HANDLE,
|
||||
url=episode['file'],
|
||||
listitem=li)
|
||||
listitem=listitem)
|
||||
count += 1
|
||||
|
||||
if count == limit:
|
||||
break
|
||||
|
||||
xbmcplugin.endOfDirectory(handle=HANDLE)
|
||||
|
||||
|
||||
|
@ -644,15 +593,6 @@ def getOnDeck(viewid, mediatype, tagname, limit):
|
|||
{'operator': "is", 'field': "tag", 'value': "%s" % tagname}
|
||||
]}
|
||||
}
|
||||
result = JSONRPC('VideoLibrary.GetTVShows').execute(params)
|
||||
# If we found any, find the oldest unwatched show for each one.
|
||||
try:
|
||||
items = result['result'][mediatype]
|
||||
except (KeyError, TypeError):
|
||||
# Now items retrieved - empty directory
|
||||
xbmcplugin.endOfDirectory(handle=HANDLE)
|
||||
return
|
||||
|
||||
params = {
|
||||
'sort': {'method': "episode"},
|
||||
'limits': {"end": 1},
|
||||
|
@ -677,7 +617,6 @@ def getOnDeck(viewid, mediatype, tagname, limit):
|
|||
{'operator': "true", 'field': "inprogress", 'value': ""}
|
||||
]
|
||||
}
|
||||
|
||||
# Are there any episodes still in progress/not yet finished watching?!?
|
||||
# Then we should show this episode, NOT the "next up"
|
||||
inprog_params = {
|
||||
|
@ -687,35 +626,26 @@ def getOnDeck(viewid, mediatype, tagname, limit):
|
|||
}
|
||||
|
||||
count = 0
|
||||
for item in items:
|
||||
for item in js.get_tv_shows(params):
|
||||
inprog_params['tvshowid'] = item['tvshowid']
|
||||
result = JSONRPC('VideoLibrary.GetEpisodes').execute(inprog_params)
|
||||
try:
|
||||
episodes = result['result']['episodes']
|
||||
except (KeyError, TypeError):
|
||||
episodes = js.get_episodes(inprog_params)
|
||||
if not episodes:
|
||||
# No, there are no episodes not yet finished. Get "next up"
|
||||
params['tvshowid'] = item['tvshowid']
|
||||
result = JSONRPC('VideoLibrary.GetEpisodes').execute(params)
|
||||
try:
|
||||
episodes = result['result']['episodes']
|
||||
except (KeyError, TypeError):
|
||||
# Also no episodes currently coming up
|
||||
continue
|
||||
episodes = js.get_episodes(params)
|
||||
for episode in episodes:
|
||||
# There will always be only 1 episode ('limit=1')
|
||||
li = createListItem(episode,
|
||||
listitem = createListItem(episode,
|
||||
appendShowTitle=appendShowTitle,
|
||||
appendSxxExx=appendSxxExx)
|
||||
xbmcplugin.addDirectoryItem(
|
||||
handle=HANDLE,
|
||||
url=episode['file'],
|
||||
listitem=li,
|
||||
listitem=listitem,
|
||||
isFolder=False)
|
||||
|
||||
count += 1
|
||||
if count >= limit:
|
||||
break
|
||||
|
||||
xbmcplugin.endOfDirectory(handle=HANDLE)
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Collection of functions using the Kodi JSON RPC interface.
|
||||
See http://kodi.wiki/view/JSON-RPC_API
|
||||
"""
|
||||
from utils import JSONRPC, milliseconds_to_kodi_time
|
||||
from utils import jsonrpc, milliseconds_to_kodi_time
|
||||
|
||||
|
||||
def get_players():
|
||||
|
@ -14,7 +14,7 @@ def get_players():
|
|||
'picture': ...
|
||||
}
|
||||
"""
|
||||
info = JSONRPC("Player.GetActivePlayers").execute()['result'] or []
|
||||
info = jsonrpc("Player.GetActivePlayers").execute()['result'] or []
|
||||
ret = {}
|
||||
for player in info:
|
||||
player['playerid'] = int(player['playerid'])
|
||||
|
@ -53,7 +53,19 @@ def get_playlists():
|
|||
{u'playlistid': 2, u'type': u'picture'}
|
||||
]
|
||||
"""
|
||||
return JSONRPC('Playlist.GetPlaylists').execute()
|
||||
try:
|
||||
ret = jsonrpc('Playlist.GetPlaylists').execute()['result']
|
||||
except KeyError:
|
||||
ret = []
|
||||
return ret
|
||||
|
||||
|
||||
def get_volume():
|
||||
"""
|
||||
Returns the Kodi volume as an int between 0 (min) and 100 (max)
|
||||
"""
|
||||
return jsonrpc('Application.GetProperties').execute(
|
||||
{"properties": ['volume']})['result']['volume']
|
||||
|
||||
|
||||
def set_volume(volume):
|
||||
|
@ -61,7 +73,15 @@ def set_volume(volume):
|
|||
Set's the volume (for Kodi overall, not only a player).
|
||||
Feed with an int
|
||||
"""
|
||||
return JSONRPC('Application.SetVolume').execute({"volume": volume})
|
||||
return jsonrpc('Application.SetVolume').execute({"volume": volume})
|
||||
|
||||
|
||||
def get_muted():
|
||||
"""
|
||||
Returns True if Kodi is muted, False otherwise
|
||||
"""
|
||||
return jsonrpc('Application.GetProperties').execute(
|
||||
{"properties": ['muted']})['result']['muted']
|
||||
|
||||
|
||||
def play():
|
||||
|
@ -69,7 +89,7 @@ def play():
|
|||
Toggles all Kodi players to play
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.PlayPause").execute({"playerid": playerid,
|
||||
jsonrpc("Player.PlayPause").execute({"playerid": playerid,
|
||||
"play": True})
|
||||
|
||||
|
||||
|
@ -78,7 +98,7 @@ def pause():
|
|||
Pauses playback for all Kodi players
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.PlayPause").execute({"playerid": playerid,
|
||||
jsonrpc("Player.PlayPause").execute({"playerid": playerid,
|
||||
"play": False})
|
||||
|
||||
|
||||
|
@ -87,7 +107,7 @@ def stop():
|
|||
Stops playback for all Kodi players
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.Stop").execute({"playerid": playerid})
|
||||
jsonrpc("Player.Stop").execute({"playerid": playerid})
|
||||
|
||||
|
||||
def seek_to(offset):
|
||||
|
@ -95,7 +115,7 @@ def seek_to(offset):
|
|||
Seeks all Kodi players to offset [int]
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.Seek").execute(
|
||||
jsonrpc("Player.Seek").execute(
|
||||
{"playerid": playerid,
|
||||
"value": milliseconds_to_kodi_time(offset)})
|
||||
|
||||
|
@ -105,7 +125,7 @@ def smallforward():
|
|||
Small step forward for all Kodi players
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.Seek").execute({"playerid": playerid,
|
||||
jsonrpc("Player.Seek").execute({"playerid": playerid,
|
||||
"value": "smallforward"})
|
||||
|
||||
|
||||
|
@ -114,7 +134,7 @@ def smallbackward():
|
|||
Small step backward for all Kodi players
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.Seek").execute({"playerid": playerid,
|
||||
jsonrpc("Player.Seek").execute({"playerid": playerid,
|
||||
"value": "smallbackward"})
|
||||
|
||||
|
||||
|
@ -123,7 +143,7 @@ def skipnext():
|
|||
Skips to the next item to play for all Kodi players
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.GoTo").execute({"playerid": playerid,
|
||||
jsonrpc("Player.GoTo").execute({"playerid": playerid,
|
||||
"to": "next"})
|
||||
|
||||
|
||||
|
@ -132,7 +152,7 @@ def skipprevious():
|
|||
Skips to the previous item to play for all Kodi players
|
||||
"""
|
||||
for playerid in get_player_ids():
|
||||
JSONRPC("Player.GoTo").execute({"playerid": playerid,
|
||||
jsonrpc("Player.GoTo").execute({"playerid": playerid,
|
||||
"to": "previous"})
|
||||
|
||||
|
||||
|
@ -140,46 +160,209 @@ def input_up():
|
|||
"""
|
||||
Tells Kodi the users pushed up
|
||||
"""
|
||||
JSONRPC("Input.Up").execute()
|
||||
jsonrpc("Input.Up").execute()
|
||||
|
||||
|
||||
def input_down():
|
||||
"""
|
||||
Tells Kodi the users pushed down
|
||||
"""
|
||||
JSONRPC("Input.Down").execute()
|
||||
jsonrpc("Input.Down").execute()
|
||||
|
||||
|
||||
def input_left():
|
||||
"""
|
||||
Tells Kodi the users pushed left
|
||||
"""
|
||||
JSONRPC("Input.Left").execute()
|
||||
jsonrpc("Input.Left").execute()
|
||||
|
||||
|
||||
def input_right():
|
||||
"""
|
||||
Tells Kodi the users pushed left
|
||||
"""
|
||||
JSONRPC("Input.Right").execute()
|
||||
jsonrpc("Input.Right").execute()
|
||||
|
||||
|
||||
def input_select():
|
||||
"""
|
||||
Tells Kodi the users pushed select
|
||||
"""
|
||||
JSONRPC("Input.Select").execute()
|
||||
jsonrpc("Input.Select").execute()
|
||||
|
||||
|
||||
def input_home():
|
||||
"""
|
||||
Tells Kodi the users pushed home
|
||||
"""
|
||||
JSONRPC("Input.Home").execute()
|
||||
jsonrpc("Input.Home").execute()
|
||||
|
||||
|
||||
def input_back():
|
||||
"""
|
||||
Tells Kodi the users pushed back
|
||||
"""
|
||||
JSONRPC("Input.Back").execute()
|
||||
jsonrpc("Input.Back").execute()
|
||||
|
||||
|
||||
def playlist_get_items(playlistid, properties):
|
||||
"""
|
||||
playlistid: [int] id of the Kodi playlist
|
||||
properties: [list] of strings for the properties to return
|
||||
e.g. 'title', 'file'
|
||||
|
||||
Returns a list of Kodi playlist items as dicts with the keys specified in
|
||||
properties. Or an empty list if unsuccessful. Example:
|
||||
[{u'title': u'3 Idiots', u'type': u'movie', u'id': 3, u'file':
|
||||
u'smb://nas/PlexMovies/3 Idiots 2009 pt1.mkv', u'label': u'3 Idiots'}]
|
||||
"""
|
||||
reply = jsonrpc('Playlist.GetItems').execute({
|
||||
'playlistid': playlistid,
|
||||
'properties': properties
|
||||
})
|
||||
try:
|
||||
reply = reply['result']['items']
|
||||
except KeyError:
|
||||
reply = []
|
||||
return reply
|
||||
|
||||
|
||||
def playlist_add(playlistid, item):
|
||||
"""
|
||||
Adds an item to the Kodi playlist with id playlistid. item is either the
|
||||
dict
|
||||
{'file': filepath as string}
|
||||
or
|
||||
{kodi_type: kodi_id}
|
||||
|
||||
Returns a dict with the key 'error' if unsuccessful.
|
||||
"""
|
||||
return jsonrpc('Playlist.Add').execute({'playlistid': playlistid,
|
||||
'item': item})
|
||||
|
||||
|
||||
def playlist_insert(params):
|
||||
"""
|
||||
Insert item(s) into playlist. Does not work for picture playlists (aka
|
||||
slideshows). params is the dict
|
||||
{
|
||||
'playlistid': [int]
|
||||
'position': [int]
|
||||
'item': <item>
|
||||
}
|
||||
item is either the dict
|
||||
{'file': filepath as string}
|
||||
or
|
||||
{kodi_type: kodi_id}
|
||||
Returns a dict with the key 'error' if something went wrong.
|
||||
"""
|
||||
return jsonrpc('Playlist.Insert').execute(params)
|
||||
|
||||
|
||||
def playlist_remove(playlistid, position):
|
||||
"""
|
||||
Removes the playlist item at position from the playlist
|
||||
position: [int]
|
||||
|
||||
Returns a dict with the key 'error' if something went wrong.
|
||||
"""
|
||||
return jsonrpc('Playlist.Remove').execute({'playlistid': playlistid,
|
||||
'position': position})
|
||||
|
||||
|
||||
def get_setting(setting):
|
||||
"""
|
||||
Returns the Kodi setting, a [str], or None if not possible
|
||||
"""
|
||||
try:
|
||||
ret = jsonrpc('Settings.GetSettingValue').execute(
|
||||
{'setting': setting})['result']['value']
|
||||
except (KeyError, TypeError):
|
||||
ret = None
|
||||
return ret
|
||||
|
||||
|
||||
def set_setting(setting, value):
|
||||
"""
|
||||
Sets the Kodi setting, a [str], to value
|
||||
"""
|
||||
return jsonrpc('Settings.SetSettingValue').execute(
|
||||
{'setting': setting, 'value': value})
|
||||
|
||||
|
||||
def get_tv_shows(params):
|
||||
"""
|
||||
Returns a list of tv shows for params (check the Kodi wiki)
|
||||
"""
|
||||
ret = jsonrpc('VideoLibrary.GetTVShows').execute(params)
|
||||
try:
|
||||
ret['result']['tvshows']
|
||||
except (KeyError, TypeError):
|
||||
ret = []
|
||||
return ret
|
||||
|
||||
|
||||
def get_episodes(params):
|
||||
"""
|
||||
Returns a list of tv show episodes for params (check the Kodi wiki)
|
||||
"""
|
||||
ret = jsonrpc('VideoLibrary.GetEpisodes').execute(params)
|
||||
try:
|
||||
ret['result']['episodes']
|
||||
except (KeyError, TypeError):
|
||||
ret = []
|
||||
return ret
|
||||
|
||||
|
||||
def current_audiostream(playerid):
|
||||
"""
|
||||
Returns a dict of the active audiostream for playerid [int]:
|
||||
{
|
||||
'index': [int], audiostream index
|
||||
'language': [str]
|
||||
'name': [str]
|
||||
'codec': [str]
|
||||
'bitrate': [int]
|
||||
'channels': [int]
|
||||
}
|
||||
or an empty dict if unsuccessful
|
||||
"""
|
||||
ret = jsonrpc('Player.GetProperties').execute(
|
||||
{'properties': ['currentaudiostream'], 'playerid': playerid})
|
||||
try:
|
||||
ret = ret['result']['currentaudiostream']
|
||||
except (KeyError, TypeError):
|
||||
ret = {}
|
||||
return ret
|
||||
|
||||
|
||||
def current_subtitle(playerid):
|
||||
"""
|
||||
Returns a dict of the active subtitle for playerid [int]:
|
||||
{
|
||||
'index': [int], subtitle index
|
||||
'language': [str]
|
||||
'name': [str]
|
||||
}
|
||||
or an empty dict if unsuccessful
|
||||
"""
|
||||
ret = jsonrpc('Player.GetProperties').execute(
|
||||
{'properties': ['currentsubtitle'], 'playerid': playerid})
|
||||
try:
|
||||
ret = ret['result']['currentsubtitle']
|
||||
except (KeyError, TypeError):
|
||||
ret = {}
|
||||
return ret
|
||||
|
||||
|
||||
def subtitle_enabled(playerid):
|
||||
"""
|
||||
Returns True if a subtitle is enabled, False otherwise
|
||||
"""
|
||||
ret = jsonrpc('Player.GetProperties').execute(
|
||||
{'properties': ['subtitleenabled'], 'playerid': playerid})
|
||||
try:
|
||||
ret = ret['result']['subtitleenabled']
|
||||
except (KeyError, TypeError):
|
||||
ret = False
|
||||
return ret
|
||||
|
|
|
@ -12,7 +12,7 @@ from playbackutils import PlaybackUtils
|
|||
from utils import window
|
||||
from PlexFunctions import GetPlexMetadata
|
||||
from PlexAPI import API
|
||||
from playqueue import lock
|
||||
from playqueue import LOCK
|
||||
import variables as v
|
||||
from downloadutils import DownloadUtils
|
||||
from PKC_listitem import convert_PKC_to_listitem
|
||||
|
@ -62,7 +62,7 @@ class Playback_Starter(Thread):
|
|||
# Video and Music
|
||||
playqueue = self.playqueue.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
||||
with lock:
|
||||
with LOCK:
|
||||
result = PlaybackUtils(xml, playqueue).play(
|
||||
plex_id,
|
||||
kodi_id,
|
||||
|
@ -113,7 +113,7 @@ class Playback_Starter(Thread):
|
|||
log.info('Couldnt find item %s in Kodi db'
|
||||
% api.getRatingKey())
|
||||
playqueue = self.playqueue.get_playqueue_from_type(typus)
|
||||
with lock:
|
||||
with LOCK:
|
||||
result = PlaybackUtils(xml, playqueue).play(
|
||||
plex_id,
|
||||
kodi_id=kodi_id,
|
||||
|
|
|
@ -18,7 +18,7 @@ from PlexFunctions import init_plex_playqueue
|
|||
from PKC_listitem import PKC_ListItem as ListItem, convert_PKC_to_listitem
|
||||
from playlist_func import add_item_to_kodi_playlist, \
|
||||
get_playlist_details_from_xml, add_listitem_to_Kodi_playlist, \
|
||||
add_listitem_to_playlist, remove_from_Kodi_playlist
|
||||
add_listitem_to_playlist, remove_from_kodi_playlist
|
||||
from pickler import Playback_Successful
|
||||
from plexdb_functions import Get_Plex_DB
|
||||
import variables as v
|
||||
|
@ -155,7 +155,7 @@ class PlaybackUtils():
|
|||
playurl,
|
||||
xml[0])
|
||||
# Remove the original item from playlist
|
||||
remove_from_Kodi_playlist(
|
||||
remove_from_kodi_playlist(
|
||||
playqueue,
|
||||
startPos+1)
|
||||
# Readd the original item to playlist - via jsonrpc so we have
|
||||
|
|
|
@ -10,12 +10,13 @@ from utils import window, DateToKodi, getUnixTimestamp, tryDecode, tryEncode
|
|||
import downloadutils
|
||||
import plexdb_functions as plexdb
|
||||
import kodidb_functions as kodidb
|
||||
import json_rpc as js
|
||||
import variables as v
|
||||
import state
|
||||
|
||||
###############################################################################
|
||||
|
||||
log = logging.getLogger("PLEX."+__name__)
|
||||
LOG = logging.getLogger("PLEX." + __name__)
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
@ -29,7 +30,7 @@ class Player(xbmc.Player):
|
|||
def __init__(self):
|
||||
self.doUtils = downloadutils.DownloadUtils
|
||||
xbmc.Player.__init__(self)
|
||||
log.info("Started playback monitor.")
|
||||
LOG.info("Started playback monitor.")
|
||||
|
||||
def onPlayBackStarted(self):
|
||||
"""
|
||||
|
@ -56,7 +57,7 @@ class Player(xbmc.Player):
|
|||
else:
|
||||
count += 1
|
||||
if not currentFile:
|
||||
log.warn('Error getting currently playing file; abort reporting')
|
||||
LOG.warn('Error getting currently playing file; abort reporting')
|
||||
return
|
||||
|
||||
# Save currentFile for cleanup later and for references
|
||||
|
@ -69,11 +70,11 @@ class Player(xbmc.Player):
|
|||
xbmc.sleep(200)
|
||||
itemId = window("plex_%s.itemid" % tryEncode(currentFile))
|
||||
if count == 5:
|
||||
log.warn("Could not find itemId, cancelling playback report!")
|
||||
LOG.warn("Could not find itemId, cancelling playback report!")
|
||||
return
|
||||
count += 1
|
||||
|
||||
log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId))
|
||||
LOG.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId))
|
||||
|
||||
plexitem = "plex_%s" % tryEncode(currentFile)
|
||||
runtime = window("%s.runtime" % plexitem)
|
||||
|
@ -86,40 +87,26 @@ class Player(xbmc.Player):
|
|||
playcount = 0
|
||||
window('plex_skipWatched%s' % itemId, value="true")
|
||||
|
||||
log.debug("Playing itemtype is: %s" % itemType)
|
||||
LOG.debug("Playing itemtype is: %s" % itemType)
|
||||
|
||||
customseek = window('plex_customplaylist.seektime')
|
||||
if customseek:
|
||||
# Start at, when using custom playlist (play to Kodi from
|
||||
# webclient)
|
||||
log.info("Seeking to: %s" % customseek)
|
||||
LOG.info("Seeking to: %s" % customseek)
|
||||
try:
|
||||
self.seekTime(int(customseek))
|
||||
except:
|
||||
log.error('Could not seek!')
|
||||
LOG.error('Could not seek!')
|
||||
window('plex_customplaylist.seektime', clear=True)
|
||||
|
||||
try:
|
||||
seekTime = self.getTime()
|
||||
except RuntimeError:
|
||||
log.error('Could not get current seektime from xbmc player')
|
||||
LOG.error('Could not get current seektime from xbmc player')
|
||||
seekTime = 0
|
||||
|
||||
# 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 = result.get('result')
|
||||
|
||||
volume = result.get('volume')
|
||||
muted = result.get('muted')
|
||||
volume = js.get_volume()
|
||||
muted = js.get_muted()
|
||||
|
||||
# Postdata structure to send to plex server
|
||||
url = "{server}/:/timeline?"
|
||||
|
@ -144,35 +131,13 @@ class Player(xbmc.Player):
|
|||
% tryEncode(currentFile))
|
||||
else:
|
||||
# Get the current kodi audio and subtitles and convert to plex equivalent
|
||||
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)
|
||||
result = result.get('result')
|
||||
|
||||
try: # Audio tracks
|
||||
indexAudio = result['currentaudiostream']['index']
|
||||
except (KeyError, TypeError):
|
||||
indexAudio = 0
|
||||
|
||||
try: # Subtitles tracks
|
||||
indexSubs = result['currentsubtitle']['index']
|
||||
except (KeyError, TypeError):
|
||||
indexAudio = js.current_audiostream(1).get('index', 0)
|
||||
subsEnabled = js.subtitle_enabled(1)
|
||||
if subsEnabled:
|
||||
indexSubs = js.current_subtitle(1).get('index', 0)
|
||||
else:
|
||||
indexSubs = 0
|
||||
|
||||
try: # If subtitles are enabled
|
||||
subsEnabled = result['subtitleenabled']
|
||||
except (KeyError, TypeError):
|
||||
subsEnabled = ""
|
||||
|
||||
# Postdata for the audio
|
||||
postdata['AudioStreamIndex'] = indexAudio + 1
|
||||
|
||||
|
@ -185,7 +150,7 @@ class Player(xbmc.Player):
|
|||
|
||||
if mapping: # Set in playbackutils.py
|
||||
|
||||
log.debug("Mapping for external subtitles index: %s"
|
||||
LOG.debug("Mapping for external subtitles index: %s"
|
||||
% mapping)
|
||||
externalIndex = json.loads(mapping)
|
||||
|
||||
|
@ -213,9 +178,9 @@ class Player(xbmc.Player):
|
|||
except ValueError:
|
||||
try:
|
||||
runtime = self.getTotalTime()
|
||||
log.error("Runtime is missing, Kodi runtime: %s" % runtime)
|
||||
LOG.error("Runtime is missing, Kodi runtime: %s" % runtime)
|
||||
except:
|
||||
log.error('Could not get kodi runtime, setting to zero')
|
||||
LOG.error('Could not get kodi runtime, setting to zero')
|
||||
runtime = 0
|
||||
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
|
@ -223,7 +188,7 @@ class Player(xbmc.Player):
|
|||
try:
|
||||
fileid = plex_dbitem[1]
|
||||
except TypeError:
|
||||
log.info("Could not find fileid in plex db.")
|
||||
LOG.info("Could not find fileid in plex db.")
|
||||
fileid = None
|
||||
# Save data map for updates and position calls
|
||||
data = {
|
||||
|
@ -242,7 +207,7 @@ class Player(xbmc.Player):
|
|||
}
|
||||
|
||||
self.played_info[currentFile] = data
|
||||
log.info("ADDING_FILE: %s" % data)
|
||||
LOG.info("ADDING_FILE: %s" % data)
|
||||
|
||||
# log some playback stats
|
||||
'''if(itemType != None):
|
||||
|
@ -262,7 +227,7 @@ class Player(xbmc.Player):
|
|||
def onPlayBackPaused(self):
|
||||
|
||||
currentFile = self.currentFile
|
||||
log.info("PLAYBACK_PAUSED: %s" % currentFile)
|
||||
LOG.info("PLAYBACK_PAUSED: %s" % currentFile)
|
||||
|
||||
if self.played_info.get(currentFile):
|
||||
self.played_info[currentFile]['paused'] = True
|
||||
|
@ -270,7 +235,7 @@ class Player(xbmc.Player):
|
|||
def onPlayBackResumed(self):
|
||||
|
||||
currentFile = self.currentFile
|
||||
log.info("PLAYBACK_RESUMED: %s" % currentFile)
|
||||
LOG.info("PLAYBACK_RESUMED: %s" % currentFile)
|
||||
|
||||
if self.played_info.get(currentFile):
|
||||
self.played_info[currentFile]['paused'] = False
|
||||
|
@ -278,7 +243,7 @@ class Player(xbmc.Player):
|
|||
def onPlayBackSeek(self, time, seekOffset):
|
||||
# Make position when seeking a bit more accurate
|
||||
currentFile = self.currentFile
|
||||
log.info("PLAYBACK_SEEK: %s" % currentFile)
|
||||
LOG.info("PLAYBACK_SEEK: %s" % currentFile)
|
||||
|
||||
if self.played_info.get(currentFile):
|
||||
try:
|
||||
|
@ -290,7 +255,7 @@ class Player(xbmc.Player):
|
|||
|
||||
def onPlayBackStopped(self):
|
||||
# Will be called when user stops xbmc playing a file
|
||||
log.info("ONPLAYBACK_STOPPED")
|
||||
LOG.info("ONPLAYBACK_STOPPED")
|
||||
|
||||
self.stopAll()
|
||||
|
||||
|
@ -303,24 +268,24 @@ class Player(xbmc.Player):
|
|||
# We might have saved a transient token from a user flinging media via
|
||||
# Companion (if we could not use the playqueue to store the token)
|
||||
state.PLEX_TRANSIENT_TOKEN = None
|
||||
log.debug("Cleared playlist properties.")
|
||||
LOG.debug("Cleared playlist properties.")
|
||||
|
||||
def onPlayBackEnded(self):
|
||||
# Will be called when xbmc stops playing a file, because the file ended
|
||||
log.info("ONPLAYBACK_ENDED")
|
||||
LOG.info("ONPLAYBACK_ENDED")
|
||||
self.onPlayBackStopped()
|
||||
|
||||
def stopAll(self):
|
||||
if not self.played_info:
|
||||
return
|
||||
log.info("Played_information: %s" % self.played_info)
|
||||
LOG.info("Played_information: %s" % self.played_info)
|
||||
# Process each items
|
||||
for item in self.played_info:
|
||||
data = self.played_info.get(item)
|
||||
if not data:
|
||||
continue
|
||||
log.debug("Item path: %s" % item)
|
||||
log.debug("Item data: %s" % data)
|
||||
LOG.debug("Item path: %s" % item)
|
||||
LOG.debug("Item data: %s" % data)
|
||||
|
||||
runtime = data['runtime']
|
||||
currentPosition = data['currentPosition']
|
||||
|
@ -340,7 +305,7 @@ class Player(xbmc.Player):
|
|||
except ZeroDivisionError:
|
||||
# Runtime is 0.
|
||||
percentComplete = 0
|
||||
log.info("Percent complete: %s Mark played at: %s"
|
||||
LOG.info("Percent complete: %s Mark played at: %s"
|
||||
% (percentComplete, v.MARK_PLAYED_AT))
|
||||
if percentComplete >= v.MARK_PLAYED_AT:
|
||||
# Tell Kodi that we've finished watching (Plex knows)
|
||||
|
@ -374,7 +339,7 @@ class Player(xbmc.Player):
|
|||
|
||||
# Stop transcoding
|
||||
if playMethod == "Transcode":
|
||||
log.info("Transcoding for %s terminating" % itemid)
|
||||
LOG.info("Transcoding for %s terminating" % itemid)
|
||||
self.doUtils().downloadUrl(
|
||||
"{server}/video/:/transcode/universal/stop",
|
||||
parameters={'session': window('plex_client_Id')})
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
"""
|
||||
Collection of functions associated with Kodi and Plex playlists and playqueues
|
||||
"""
|
||||
import logging
|
||||
from urllib import quote
|
||||
from urlparse import parse_qsl, urlsplit
|
||||
|
@ -5,13 +8,14 @@ from re import compile as re_compile
|
|||
|
||||
import plexdb_functions as plexdb
|
||||
from downloadutils import DownloadUtils as DU
|
||||
from utils import JSONRPC, tryEncode, escape_html
|
||||
from utils import tryEncode, escape_html
|
||||
from PlexAPI import API
|
||||
from PlexFunctions import GetPlexMetadata
|
||||
import json_rpc as js
|
||||
|
||||
###############################################################################
|
||||
|
||||
log = logging.getLogger("PLEX."+__name__)
|
||||
LOG = logging.getLogger("PLEX." + __name__)
|
||||
|
||||
REGEX = re_compile(r'''metadata%2F(\d+)''')
|
||||
###############################################################################
|
||||
|
@ -29,7 +33,7 @@ class Playlist_Object_Baseclase(object):
|
|||
kodi_pl = None
|
||||
items = []
|
||||
old_kodi_pl = []
|
||||
ID = None
|
||||
id = None
|
||||
version = None
|
||||
selectedItemID = None
|
||||
selectedItemOffset = None
|
||||
|
@ -43,10 +47,10 @@ class Playlist_Object_Baseclase(object):
|
|||
"""
|
||||
answ = "<%s: " % (self.__class__.__name__)
|
||||
# For some reason, can't use dir directly
|
||||
answ += "ID: %s, " % self.ID
|
||||
answ += "id: %s, " % self.id
|
||||
answ += "items: %s, " % self.items
|
||||
for key in self.__dict__:
|
||||
if key not in ("ID", 'items'):
|
||||
if key not in ("id", 'items'):
|
||||
if type(getattr(self, key)) in (str, unicode):
|
||||
answ += '%s: %s, ' % (key, tryEncode(getattr(self, key)))
|
||||
else:
|
||||
|
@ -61,14 +65,14 @@ class Playlist_Object_Baseclase(object):
|
|||
self.kodi_pl.clear() # Clear Kodi playlist object
|
||||
self.items = []
|
||||
self.old_kodi_pl = []
|
||||
self.ID = None
|
||||
self.id = None
|
||||
self.version = None
|
||||
self.selectedItemID = None
|
||||
self.selectedItemOffset = None
|
||||
self.shuffled = 0
|
||||
self.repeat = 0
|
||||
self.plex_transient_token = None
|
||||
log.debug('Playlist cleared: %s' % self)
|
||||
LOG.debug('Playlist cleared: %s', self)
|
||||
|
||||
|
||||
class Playlist_Object(Playlist_Object_Baseclase):
|
||||
|
@ -82,12 +86,12 @@ class Playqueue_Object(Playlist_Object_Baseclase):
|
|||
"""
|
||||
PKC object to represent PMS playQueues and Kodi playlist for queueing
|
||||
|
||||
playlistid = None [int] Kodi playlist ID (0, 1, 2)
|
||||
playlistid = None [int] Kodi playlist id (0, 1, 2)
|
||||
type = None [str] Kodi type: 'audio', 'video', 'picture'
|
||||
kodi_pl = None Kodi xbmc.PlayList object
|
||||
items = [] [list] of Playlist_Items
|
||||
old_kodi_pl = [] [list] store old Kodi JSON result with all pl items
|
||||
ID = None [str] Plex playQueueID, unique Plex identifier
|
||||
id = None [str] Plex playQueueID, unique Plex identifier
|
||||
version = None [int] Plex version of the playQueue
|
||||
selectedItemID = None
|
||||
[str] Plex selectedItemID, playing element in queue
|
||||
|
@ -106,7 +110,7 @@ class Playlist_Item(object):
|
|||
"""
|
||||
Object to fill our playqueues and playlists with.
|
||||
|
||||
ID = None [str] Plex playlist/playqueue id, e.g. playQueueItemID
|
||||
id = None [str] Plex playlist/playqueue id, e.g. playQueueItemID
|
||||
plex_id = None [str] Plex unique item id, "ratingKey"
|
||||
plex_type = None [str] Plex type, e.g. 'movie', 'clip'
|
||||
plex_UUID = None [str] Plex librarySectionUUID
|
||||
|
@ -117,7 +121,7 @@ class Playlist_Item(object):
|
|||
guid = None [str] Weird Plex guid
|
||||
xml = None [etree] XML from PMS, 1 lvl below <MediaContainer>
|
||||
"""
|
||||
ID = None
|
||||
id = None
|
||||
plex_id = None
|
||||
plex_type = None
|
||||
plex_UUID = None
|
||||
|
@ -176,7 +180,7 @@ def playlist_item_from_kodi(kodi_item):
|
|||
# TO BE VERIFIED - PLEX DOESN'T LIKE PLAYLIST ADDS IN THIS MANNER
|
||||
item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' %
|
||||
(item.plex_UUID, item.plex_id))
|
||||
log.debug('Made playlist item from Kodi: %s' % item)
|
||||
LOG.debug('Made playlist item from Kodi: %s', item)
|
||||
return item
|
||||
|
||||
|
||||
|
@ -199,7 +203,7 @@ def playlist_item_from_plex(plex_id):
|
|||
item.plex_UUID = plex_id
|
||||
item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' %
|
||||
(item.plex_UUID, plex_id))
|
||||
log.debug('Made playlist item from plex: %s' % item)
|
||||
LOG.debug('Made playlist item from plex: %s', item)
|
||||
return item
|
||||
|
||||
|
||||
|
@ -213,7 +217,7 @@ def playlist_item_from_xml(playlist, xml_video_element):
|
|||
api = API(xml_video_element)
|
||||
item.plex_id = api.getRatingKey()
|
||||
item.plex_type = api.getType()
|
||||
item.ID = xml_video_element.attrib['%sItemID' % playlist.kind]
|
||||
item.id = xml_video_element.attrib['%sItemID' % playlist.kind]
|
||||
item.guid = xml_video_element.attrib.get('guid')
|
||||
if item.guid is not None:
|
||||
item.guid = escape_html(item.guid)
|
||||
|
@ -225,7 +229,7 @@ def playlist_item_from_xml(playlist, xml_video_element):
|
|||
except TypeError:
|
||||
pass
|
||||
item.xml = xml_video_element
|
||||
log.debug('Created new playlist item from xml: %s' % item)
|
||||
LOG.debug('Created new playlist item from xml: %s', item)
|
||||
return item
|
||||
|
||||
|
||||
|
@ -237,8 +241,8 @@ def _get_playListVersion_from_xml(playlist, xml):
|
|||
try:
|
||||
playlist.version = int(xml.attrib['%sVersion' % playlist.kind])
|
||||
except (TypeError, AttributeError, KeyError):
|
||||
log.error('Could not get new playlist Version for playlist %s'
|
||||
% playlist)
|
||||
LOG.error('Could not get new playlist Version for playlist %s',
|
||||
playlist)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -249,7 +253,7 @@ def get_playlist_details_from_xml(playlist, xml):
|
|||
playlist.ID with the XML's playQueueID
|
||||
"""
|
||||
try:
|
||||
playlist.ID = xml.attrib['%sID' % playlist.kind]
|
||||
playlist.id = xml.attrib['%sID' % playlist.kind]
|
||||
playlist.version = xml.attrib['%sVersion' % playlist.kind]
|
||||
playlist.shuffled = xml.attrib['%sShuffled' % playlist.kind]
|
||||
playlist.selectedItemID = xml.attrib.get(
|
||||
|
@ -257,12 +261,12 @@ def get_playlist_details_from_xml(playlist, xml):
|
|||
playlist.selectedItemOffset = xml.attrib.get(
|
||||
'%sSelectedItemOffset' % playlist.kind)
|
||||
except:
|
||||
log.error('Could not parse xml answer from PMS for playlist %s'
|
||||
% playlist)
|
||||
LOG.error('Could not parse xml answer from PMS for playlist %s',
|
||||
playlist)
|
||||
import traceback
|
||||
log.error(traceback.format_exc())
|
||||
LOG.error(traceback.format_exc())
|
||||
raise KeyError
|
||||
log.debug('Updated playlist from xml: %s' % playlist)
|
||||
LOG.debug('Updated playlist from xml: %s', playlist)
|
||||
|
||||
|
||||
def update_playlist_from_PMS(playlist, playlist_id=None, xml=None):
|
||||
|
@ -280,7 +284,7 @@ def update_playlist_from_PMS(playlist, playlist_id=None, xml=None):
|
|||
try:
|
||||
get_playlist_details_from_xml(playlist, xml)
|
||||
except KeyError:
|
||||
log.error('Could not update playlist from PMS')
|
||||
LOG.error('Could not update playlist from PMS')
|
||||
return
|
||||
for plex_item in xml:
|
||||
playlist_item = add_to_Kodi_playlist(playlist, plex_item)
|
||||
|
@ -295,7 +299,7 @@ def init_Plex_playlist(playlist, plex_id=None, kodi_item=None):
|
|||
|
||||
Returns True if successful, False otherwise
|
||||
"""
|
||||
log.debug('Initializing the playlist %s on the Plex side' % playlist)
|
||||
LOG.debug('Initializing the playlist %s on the Plex side', playlist)
|
||||
try:
|
||||
if plex_id:
|
||||
item = playlist_item_from_plex(plex_id)
|
||||
|
@ -312,11 +316,11 @@ def init_Plex_playlist(playlist, plex_id=None, kodi_item=None):
|
|||
get_playlist_details_from_xml(playlist, xml)
|
||||
item.xml = xml[0]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
log.error('Could not init Plex playlist with plex_id %s and '
|
||||
LOG.error('Could not init Plex playlist with plex_id %s and '
|
||||
'kodi_item %s', plex_id, kodi_item)
|
||||
return False
|
||||
playlist.items.append(item)
|
||||
log.debug('Initialized the playlist on the Plex side: %s' % playlist)
|
||||
LOG.debug('Initialized the playlist on the Plex side: %s' % playlist)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -329,10 +333,10 @@ def add_listitem_to_playlist(playlist, pos, listitem, kodi_id=None,
|
|||
|
||||
file: str!!
|
||||
"""
|
||||
log.debug('add_listitem_to_playlist at position %s. Playlist before add: '
|
||||
'%s' % (pos, playlist))
|
||||
LOG.debug('add_listitem_to_playlist at position %s. Playlist before add: '
|
||||
'%s', pos, playlist)
|
||||
kodi_item = {'id': kodi_id, 'type': kodi_type, 'file': file}
|
||||
if playlist.ID is None:
|
||||
if playlist.id is None:
|
||||
init_Plex_playlist(playlist, plex_id, kodi_item)
|
||||
else:
|
||||
add_item_to_PMS_playlist(playlist, pos, plex_id, kodi_item)
|
||||
|
@ -358,9 +362,9 @@ def add_item_to_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
|
||||
file: str!
|
||||
"""
|
||||
log.debug('add_item_to_playlist. Playlist before adding: %s' % playlist)
|
||||
LOG.debug('add_item_to_playlist. Playlist before adding: %s', playlist)
|
||||
kodi_item = {'id': kodi_id, 'type': kodi_type, 'file': file}
|
||||
if playlist.ID is None:
|
||||
if playlist.id is None:
|
||||
success = init_Plex_playlist(playlist, plex_id, kodi_item)
|
||||
else:
|
||||
success = add_item_to_PMS_playlist(playlist, pos, plex_id, kodi_item)
|
||||
|
@ -376,9 +380,9 @@ def add_item_to_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
params['item'] = {'%sid' % item.kodi_type: int(item.kodi_id)}
|
||||
else:
|
||||
params['item'] = {'file': item.file}
|
||||
reply = JSONRPC('Playlist.Insert').execute(params)
|
||||
reply = js.playlist_insert(params)
|
||||
if reply.get('error') is not None:
|
||||
log.error('Could not add item to playlist. Kodi reply. %s', reply)
|
||||
LOG.error('Could not add item to playlist. Kodi reply. %s', reply)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -395,25 +399,24 @@ def add_item_to_PMS_playlist(playlist, pos, plex_id=None, kodi_item=None):
|
|||
try:
|
||||
item = playlist_item_from_plex(plex_id)
|
||||
except KeyError:
|
||||
log.error('Could not add new item to the PMS playlist')
|
||||
LOG.error('Could not add new item to the PMS playlist')
|
||||
return False
|
||||
else:
|
||||
item = playlist_item_from_kodi(kodi_item)
|
||||
url = "{server}/%ss/%s?uri=%s" % (playlist.kind, playlist.ID, item.uri)
|
||||
url = "{server}/%ss/%s?uri=%s" % (playlist.kind, playlist.id, item.uri)
|
||||
# Will always put the new item at the end of the Plex playlist
|
||||
xml = DU().downloadUrl(url, action_type="PUT")
|
||||
try:
|
||||
item.xml = xml[-1]
|
||||
item.ID = xml[-1].attrib['%sItemID' % playlist.kind]
|
||||
except IndexError:
|
||||
log.info('Could not get playlist children. Adding a dummy')
|
||||
LOG.info('Could not get playlist children. Adding a dummy')
|
||||
except (TypeError, AttributeError, KeyError):
|
||||
log.error('Could not add item %s to playlist %s'
|
||||
% (kodi_item, playlist))
|
||||
LOG.error('Could not add item %s to playlist %s', kodi_item, playlist)
|
||||
return False
|
||||
# Get the guid for this item
|
||||
for plex_item in xml:
|
||||
if plex_item.attrib['%sItemID' % playlist.kind] == item.ID:
|
||||
if plex_item.attrib['%sItemID' % playlist.kind] == item.id:
|
||||
item.guid = escape_html(plex_item.attrib['guid'])
|
||||
playlist.items.append(item)
|
||||
if pos == len(playlist.items) - 1:
|
||||
|
@ -424,7 +427,7 @@ def add_item_to_PMS_playlist(playlist, pos, plex_id=None, kodi_item=None):
|
|||
move_playlist_item(playlist,
|
||||
len(playlist.items) - 1,
|
||||
pos)
|
||||
log.debug('Successfully added item on the Plex side: %s' % playlist)
|
||||
LOG.debug('Successfully added item on the Plex side: %s', playlist)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -437,9 +440,9 @@ def add_item_to_kodi_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
|
||||
file: str!
|
||||
"""
|
||||
log.debug('Adding new item kodi_id: %s, kodi_type: %s, file: %s to Kodi '
|
||||
'only at position %s for %s'
|
||||
% (kodi_id, kodi_type, file, pos, playlist))
|
||||
LOG.debug('Adding new item kodi_id: %s, kodi_type: %s, file: %s to Kodi '
|
||||
'only at position %s for %s',
|
||||
kodi_id, kodi_type, file, pos, playlist)
|
||||
params = {
|
||||
'playlistid': playlist.playlistid,
|
||||
'position': pos
|
||||
|
@ -448,18 +451,18 @@ def add_item_to_kodi_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
params['item'] = {'%sid' % kodi_type: int(kodi_id)}
|
||||
else:
|
||||
params['item'] = {'file': file}
|
||||
reply = JSONRPC('Playlist.Insert').execute(params)
|
||||
reply = js.playlist_insert(params)
|
||||
if reply.get('error') is not None:
|
||||
log.error('Could not add item to playlist. Kodi reply. %s' % reply)
|
||||
LOG.error('Could not add item to playlist. Kodi reply. %s', reply)
|
||||
return False
|
||||
item = playlist_item_from_kodi(
|
||||
{'id': kodi_id, 'type': kodi_type, 'file': file}, playlist)
|
||||
{'id': kodi_id, 'type': kodi_type, 'file': file})
|
||||
if item.plex_id is not None:
|
||||
xml = GetPlexMetadata(item.plex_id)
|
||||
try:
|
||||
item.xml = xml[-1]
|
||||
except (TypeError, IndexError):
|
||||
log.error('Could not get metadata for playlist item %s', item)
|
||||
LOG.error('Could not get metadata for playlist item %s', item)
|
||||
playlist.items.insert(pos, item)
|
||||
return True
|
||||
|
||||
|
@ -470,26 +473,26 @@ def move_playlist_item(playlist, before_pos, after_pos):
|
|||
|
||||
WILL ALSO CHANGE OUR PLAYLISTS. Returns True if successful
|
||||
"""
|
||||
log.debug('Moving item from %s to %s on the Plex side for %s'
|
||||
% (before_pos, after_pos, playlist))
|
||||
LOG.debug('Moving item from %s to %s on the Plex side for %s',
|
||||
before_pos, after_pos, playlist)
|
||||
if after_pos == 0:
|
||||
url = "{server}/%ss/%s/items/%s/move?after=0" % \
|
||||
(playlist.kind,
|
||||
playlist.ID,
|
||||
playlist.items[before_pos].ID)
|
||||
playlist.id,
|
||||
playlist.items[before_pos].id)
|
||||
else:
|
||||
url = "{server}/%ss/%s/items/%s/move?after=%s" % \
|
||||
(playlist.kind,
|
||||
playlist.ID,
|
||||
playlist.items[before_pos].ID,
|
||||
playlist.items[after_pos - 1].ID)
|
||||
playlist.id,
|
||||
playlist.items[before_pos].id,
|
||||
playlist.items[after_pos - 1].id)
|
||||
# We need to increment the playlistVersion
|
||||
if _get_playListVersion_from_xml(
|
||||
playlist, DU().downloadUrl(url, action_type="PUT")) is False:
|
||||
return False
|
||||
# Move our item's position in our internal playlist
|
||||
playlist.items.insert(after_pos, playlist.items.pop(before_pos))
|
||||
log.debug('Done moving for %s' % playlist)
|
||||
LOG.debug('Done moving for %s' % playlist)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -500,7 +503,7 @@ def get_PMS_playlist(playlist, playlist_id=None):
|
|||
|
||||
Returns None if something went wrong
|
||||
"""
|
||||
playlist_id = playlist_id if playlist_id else playlist.ID
|
||||
playlist_id = playlist_id if playlist_id else playlist.id
|
||||
xml = DU().downloadUrl(
|
||||
"{server}/%ss/%s" % (playlist.kind, playlist_id),
|
||||
headerOptions={'Accept': 'application/xml'})
|
||||
|
@ -520,59 +523,24 @@ def refresh_playlist_from_PMS(playlist):
|
|||
try:
|
||||
get_playlist_details_from_xml(playlist, xml)
|
||||
except KeyError:
|
||||
log.error('Could not refresh playlist from PMS')
|
||||
LOG.error('Could not refresh playlist from PMS')
|
||||
|
||||
|
||||
def delete_playlist_item_from_PMS(playlist, pos):
|
||||
"""
|
||||
Delete the item at position pos [int] on the Plex side and our playlists
|
||||
"""
|
||||
log.debug('Deleting position %s for %s on the Plex side' % (pos, playlist))
|
||||
LOG.debug('Deleting position %s for %s on the Plex side', pos, playlist)
|
||||
xml = DU().downloadUrl("{server}/%ss/%s/items/%s?repeat=%s" %
|
||||
(playlist.kind,
|
||||
playlist.ID,
|
||||
playlist.items[pos].ID,
|
||||
playlist.id,
|
||||
playlist.items[pos].id,
|
||||
playlist.repeat),
|
||||
action_type="DELETE")
|
||||
_get_playListVersion_from_xml(playlist, xml)
|
||||
del playlist.items[pos]
|
||||
|
||||
|
||||
def get_kodi_playlist_items(playlist):
|
||||
"""
|
||||
Returns a list of the current Kodi playlist items using JSON
|
||||
|
||||
E.g.:
|
||||
[{u'title': u'3 Idiots', u'type': u'movie', u'id': 3, u'file':
|
||||
u'smb://nas/PlexMovies/3 Idiots 2009 pt1.mkv', u'label': u'3 Idiots'}]
|
||||
"""
|
||||
answ = JSONRPC('Playlist.GetItems').execute({
|
||||
'playlistid': playlist.playlistid,
|
||||
'properties': ["title", "file"]
|
||||
})
|
||||
try:
|
||||
answ = answ['result']['items']
|
||||
except KeyError:
|
||||
answ = []
|
||||
return answ
|
||||
|
||||
|
||||
def get_kodi_playqueues():
|
||||
"""
|
||||
Example return: [{u'playlistid': 0, u'type': u'audio'},
|
||||
{u'playlistid': 1, u'type': u'video'},
|
||||
{u'playlistid': 2, u'type': u'picture'}]
|
||||
"""
|
||||
queues = JSONRPC('Playlist.GetPlaylists').execute()
|
||||
try:
|
||||
queues = queues['result']
|
||||
except KeyError:
|
||||
log.error('Could not get Kodi playqueues. JSON Result was: %s'
|
||||
% queues)
|
||||
queues = []
|
||||
return queues
|
||||
|
||||
|
||||
# Functions operating on the Kodi playlist objects ##########
|
||||
|
||||
def add_to_Kodi_playlist(playlist, xml_video_element):
|
||||
|
@ -583,17 +551,14 @@ def add_to_Kodi_playlist(playlist, xml_video_element):
|
|||
Returns a Playlist_Item or None if it did not work
|
||||
"""
|
||||
item = playlist_item_from_xml(playlist, xml_video_element)
|
||||
params = {
|
||||
'playlistid': playlist.playlistid
|
||||
}
|
||||
if item.kodi_id:
|
||||
params['item'] = {'%sid' % item.kodi_type: item.kodi_id}
|
||||
json_item = {'%sid' % item.kodi_type: item.kodi_id}
|
||||
else:
|
||||
params['item'] = {'file': item.file}
|
||||
reply = JSONRPC('Playlist.Add').execute(params)
|
||||
json_item = {'file': item.file}
|
||||
reply = js.playlist_add(playlist.playlistid, json_item)
|
||||
if reply.get('error') is not None:
|
||||
log.error('Could not add item %s to Kodi playlist. Error: %s'
|
||||
% (xml_video_element, reply))
|
||||
LOG.error('Could not add item %s to Kodi playlist. Error: %s',
|
||||
xml_video_element, reply)
|
||||
return None
|
||||
return item
|
||||
|
||||
|
@ -607,8 +572,8 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file,
|
|||
|
||||
file: string!
|
||||
"""
|
||||
log.debug('Insert listitem at position %s for Kodi only for %s'
|
||||
% (pos, playlist))
|
||||
LOG.debug('Insert listitem at position %s for Kodi only for %s',
|
||||
pos, playlist)
|
||||
# Add the item into Kodi playlist
|
||||
playlist.kodi_pl.add(file, listitem, index=pos)
|
||||
# We need to add this to our internal queue as well
|
||||
|
@ -619,29 +584,25 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file,
|
|||
if file is not None:
|
||||
item.file = file
|
||||
playlist.items.insert(pos, item)
|
||||
log.debug('Done inserting for %s' % playlist)
|
||||
LOG.debug('Done inserting for %s', playlist)
|
||||
|
||||
|
||||
def remove_from_Kodi_playlist(playlist, pos):
|
||||
def remove_from_kodi_playlist(playlist, pos):
|
||||
"""
|
||||
Removes the item at position pos from the Kodi playlist using JSON.
|
||||
|
||||
WILL NOT UPDATE THE PLEX SIDE, BUT WILL UPDATE OUR PLAYLISTS
|
||||
"""
|
||||
log.debug('Removing position %s from Kodi only from %s' % (pos, playlist))
|
||||
reply = JSONRPC('Playlist.Remove').execute({
|
||||
'playlistid': playlist.playlistid,
|
||||
'position': pos
|
||||
})
|
||||
LOG.debug('Removing position %s from Kodi only from %s', pos, playlist)
|
||||
reply = js.playlist_remove(playlist.playlistid, pos)
|
||||
if reply.get('error') is not None:
|
||||
log.error('Could not delete the item from the playlist. Error: %s'
|
||||
% reply)
|
||||
LOG.error('Could not delete the item from the playlist. Error: %s',
|
||||
reply)
|
||||
return
|
||||
else:
|
||||
try:
|
||||
del playlist.items[pos]
|
||||
except IndexError:
|
||||
log.error('Cannot delete position %s for %s' % (pos, playlist))
|
||||
LOG.error('Cannot delete position %s for %s', pos, playlist)
|
||||
|
||||
|
||||
def get_pms_playqueue(playqueue_id):
|
||||
|
@ -654,7 +615,7 @@ def get_pms_playqueue(playqueue_id):
|
|||
try:
|
||||
xml.attrib
|
||||
except AttributeError:
|
||||
log.error('Could not download Plex playqueue %s' % playqueue_id)
|
||||
LOG.error('Could not download Plex playqueue %s', playqueue_id)
|
||||
xml = None
|
||||
return xml
|
||||
|
||||
|
@ -669,12 +630,12 @@ def get_plextype_from_xml(xml):
|
|||
try:
|
||||
plex_id = REGEX.findall(xml.attrib['playQueueSourceURI'])[0]
|
||||
except IndexError:
|
||||
log.error('Could not get plex_id from xml: %s' % xml.attrib)
|
||||
LOG.error('Could not get plex_id from xml: %s', xml.attrib)
|
||||
return
|
||||
new_xml = GetPlexMetadata(plex_id)
|
||||
try:
|
||||
new_xml[0].attrib
|
||||
except (TypeError, IndexError, AttributeError):
|
||||
log.error('Could not get plex metadata for plex id %s' % plex_id)
|
||||
LOG.error('Could not get plex metadata for plex id %s', plex_id)
|
||||
return
|
||||
return new_xml[0].attrib.get('type')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
"""
|
||||
Monitors the Kodi playqueue and adjusts the Plex playqueue accordingly
|
||||
"""
|
||||
import logging
|
||||
from threading import RLock, Thread
|
||||
|
||||
|
@ -10,13 +11,14 @@ import playlist_func as PL
|
|||
from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren
|
||||
from PlexAPI import API
|
||||
from playbackutils import PlaybackUtils
|
||||
import json_rpc as js
|
||||
import variables as v
|
||||
|
||||
###############################################################################
|
||||
log = logging.getLogger("PLEX."+__name__)
|
||||
LOG = logging.getLogger("PLEX." + __name__)
|
||||
|
||||
# Lock used for playqueue manipulations
|
||||
lock = RLock()
|
||||
# lock used for playqueue manipulations
|
||||
LOCK = RLock()
|
||||
PLUGIN = 'plugin://%s' % v.ADDON_ID
|
||||
###############################################################################
|
||||
|
||||
|
@ -33,15 +35,15 @@ class Playqueue(Thread):
|
|||
def __init__(self, callback=None):
|
||||
self.__dict__ = self.__shared_state
|
||||
if self.playqueues is not None:
|
||||
log.debug('Playqueue thread has already been initialized')
|
||||
LOG.debug('Playqueue thread has already been initialized')
|
||||
Thread.__init__(self)
|
||||
return
|
||||
self.mgr = callback
|
||||
|
||||
# Initialize Kodi playqueues
|
||||
with lock:
|
||||
with LOCK:
|
||||
self.playqueues = []
|
||||
for queue in PL.get_kodi_playqueues():
|
||||
for queue in js.get_playlists():
|
||||
playqueue = PL.Playqueue_Object()
|
||||
playqueue.playlistid = queue['playlistid']
|
||||
playqueue.type = queue['type']
|
||||
|
@ -59,7 +61,7 @@ class Playqueue(Thread):
|
|||
# sort the list by their playlistid, just in case
|
||||
self.playqueues = sorted(
|
||||
self.playqueues, key=lambda i: i.playlistid)
|
||||
log.debug('Initialized the Kodi play queues: %s' % self.playqueues)
|
||||
LOG.debug('Initialized the Kodi play queues: %s' % self.playqueues)
|
||||
Thread.__init__(self)
|
||||
|
||||
def get_playqueue_from_type(self, typus):
|
||||
|
@ -67,7 +69,7 @@ class Playqueue(Thread):
|
|||
Returns the playqueue according to the typus ('video', 'audio',
|
||||
'picture') passed in
|
||||
"""
|
||||
with lock:
|
||||
with LOCK:
|
||||
for playqueue in self.playqueues:
|
||||
if playqueue.type == typus:
|
||||
break
|
||||
|
@ -85,7 +87,7 @@ class Playqueue(Thread):
|
|||
try:
|
||||
xml[0].attrib
|
||||
except (TypeError, IndexError, AttributeError):
|
||||
log.error('Could not download the PMS xml for %s' % plex_id)
|
||||
LOG.error('Could not download the PMS xml for %s' % plex_id)
|
||||
return
|
||||
playqueue = self.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']])
|
||||
|
@ -93,7 +95,7 @@ class Playqueue(Thread):
|
|||
for i, child in enumerate(xml):
|
||||
api = API(child)
|
||||
PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey())
|
||||
log.debug('Firing up Kodi player')
|
||||
LOG.debug('Firing up Kodi player')
|
||||
Player().play(playqueue.kodi_pl, None, False, 0)
|
||||
return playqueue
|
||||
|
||||
|
@ -109,15 +111,15 @@ class Playqueue(Thread):
|
|||
repeat = 0, 1, 2
|
||||
offset = time offset in Plextime (milliseconds)
|
||||
"""
|
||||
log.info('New playqueue %s received from Plex companion with offset '
|
||||
LOG.info('New playqueue %s received from Plex companion with offset '
|
||||
'%s, repeat %s' % (playqueue_id, offset, repeat))
|
||||
with lock:
|
||||
with LOCK:
|
||||
xml = PL.get_PMS_playlist(playqueue, playqueue_id)
|
||||
playqueue.clear()
|
||||
try:
|
||||
PL.get_playlist_details_from_xml(playqueue, xml)
|
||||
except KeyError:
|
||||
log.error('Could not get playqueue ID %s' % playqueue_id)
|
||||
LOG.error('Could not get playqueue ID %s' % playqueue_id)
|
||||
return
|
||||
PlaybackUtils(xml, playqueue).play_all()
|
||||
playqueue.repeat = 0 if not repeat else int(repeat)
|
||||
|
@ -126,12 +128,12 @@ class Playqueue(Thread):
|
|||
window('plex_customplaylist.seektime',
|
||||
str(ConvertPlexToKodiTime(offset)))
|
||||
for startpos, item in enumerate(playqueue.items):
|
||||
if item.ID == playqueue.selectedItemID:
|
||||
if item.id == playqueue.selectedItemID:
|
||||
break
|
||||
else:
|
||||
startpos = 0
|
||||
# Start playback. Player does not return in time
|
||||
log.debug('Playqueues after Plex Companion update are now: %s'
|
||||
LOG.debug('Playqueues after Plex Companion update are now: %s'
|
||||
% self.playqueues)
|
||||
thread = Thread(target=Player().play,
|
||||
args=(playqueue.kodi_pl,
|
||||
|
@ -147,7 +149,7 @@ class Playqueue(Thread):
|
|||
"""
|
||||
old = list(playqueue.items)
|
||||
index = list(range(0, len(old)))
|
||||
log.debug('Comparing new Kodi playqueue %s with our play queue %s'
|
||||
LOG.debug('Comparing new Kodi playqueue %s with our play queue %s'
|
||||
% (new, old))
|
||||
if self.thread_stopped():
|
||||
# Chances are that we got an empty Kodi playlist due to
|
||||
|
@ -176,15 +178,15 @@ class Playqueue(Thread):
|
|||
del old[j], index[j]
|
||||
break
|
||||
elif identical:
|
||||
log.debug('Detected playqueue item %s moved to position %s'
|
||||
LOG.debug('Detected playqueue item %s moved to position %s'
|
||||
% (i+j, i))
|
||||
PL.move_playlist_item(playqueue, i + j, i)
|
||||
del old[j], index[j]
|
||||
break
|
||||
else:
|
||||
log.debug('Detected new Kodi element at position %s: %s '
|
||||
LOG.debug('Detected new Kodi element at position %s: %s '
|
||||
% (i, new_item))
|
||||
if playqueue.ID is None:
|
||||
if playqueue.id is None:
|
||||
PL.init_Plex_playlist(playqueue,
|
||||
kodi_item=new_item)
|
||||
else:
|
||||
|
@ -194,17 +196,18 @@ class Playqueue(Thread):
|
|||
for j in range(i, len(index)):
|
||||
index[j] += 1
|
||||
for i in reversed(index):
|
||||
log.debug('Detected deletion of playqueue element at pos %s' % i)
|
||||
LOG.debug('Detected deletion of playqueue element at pos %s' % i)
|
||||
PL.delete_playlist_item_from_PMS(playqueue, i)
|
||||
log.debug('Done comparing playqueues')
|
||||
LOG.debug('Done comparing playqueues')
|
||||
|
||||
def run(self):
|
||||
thread_stopped = self.thread_stopped
|
||||
thread_suspended = self.thread_suspended
|
||||
log.info("----===## Starting PlayQueue client ##===----")
|
||||
LOG.info("----===## Starting PlayQueue client ##===----")
|
||||
# Initialize the playqueues, if Kodi already got items in them
|
||||
for playqueue in self.playqueues:
|
||||
for i, item in enumerate(PL.get_kodi_playlist_items(playqueue)):
|
||||
for i, item in enumerate(js.playlist_get_items(
|
||||
playqueue.id, ["title", "file"])):
|
||||
if i == 0:
|
||||
PL.init_Plex_playlist(playqueue, kodi_item=item)
|
||||
else:
|
||||
|
@ -214,9 +217,10 @@ class Playqueue(Thread):
|
|||
if thread_stopped():
|
||||
break
|
||||
sleep(1000)
|
||||
with lock:
|
||||
with LOCK:
|
||||
for playqueue in self.playqueues:
|
||||
kodi_playqueue = PL.get_kodi_playlist_items(playqueue)
|
||||
kodi_playqueue = js.playlist_get_items(playqueue.id,
|
||||
["title", "file"])
|
||||
if playqueue.old_kodi_pl != kodi_playqueue:
|
||||
# compare old and new playqueue
|
||||
self._compare_playqueues(playqueue, kodi_playqueue)
|
||||
|
@ -226,4 +230,4 @@ class Playqueue(Thread):
|
|||
sleep(10)
|
||||
continue
|
||||
sleep(200)
|
||||
log.info("----===## PlayQueue client stopped ##===----")
|
||||
LOG.info("----===## PlayQueue client stopped ##===----")
|
||||
|
|
|
@ -333,14 +333,14 @@ def create_actor_db_index():
|
|||
def getScreensaver():
|
||||
# Get the current screensaver value
|
||||
params = {'setting': "screensaver.mode"}
|
||||
return JSONRPC('Settings.getSettingValue').execute(params)['result']['value']
|
||||
return jsonrpc('Settings.getSettingValue').execute(params)['result']['value']
|
||||
|
||||
|
||||
def setScreensaver(value):
|
||||
# Toggle the screensaver
|
||||
params = {'setting': "screensaver.mode", 'value': value}
|
||||
log.debug('Toggling screensaver to "%s": %s'
|
||||
% (value, JSONRPC('Settings.setSettingValue').execute(params)))
|
||||
% (value, jsonrpc('Settings.setSettingValue').execute(params)))
|
||||
|
||||
|
||||
def reset():
|
||||
|
@ -1141,7 +1141,7 @@ def changePlayState(itemType, kodiId, playCount, lastplayed):
|
|||
log.debug("JSON result was: %s" % result)
|
||||
|
||||
|
||||
class JSONRPC(object):
|
||||
class jsonrpc(object):
|
||||
id_ = 1
|
||||
jsonrpc = "2.0"
|
||||
|
||||
|
|
Loading…
Reference in a new issue