nowplaying.py
· 1.3 KiB · Python
Raw
#!/usr/bin/env python3
from plexapi.myplex import MyPlexAccount
from plexapi.server import PlexServer
from dotenv import load_dotenv
import xmltodict as xtd
load_dotenv()
from os import environ
from xml.etree import ElementTree
from json import dumps, dump
from time import sleep
out = ""
account = MyPlexAccount(environ["PLEX_USER"], environ["PLEX_PASSWORD"])
token = account.resource(environ["PLEX_SERVER_NAME"]).accessToken
laptop = PlexServer(environ["PLEX_SERVER_ADDR"], token)
prev = {"@guid": None}
recent = []
while True:
try:
p = [p for p in laptop.sessions() if p.player.title == "l"][0]
xml = xtd.parse(ElementTree.tostring(p._data,encoding="utf-8"))["Track"]
if xml["@guid"] == prev["@guid"]:
sleep(15)
continue
recent.append({"url": xml["Media"]["Part"]["@key"], "guid": xml["@guid"], "artist": xml["@grandparentTitle"], "title": xml["@title"]})
if len(recent) > 5:
recent = recent[1:]
with open("/usr/share/icecast2/web/recent.json", "w") as f:
dump(recent,f)
prev = xml
del xml["Player"]
del xml["User"]
out = xml
except Exception as e:
out = {"error": str(e)}
out = dumps(out,indent=4)
print(out)
with open("/usr/share/icecast2/web/np.json", "w") as f:
f.write(out)
sleep(15)
| 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
· 5.0 KiB · XML
Raw
<!-- /usr/share/icecast2/web/status.xsl -->
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0">
<xsl:output omit-xml-declaration="yes" method="xml" indent="yes" encoding="UTF-8" />
<xsl:template match = "/icestats">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>radio.aly.pet</title>
<link rel="stylesheet" type="text/css" href="style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
</head>
<body>
<h1 id="header">radio.aly.pet</h1>
<!--index header menu
<div id="menu">
<ul>
<li><a href="admin/">Administration</a></li>
<li><a href="status.xsl">Server Status</a></li>
<li><a href="server_version.xsl">Version</a></li>
</ul>
</div>
end index header menu -->
<div id="container">
<div id="details" style="display:flex;flex-wrap:wrap">
<img />
<div>
<span>
Now playing:
<a></a>
</span>
<ul style="display:flex;flex-direction:column">
Recently played:
<li><a></a></li>
<li><a></a></li>
<li><a></a></li>
<li><a></a></li>
<li><a></a></li>
</ul>
</div>
</div>
<audio style="width:90%" controls="" src="/stream" type="audio/mpeg" />
<iframe id="iframe"></iframe>
</div>
<div id="footer">
Support icecast development at <a href="https://www.icecast.org/">www.icecast.org</a>
</div>
</body>
<script>
var np;
var iframe = document.getElementById("iframe");
iframe.style.display = "none";
iframe.style.width = "90%";
iframe.style.minHeight = "550px";
iframe.style.borderRadius = "10px";
iframe.style.padding = "15px 20px";
iframe.style.marginBottom = "35px";
var img = document.querySelector("#details img");
img.style.maxWidth = "350px";
img.style.display = "none";
var text = document.querySelector("#details div span");
text.style.display = "none";
var textA = document.querySelector("#details div span a");
var ul = document.querySelector("#details div ul");
function getData() {
fetch("/recent.json").then(function(req){
req.json().then(function(data){
data = data.reverse()
var items = ul.querySelectorAll("li a")
for (const n of Array(data.length).keys()) {
var a = items[n];
if (data[n].guid.startsWith("plex://")) {
a.setAttribute("href", "https://listen.plex.tv" + data[n].guid.substr(6));
} else {
a.setAttribute("href", data[n].url);
}
a.innerHTML = data[n].artist + " - " + data[n].title;
}
});
});
fetch("/np.json").then(function(req){
req.json().then(function(data){
np = data;
if (iframe.src.endsWith(np["@parentGuid"].substr(6))) return;
if (!np["@parentGuid"].startsWith("plex://")) {
iframe.style.display = "none";
textA.href = np["Media"]["Part"]["@key"];
} else {
iframe.setAttribute("src", "https://listen.plex.tv" + np["@parentGuid"].substr(6));
iframe.style.display = "block";
textA.href = "https://listen.plex.tv" + np["@parentGuid"].substr(6);
}
img.setAttribute("src", np["@thumb"]);
img.style.display = "block";
textA.innerText = np['@grandparentTitle'] + " - " + np['@title'] + " (" + np['@parentTitle'] + ")";
text.style.display = "block";
})
});
setTimeout(getData,5000);
}
getData();
</script>
</html>
</xsl:template>
</xsl:stylesheet>
| 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> |