Last active 1703847764

aly's Avatar aly revised this gist 1703847764. 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