10-27-2023, 08:06 PM
I had a play with the AirPlay decoder and was able to create a HTML page that had track info and relevant cover art.
Using the example, I extended it to write a HTML file, but I understand that writing cuurentsong.txt might be a better approach.
I am not familiar enough with how the moode code changes the display when airplay is active.
To test I run the following:
Where the reader has the following:
Using the example, I extended it to write a HTML file, but I understand that writing cuurentsong.txt might be a better approach.
I am not familiar enough with how the moode code changes the display when airplay is active.
To test I run the following:
Code:
cat /tmp/shairport-sync-metadata | shairport-sync-metadata-reader | sudo ~/reader.py
Where the reader has the following:
Code:
#!/usr/bin/env python3
# @TheOldPresbyope
# this code was copied from
# https://appcodelabs.com/show-artist-song-metadata-using-airplay-on-raspberry-pi
# and modified to print to console as if it were a three-line display
# NOTE - doesn't clear previous value of specific key when no new value is received
# @steve4star
# Adapted to clear key values when track/album identifier changes
# Writes to HTML page /var/www/airplay.html
# NOTE needs permission 755 for html and jpg file
import sys
import re
import os
import shutil
# a device isn't needed but let's define a dummy just
# to keep the same render() syntax as in the original code
device = "null"
artist = ""
track = ""
album = ""
tracklength = ""
picture = ""
pictureURL =""
status = ""
progress = ""
ID = ""
def extract(line):
#Progress String "388666875/630526809/706186875".
m = re.match('^(Title|Artist|Album Name|Track length|Progress String): \"(.*?)\"\.$', line)
if m:
return m.group(1), m.group(2)
else:
#Track length: 300000 milliseconds.
m = re.match('^(Track length): (.*?) milliseconds\.$', line)
if m:
return m.group(1), m.group(2)
else:
#Picture received, length 98020 bytes.
m = re.match('^(Picture received, length) (.*?) bytes\.$', line)
if m:
return m.group(1), m.group(2)
else:
m = re.match('^(Play Session) (.*?)\.$', line)
if m:
return m.group(1), m.group(2)
else:
m = re.match('^(Persistent ID): (.*?)\.$', line)
if m:
return m.group(1), m.group(2)
else:
return None, None
def update(key, val):
global artist, album, track, tracklength, picture, status, progress, ID
if key == "Artist":
artist = val
elif key == "Album Name":
album = val
elif key == "Title":
track = val
elif key == "Track length":
tracklength = val
elif key == "Picture received, length":
picture = val
elif key == "Progress String":
progress = val
elif key == "Play Session":
status = val
elif key == "Persistent ID":
if ID != val: # new item, clear cached values
ID = val
artist = album = track = tracklength = picture = progress = ""
def render(device):
global pictureURL
sTime=""
# print to console instead of writing output to a device
# use an ASCII escape sequence to imitate a three-line display
print(f'\u001b[2J\
ID: {ID}\n\
Artist: {artist}\n\
Album Name: {album}\n\
Title: {track}\n\
Cover: {pictureURL}\n\
Status: {status}')
if tracklength != "":
mill_sec = int(tracklength)
total_sec = mill_sec / 1000
hours = int(total_sec // 3600 )
min = int((total_sec // 60) % 60)
sec = int(total_sec % 60)
sTime = f"{hours:02d}:{min:02d}:{sec:02d}"
print(f"Length: {sTime}")
if picture != "":
# directory/folder path
dir_path = r'/tmp/shairport-sync/.cache/coverart'
res = []
for file_path in os.listdir(dir_path):
if os.path.isfile(os.path.join(dir_path, file_path)):
res.append(file_path)
if res:
pictureURL = res[-1:][0]
shutil.copyfile( f'{dir_path}/{pictureURL}', '/var/www/images/airplay.jpg' )
with open('/var/www/airplay.html', 'w') as html:
html.write(f'<!DOCTYPE html>\n\
<html>\n\
<head><title>Airplay Metadata</title><meta http-equiv="refresh" content="60"></head>\n\
<body>\n\
<table>\n\
<tbody>\n\
<tr><td>Artist</td><td>{artist}</td></tr>\n\
<tr><td>Album Title</td><td>{album}</td></tr>\n\
<tr><td>Track Title</td><td>{track}</td></tr>\n\
<tr><td>Track Length</td><td>{sTime}</td></tr>\n\
<tr><td><img src="/images/airplay.jpg"></td><td>{status}</td></tr>\n\
</tbody>\n\
</table>\n\
</body>\n\
</html>\n')
# Create devices
# deleted all the device code
# Welcome message
# No, let's not
# Main loop
try:
while True:
line = sys.stdin.readline()
key, val = extract(line)
if key and val:
update(key, val)
render(device)
except KeyboardInterrupt:
sys.stdout.flush()
pass