Thank you for your donation!


Cloudsmith graciously provides open-source package management and distribution for our project.


Alternative renderers and metadata
#15
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:

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
Reply


Messages In This Thread
RE: Alternative renderers and metadata - by steve4star - 10-27-2023, 08:06 PM

Forum Jump: