aly revised this gist . Go to revision
2 files changed, 142 insertions
nowplaying.py(file created)
| @@ -0,0 +1,39 @@ | |||
| 1 | + | #!/usr/bin/env python3 | |
| 2 | + | from plexapi.myplex import MyPlexAccount | |
| 3 | + | from plexapi.server import PlexServer | |
| 4 | + | from dotenv import load_dotenv | |
| 5 | + | import xmltodict as xtd | |
| 6 | + | load_dotenv() | |
| 7 | + | from os import environ | |
| 8 | + | from xml.etree import ElementTree | |
| 9 | + | from json import dumps, dump | |
| 10 | + | from time import sleep | |
| 11 | + | out = "" | |
| 12 | + | account = MyPlexAccount(environ["PLEX_USER"], environ["PLEX_PASSWORD"]) | |
| 13 | + | token = account.resource(environ["PLEX_SERVER_NAME"]).accessToken | |
| 14 | + | laptop = PlexServer(environ["PLEX_SERVER_ADDR"], token) | |
| 15 | + | prev = {"@guid": None} | |
| 16 | + | recent = [] | |
| 17 | + | while True: | |
| 18 | + | try: | |
| 19 | + | p = [p for p in laptop.sessions() if p.player.title == "l"][0] | |
| 20 | + | xml = xtd.parse(ElementTree.tostring(p._data,encoding="utf-8"))["Track"] | |
| 21 | + | if xml["@guid"] == prev["@guid"]: | |
| 22 | + | sleep(15) | |
| 23 | + | continue | |
| 24 | + | recent.append({"url": xml["Media"]["Part"]["@key"], "guid": xml["@guid"], "artist": xml["@grandparentTitle"], "title": xml["@title"]}) | |
| 25 | + | if len(recent) > 5: | |
| 26 | + | recent = recent[1:] | |
| 27 | + | with open("/usr/share/icecast2/web/recent.json", "w") as f: | |
| 28 | + | dump(recent,f) | |
| 29 | + | prev = xml | |
| 30 | + | del xml["Player"] | |
| 31 | + | del xml["User"] | |
| 32 | + | out = xml | |
| 33 | + | except Exception as e: | |
| 34 | + | out = {"error": str(e)} | |
| 35 | + | out = dumps(out,indent=4) | |
| 36 | + | print(out) | |
| 37 | + | with open("/usr/share/icecast2/web/np.json", "w") as f: | |
| 38 | + | f.write(out) | |
| 39 | + | sleep(15) | |
status.xsl(file created)
| @@ -0,0 +1,103 @@ | |||
| 1 | + | <!-- /usr/share/icecast2/web/status.xsl --> | |
| 2 | + | <xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0"> | |
| 3 | + | <xsl:output omit-xml-declaration="yes" method="xml" indent="yes" encoding="UTF-8" /> | |
| 4 | + | <xsl:template match = "/icestats"> | |
| 5 | + | <html xmlns="http://www.w3.org/1999/xhtml"> | |
| 6 | + | <head> | |
| 7 | + | <title>radio.aly.pet</title> | |
| 8 | + | <link rel="stylesheet" type="text/css" href="style.css" /> | |
| 9 | + | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> | |
| 10 | + | </head> | |
| 11 | + | <body> | |
| 12 | + | <h1 id="header">radio.aly.pet</h1> | |
| 13 | + | <!--index header menu | |
| 14 | + | <div id="menu"> | |
| 15 | + | <ul> | |
| 16 | + | <li><a href="admin/">Administration</a></li> | |
| 17 | + | <li><a href="status.xsl">Server Status</a></li> | |
| 18 | + | <li><a href="server_version.xsl">Version</a></li> | |
| 19 | + | </ul> | |
| 20 | + | </div> | |
| 21 | + | end index header menu --> | |
| 22 | + | <div id="container"> | |
| 23 | + | <div id="details" style="display:flex;flex-wrap:wrap"> | |
| 24 | + | <img /> | |
| 25 | + | <div> | |
| 26 | + | <span> | |
| 27 | + | Now playing: | |
| 28 | + | <a></a> | |
| 29 | + | </span> | |
| 30 | + | <ul style="display:flex;flex-direction:column"> | |
| 31 | + | Recently played: | |
| 32 | + | <li><a></a></li> | |
| 33 | + | <li><a></a></li> | |
| 34 | + | <li><a></a></li> | |
| 35 | + | <li><a></a></li> | |
| 36 | + | <li><a></a></li> | |
| 37 | + | </ul> | |
| 38 | + | </div> | |
| 39 | + | </div> | |
| 40 | + | <audio style="width:90%" controls="" src="/stream" type="audio/mpeg" /> | |
| 41 | + | <iframe id="iframe"></iframe> | |
| 42 | + | </div> | |
| 43 | + | <div id="footer"> | |
| 44 | + | Support icecast development at <a href="https://www.icecast.org/">www.icecast.org</a> | |
| 45 | + | </div> | |
| 46 | + | </body> | |
| 47 | + | <script> | |
| 48 | + | var np; | |
| 49 | + | var iframe = document.getElementById("iframe"); | |
| 50 | + | iframe.style.display = "none"; | |
| 51 | + | iframe.style.width = "90%"; | |
| 52 | + | iframe.style.minHeight = "550px"; | |
| 53 | + | iframe.style.borderRadius = "10px"; | |
| 54 | + | iframe.style.padding = "15px 20px"; | |
| 55 | + | iframe.style.marginBottom = "35px"; | |
| 56 | + | var img = document.querySelector("#details img"); | |
| 57 | + | img.style.maxWidth = "350px"; | |
| 58 | + | img.style.display = "none"; | |
| 59 | + | var text = document.querySelector("#details div span"); | |
| 60 | + | text.style.display = "none"; | |
| 61 | + | var textA = document.querySelector("#details div span a"); | |
| 62 | + | var ul = document.querySelector("#details div ul"); | |
| 63 | + | function getData() { | |
| 64 | + | fetch("/recent.json").then(function(req){ | |
| 65 | + | req.json().then(function(data){ | |
| 66 | + | data = data.reverse() | |
| 67 | + | var items = ul.querySelectorAll("li a") | |
| 68 | + | for (const n of Array(data.length).keys()) { | |
| 69 | + | var a = items[n]; | |
| 70 | + | if (data[n].guid.startsWith("plex://")) { | |
| 71 | + | a.setAttribute("href", "https://listen.plex.tv" + data[n].guid.substr(6)); | |
| 72 | + | } else { | |
| 73 | + | a.setAttribute("href", data[n].url); | |
| 74 | + | } | |
| 75 | + | a.innerHTML = data[n].artist + " - " + data[n].title; | |
| 76 | + | } | |
| 77 | + | }); | |
| 78 | + | }); | |
| 79 | + | fetch("/np.json").then(function(req){ | |
| 80 | + | req.json().then(function(data){ | |
| 81 | + | np = data; | |
| 82 | + | if (iframe.src.endsWith(np["@parentGuid"].substr(6))) return; | |
| 83 | + | if (!np["@parentGuid"].startsWith("plex://")) { | |
| 84 | + | iframe.style.display = "none"; | |
| 85 | + | textA.href = np["Media"]["Part"]["@key"]; | |
| 86 | + | } else { | |
| 87 | + | iframe.setAttribute("src", "https://listen.plex.tv" + np["@parentGuid"].substr(6)); | |
| 88 | + | iframe.style.display = "block"; | |
| 89 | + | textA.href = "https://listen.plex.tv" + np["@parentGuid"].substr(6); | |
| 90 | + | } | |
| 91 | + | img.setAttribute("src", np["@thumb"]); | |
| 92 | + | img.style.display = "block"; | |
| 93 | + | textA.innerText = np['@grandparentTitle'] + " - " + np['@title'] + " (" + np['@parentTitle'] + ")"; | |
| 94 | + | text.style.display = "block"; | |
| 95 | + | }) | |
| 96 | + | }); | |
| 97 | + | setTimeout(getData,5000); | |
| 98 | + | } | |
| 99 | + | getData(); | |
| 100 | + | </script> | |
| 101 | + | </html> | |
| 102 | + | </xsl:template> | |
| 103 | + | </xsl:stylesheet> | |
Newer
Older