Ralph_Cramden
Major Contributor
- Joined
- Dec 6, 2020
- Messages
- 2,831
- Likes
- 3,770
- Thread Starter
- #21
OK, last dump here before I move this to github. Some CSS changes, and much greater efficiency on the server side, now consumes about 5% of CPU on a lowly Pi Zero W. In the next week or so, will add the usual play/pause, etc. buttons.
wiim.html:
server.py:
wiim.html:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>WiiM Mini</title>
<style>
body {
background-color: grey;
}
h1 {
color: blue;
font-family: verdana;
font-size: 300%;
}
p {
color: black;
font-family: Helvetica;
font-size: 2em;
margin: 50px 10px 10px 25px;
}
ul {
list-style-type: none;
margin-left: 0px;
margin-top: 0px;
margin-bottom: 0px;
margin-right: 0px;
padding: 0px;
overflow: hidden;
background-color: black;
}
li {
float: left;
}
li a {
display: block;
color: white;
padding: 8px;
text-align: center;
font-family: Helvetica;
font-size: 180%;
text-decoration: none;
}
.flex-container{
width: 100%;
min-height: 300px;
margin: 0 auto;
display: -webkit-flex; /* Safari */
display: flex; /* Standard syntax */
}
.flex-container .column1{
width: 48%;
padding: 0px;
background: grey;
-webkit-flex: 1; /* Safari */
-ms-flex: 1; /* IE 10 */
flex: 1; /* Standard syntax */
}
.flex-container .column2{
margin-left: 10px;
-webkit-flex: 1; /* Safari */
-ms-flex: 1; /* IE 10 */
flex: 1; /* Standard syntax */
border-radius: 10px;
background-color: lightgrey;
position: relative;
}
#info {
position: absolute;
bottom: 0;
}
img { max-width: 100%; height: auto;}
</style>
</head>
<body>
<div id="myData" class="flex-container">
<div id="albumcover" class="column1"></div>
<div class="column2">
<ul>
<li id="allmusic"></li>
<li id="lastfm"></li>
<li id="wiki"></li>
</ul>
<p id="title"></p>
<p id="album"></p>
<p id="artist"></p>
<p id="info"></p>
</div>
</div>
<script>
let oldState = '';
function getStatus() {
fetch('?action=status')
.then(function (response) {
let json = response.json();
return json;
})
.then(function (data) {
updateStatus(data);
})
.catch(function (err) {
console.log('error: ' + err);
});
}
function updateStatus(data) {
state = data['CurrentTransportState'];
if(state != oldState) {
oldState = state;
console.log(state);
if(state == 'PLAYING')
fetchJson();
}
}
function fetchJson() {
fetch('?action=getdata')
.then(function (response) {
return response.json();
})
.then(function (data) {
updateData(data);
})
.catch(function (err) {
console.log('error: ' + err);
});
}
function updateData(data) {
var mainContainer = document.getElementById("myData");
var div = document.getElementById("albumcover");
div.innerHTML = '<img src="' + data['upnp:albumArtURI'] + '" style="width:100%;"</img>';
var el = document.getElementById("title");
el.innerHTML = data['dc:title'];
el = document.getElementById("artist");
el.innerHTML = data['upnp:artist'];
el = document.getElementById("album");
el.innerHTML = data['upnp:album'];
var depth = data['song:format_s'];
if(depth > 24) depth=24;
var actualQuality = data['song:actualQuality'];
var rate = data['song:rate_hz'] / 1000.0;
if(actualQuality == 'HD')
depth = 16;
if(actualQuality == 'LOSSLESS')
actualQuality = "HiFi";
var bitrate = data['song:bitrate'] / 1000.0;
if (isNaN(bitrate))
bitrate = "";
else bitrate = bitrate + " kbps";
el = document.getElementById("info");
el.innerHTML = `${depth} bits / ${rate} kHz ${bitrate} - ${actualQuality}`;
var artist = encodeURIComponent(data['upnp:artist'].replace(/'/g,""));
p = artist.indexOf('(');
if(p>0)
artist = artist.substring(0,p);
var album = encodeURIComponent(data['upnp:album'].replace(/'/g,""));
p = album.indexOf('(');
if(p>0)
album = album.substr(0,p);
var url = `<a href='http://duckduckgo.com/?q=%5Csite:last.fm english ${artist} ${album}' target='lastfm'>Last.fm</a>`;
el = document.getElementById("lastfm");
el.innerHTML = url;
var url = `<a href='http://duckduckgo.com/?q=%5Csite:wikiwand.com ${artist} ${album}' target='wiki'>Wikiwand</a>`;
el = document.getElementById("wiki");
el.innerHTML = url;
var url = `<a href='http://duckduckgo.com/?q=%5Csite:allmusic.com ${artist} ${album}' target='allmusic'>Allmusic</a>`;
el = document.getElementById("allmusic");
el.innerHTML = url;
}
setInterval(getStatus,1000);
getStatus();
fetchJson();
</script>
</body>
</html>
server.py:
Python:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import http.server
import socketserver
from urllib.parse import urlparse
from urllib.parse import parse_qs
import json
import xmltodict
import upnpclient
class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# Extract query param
action = ''
query_components = parse_qs(urlparse(self.path).query)
if 'action' in query_components:
action = query_components["action"][0]
content_type = "application/json"
self.send_response(200)
self.send_header("Content-type", content_type)
self.end_headers()
if action == "getdata":
obj = dev.AVTransport.GetMediaInfo(InstanceID='0')
meta = obj['CurrentURIMetaData']
items = xmltodict.parse(meta)["DIDL-Lite"]["item"]
self.wfile.write(str.encode(json.dumps(items)))
return
if action == "status":
obj = dev.AVTransport.GetTransportInfo(InstanceID='0')
self.wfile.write(str.encode(json.dumps(obj)))
return
else:
self.path = 'wiim.html'
return http.server.SimpleHTTPRequestHandler.do_GET(self)
####################################################################
#### Change the ip address to that of your WiiM Mini
dev = upnpclient.Device("http://192.168.68.112:49152/description.xml")
####################################################################
# Create an object of the above class
handler_object = MyHttpRequestHandler
PORT = 8080
my_server = socketserver.TCPServer(("", PORT), handler_object)
# Start the server
my_server.serve_forever()