217 lines
7.4 KiB
Python
Executable File
217 lines
7.4 KiB
Python
Executable File
"""
|
|
Applet: Plex Tracks
|
|
Summary: Display info on currently playing music from Plex server
|
|
Description: Configure with IP/Domain of a plex server, the API key, and it'll show the currently playing track and artwork
|
|
Author: poprhythm
|
|
"""
|
|
|
|
load("render.star", "render")
|
|
load("schema.star", "schema")
|
|
load("encoding/base64.star", "base64")
|
|
load("http.star", "http")
|
|
load("xpath.star", "xpath")
|
|
load("random.star", "random")
|
|
load("cache.star", "cache")
|
|
|
|
DEFAULT_ADDR = "timetogetill.asuscomm.com"
|
|
DEFAULT_PORT = "32400"
|
|
DEFAULT_PLEXTOKEN = "1i5siHsw4toihxixjRKh"
|
|
DEFAULT_PLAYER = ""
|
|
CACHE_TTL = 45
|
|
|
|
def main(config):
|
|
addr = config.get("addr", DEFAULT_ADDR)
|
|
port = config.get("port", DEFAULT_PORT)
|
|
token = config.get("token", DEFAULT_PLEXTOKEN)
|
|
player = config.get("player", None)
|
|
|
|
trackInfo = update_plex_status(addr, port, token, player)
|
|
|
|
if trackInfo == None:
|
|
quote = SILENCE_QUOTES[random.number(0,len(SILENCE_QUOTES))]
|
|
return render.Root(
|
|
render.Column([
|
|
render.Image(src=PLEX_LOGO_W48),
|
|
render.Marquee(render.Text(quote, font="tom-thumb"), width=64)
|
|
],
|
|
expanded=True,
|
|
main_align="center",
|
|
cross_align="center"
|
|
)
|
|
)
|
|
|
|
if "status_code" in trackInfo:
|
|
return render.Root (
|
|
render.Column ([
|
|
render.Text("Plex request failed:"),
|
|
render.Marquee(
|
|
render.Text(trackInfo["status_code"]),
|
|
width = 64,
|
|
)]
|
|
)
|
|
)
|
|
|
|
return render.Root(
|
|
child = render.Box(
|
|
render.Row([
|
|
render.Image(src=trackInfo["thumbnail"], width = 32, height = 32),
|
|
render.Column([
|
|
render.Image(src=PLEX_LOGO_H8),
|
|
render.Marquee(
|
|
render.Text(trackInfo["title"]),
|
|
width = 32,
|
|
),
|
|
render.Marquee(
|
|
render.Text(trackInfo["artist"]),
|
|
width = 32,
|
|
),
|
|
render.Marquee(
|
|
render.Text(trackInfo["album"]),
|
|
width = 32,
|
|
)
|
|
]
|
|
)
|
|
],
|
|
),
|
|
),
|
|
)
|
|
|
|
def update_plex_status(addr, port, token, player):
|
|
|
|
if cache.get("title") != None:
|
|
return {
|
|
"title": cache.get("title"),
|
|
"artist": cache.get("artist"),
|
|
"album": cache.get("album"),
|
|
"thumbnail": base64.decode(cache.get("thumbnail"))
|
|
}
|
|
|
|
status_url = "http://%s:%s/status/sessions?X-Plex-Token=%s" % (addr,port,token)
|
|
|
|
rep = http.get(status_url)
|
|
|
|
if rep.status_code != 200:
|
|
print("Plex request failed with status %d", rep.status_code)
|
|
return {"status_code": rep.status_code}
|
|
|
|
xml = rep.body()
|
|
xp = xpath.loads(xml)
|
|
|
|
query = "//Track"
|
|
if not(player == None or not player):
|
|
query += "[contains(Player/@title,'%s')]" % player
|
|
|
|
t = xp.query_node(query)
|
|
|
|
if t == None:
|
|
return None
|
|
|
|
title = t.query("@title")
|
|
|
|
artist = t.query("@originalTitle")
|
|
if artist == None:
|
|
artist = t.query("@grandparentTitle")
|
|
album = t.query("@parentTitle")
|
|
else:
|
|
album = t.query("@parentTitle")
|
|
|
|
#print("%s - %s" % (artist, title))
|
|
|
|
thumbnail_path = t.query("@thumb")
|
|
thumbnail_url = "http://%s:%s%s?X-Plex-Token=%s" % (addr,port,thumbnail_path,token)
|
|
thumbnail_rep = http.get(thumbnail_url)
|
|
|
|
trackInfo = {
|
|
"title": title,
|
|
"artist": artist,
|
|
"album": album,
|
|
"thumbnail": thumbnail_rep.body()
|
|
}
|
|
|
|
cache.set("title", trackInfo["title"], CACHE_TTL)
|
|
cache.set("artist", trackInfo["artist"], CACHE_TTL)
|
|
cache.set("album", trackInfo["album"], CACHE_TTL)
|
|
cache.set("thumbnail", base64.encode(trackInfo["thumbnail"]), CACHE_TTL)
|
|
|
|
return trackInfo
|
|
|
|
def get_schema():
|
|
return schema.Schema(
|
|
version = "1",
|
|
fields = [
|
|
schema.Text(
|
|
id = "addr",
|
|
name = "IP/Domain",
|
|
desc = "IP or Domain of Plex Server",
|
|
icon = "location-dot",
|
|
default = DEFAULT_ADDR,
|
|
),
|
|
schema.Text(
|
|
id = "port",
|
|
name = "Port Number",
|
|
desc = "Port on which Plex Server is hosted",
|
|
icon = "location-dot",
|
|
default = DEFAULT_PORT,
|
|
),
|
|
schema.Text(
|
|
id = "token",
|
|
name = "X-Plex-Token",
|
|
desc = "Token for Plex API commands",
|
|
icon = "hashtag",
|
|
default = DEFAULT_PLEXTOKEN,
|
|
),
|
|
schema.Text(
|
|
id = "player",
|
|
name = "Player Name to Display",
|
|
desc = "Leave blank for first match",
|
|
icon = "mp3-player",
|
|
default = DEFAULT_PLAYER,
|
|
)
|
|
],
|
|
)
|
|
|
|
PLEX_LOGO_H8 = base64.decode("""
|
|
iVBORw0KGgoAAAANSUhEUgAAACAAAAAICAYAAACYhf2vAAAAeElEQVQ4T2N
|
|
kIAH8BwKQckYgIEEbXqUoBiFbAGMjW4jNAcjqUNQe4oM41u4TTjvA8sjOI2
|
|
QpugMI8tEcgc0DeF0HcxxIIyjYcVmIHsbIUfQf6ghsoUH1EEB3CMxyWDQQH
|
|
QJ4fYSWEGmSBqiZygnlFow0QE/LQY4DALpxgAnhQU15AAAAAElFTkSuQmCC
|
|
""")
|
|
|
|
PLEX_LOGO_W48 = base64.decode("""
|
|
iVBORw0KGgoAAAANSUhEUgAAADAAAAAXCAYAAABNq8wJAAAACXBIWXMAAACIAAAA
|
|
iAHr3JJSAAADq0lEQVRYhd1XX0iTURS/i0XF0i0MFEYoKkj2Insq0LGnvURgsPDF
|
|
B5FeehjYXgrsUWtCmriH+aBiKFFusLk39cU2JoG5fDKFMRxM3Jhkm20LFn3xG99d
|
|
d9frPqdC2oHLvnPPuefPPeeec0YkSTrxGhsb65MYWFtb2z+NvJOsS+SCw//tgMPh
|
|
6BsZGek6K2WQBZlnJQ+gDoVC+xSJxWLb0Wj0rdlsftnS0nLNarVS0vtAIBBfXV3t
|
|
s9lsHypRAIPb2tqedXR01NlsNrr9ZmtrKxcMBt/19vY+3vOqGggh09zR9ZudUtHZ
|
|
wcHBeqPR+Emj0Vyle5lM5idhH2EqlfotKcDk5OQEfWxKj1jmLQt+v393YGCgPukh
|
|
3qSHSNzqpLLm5+e/8HJmZmY8REmBCIaHh7uUHDiO8RTgRNJDdEkP+c45AFzH62F1
|
|
Cd9AOp2WfD7fusvl+hgKhb7zdJPJNF4ubRBui8XSy+4hBR0Ox1MsyGZpSC/ft8nX
|
|
hJAeTpQWqZVOpz2wibVvaWnpQQHhPdvc3MwipEqpYLfb24+KAELL3zBf43mZ0Iv9
|
|
o1LJ6XS+oryy/IIcNX/zbrf7dn9/f5Tdx0NbXFw0mc3mpuLVaLX38/l8QhSB1tZW
|
|
E4vncrmMy+VaZveqqqoK+qqrq1XAUTRkEqKwLd8+hWlL3ZOGQOBODx5xd3f3Q0oo
|
|
cSAcDqd44ylEIhEXIeQ5xWtqau7F43GviLe2tvY6i8uON4l4WUDFslql0T2vCk54
|
|
GFIhlfx+/10gBoOhSDhXjSyfz+8q8ajV6lslOIs0Nzdr8QBFUWhsbHzE4gcHB2Ge
|
|
R6PRXMFvIpH4odfrdXR/fHzcflS6sYAes+dV6QQ9IYXUMhqNX+U+cIMSSiKAfESz
|
|
gBPs/tTU1ASb/4BkMskrKeTx0NBQ+8bGRkm+I92sVusou/iz2Wz2s/w5zeU/oMcd
|
|
dxaaocFg0M3Ozv5NL1FdjsVieTSOubm5ZbmyCGuwqD7jDKoY3xRRicCPaiKSCRqq
|
|
jaACeXl5+Ka96FSNTP4tAQiHwkoa2cLCQrjSRoZLhp5DDpQbJ0BjRwks0W1SnuM4
|
|
AePl+r9cbpRAZPmziOohB3CrIqOgCM1L9KcDhlLH5ZmlSMMZnOUvBjrk5gTj22QH
|
|
2DXK62HnITQ+2CpKoeIBhI6mi9Liu7dogec4fJXoUhUslmFnZ+eXXq+/rFTuzhOU
|
|
lNGVlZXgRTKesA5gDopEIi/+rTmVgxrjLT4wsh41B51bIIT8AdWtg7+vr6x4AAAA
|
|
AElFTkSuQmCC
|
|
""")
|
|
|
|
SILENCE_QUOTES = [
|
|
"Listen to silence. It has so much to say. Rumi",
|
|
"Silence is the sleep that nourishes wisdom. Francis Bacon",
|
|
"Noise creates illusions. Silence brings truth. Maxime Lagacé",
|
|
"Speech is silver, silence is golden. Thomas Carlyle",
|
|
"Silence is a source of great strength. Lao Tzu",
|
|
"Silence is sometimes the best answer. Dalai Lama",
|
|
"Silence is a true friend who never betrays. Confucius",
|
|
"The truth hurts, but silence kills. Mark Twain",
|
|
"To hear, one must be silent. Ursula K. Le Guin",
|
|
"Sound is our mind, silence is our being. Osho"
|
|
]
|