(06-03-2020, 03:53 PM)TheOldPresbyope Wrote: What is this Python script you refer to? Examining it would be the first step in a reverse-engineering process to find out what LMS API is being accessed or what stream decoding method is being used to capture the metadata of the currently playing track. I still haven't found either approach documented on the InterWeb, which leaves me dubious.
Regards,
Kent
Try to attach the .py file but with no success. Anyway here's the portions of code that create the connection with the LMS Server, and the one able to retrieve from it the music info
1) Constants and initial comments
Code:
LMS_ENABLED = False
LMS_SERVER = "localhost"
LMS_PORT = 9090
LMS_USER = ""
LMS_PASSWORD = ""
# Set this to MAC address of the Player you want to monitor.
# THis should be the MAC of the RaspDac system if using Max2Play with SqueezePlayer
# Note: if you have another Logitech Media Server running in your network, it is entirely
# possible that your player has decided to join it, instead of the LMS on Max2Play
# To fix this, go to the SqueezeServer interface and change move the player to the
# correct server.
LMS_PLAYER = "00:01:02:aa:bb:cc"
Note: don't ask me the meaning of the above comment, is quite obscured for me....
2 ) Establishing Connection:
Code:
if LMS_ENABLED:
for i in range (1,ATTEMPTS):
try:
# Connect to the LMS daemon
self.lmsserver = pylms.server.Server(LMS_SERVER, LMS_PORT, LMS_USER, LMS_PASSWORD)
self.lmsserver.connect()
# Find correct player
players = self.lmsserver.get_players()
for p in players:
### Need to find out how to get the MAC address from player
if p.get_ref().lower() == LMS_PLAYER.lower():
self.lmsplayer = p
break
if self.lmsplayer is None:
self.lmsplayer = self.lmsserver.get_players()[0]
if self.lmsplayer is None:
raise Exception('Could not find any LMS player')
break
except (socket_error, AttributeError, IndexError):
logging.debug("Connect attempt {0} to LMS server failed".format(i))
time.sleep(2)
else:
# After the alloted number of attempts did not succeed in connecting
logging.warning("Unable to connect to LMS service on startup")
3 - Retrieving info from LMS Server
Code:
def status_lms(self):
# Try to get status from LMS daemon
try:
lms_status = self.lmsplayer.get_mode()
except:
# Try to reestablish connection to daemon
try:
self.lmsserver = pylms.server.Server(LMS_SERVER, LMS_PORT, LMS_USER, LMS_PASSWORD)
self.lmsserver.connect()
# Find correct player
players = self.lmsserver.get_players()
for p in players:
### Need to find out how to get the MAC address from player
if p.get_ref().lower() == LMS_PLAYER.lower():
self.lmsplayer = p
break
if self.lmsplayer is None:
self.lmsplayer = self.lmsserver.get_players()[0]
if self.lmsplayer is None:
raise Exception('Could not find any LMS player')
lms_status = self.lmsplayer.get_mode()
except (socket_error, AttributeError, IndexError):
logging.debug("Could not get status from LMS daemon")
return { 'state':u"stop", 'artist':u"", 'title':u"", 'album':u"", 'remaining':u"", 'current':0, 'duration':0, 'position':u"", 'volume':0, 'playlist_display':u"", 'playlist_position':0, 'playlist_count':0, 'bitrate':u"", 'type':u"", 'current_time':u""}
if lms_status == "play":
import urllib
artist = urllib.unquote(str(self.lmsplayer.request("artist ?", True))).decode('utf-8')
title = urllib.unquote(str(self.lmsplayer.request("title ?", True))).decode('utf-8')
album = urllib.unquote(str(self.lmsplayer.request("album ?", True))).decode('utf-8')
playlist_position = int(self.lmsplayer.request("playlist index ?"))+1
playlist_count = self.lmsplayer.playlist_track_count()
volume = self.lmsplayer.get_volume()
current = self.lmsplayer.get_time_elapsed()
duration = self.lmsplayer.get_track_duration()
url = self.lmsplayer.get_track_path()
# Get bitrate and tracktype if they are available. Try blocks used to prevent array out of bounds exception if values are not found
try:
bitrate = urllib.unquote(str(self.lmsplayer.request("songinfo 2 1 url:"+url+" tags:r", True))).decode('utf-8').split("bitrate:", 1)[1]
except:
bitrate = u""
try:
tracktype = urllib.unquote(str(self.lmsplayer.request("songinfo 2 1 url:"+url+" tags:o", True))).decode('utf-8').split("type:",1)[1]
except:
tracktype = u""
playlist_display = "{0}/{1}".format(playlist_position, playlist_count)
# If the track count is greater than 1, we are playing from a playlist and can display track position and track count
if self.lmsplayer.playlist_track_count() > 1:
playlist_display = "{0}/{1}".format(playlist_position, playlist_count)
# if the track count is exactly 1, this is either a short playlist or it is streaming
elif self.lmsplayer.playlist_track_count() == 1:
try:
# if streaming
if self.lmsplayer.playlist_get_info()[0]['duration'] == 0.0:
playlist_display = "Streaming"
# it really is a short playlist
else:
playlist_display = "{0}/{1}".format(playlist_position, playlist_count)
except KeyError:
logging.debug("In LMS couldn't get valid track information")
playlist_display = u""
else:
logging.debug("In LMS track length is <= 0")
playlist_display = u""
# since we are returning the info as a JSON formatted return, convert
# any None's into reasonable values
if artist is None: artist = u""
if title is None: title = u""
if album is None: album = u""
if current is None: current = 0
if volume is None: volume = 0
if bitrate is None: bitrate = u""
if tracktype is None: tracktype = u""
if duration is None: duration = 0
# if duration is not available, then suppress its display
if int(duration) > 0:
timepos = time.strftime("%M:%S", time.gmtime(int(current))) + "/" + time.strftime("%M:%S", time.gmtime(int(duration)))
remaining = time.strftime("%M:%S", time.gmtime(int(duration) - int(current) ) )
else:
timepos = time.strftime("%M:%S", time.gmtime(int(current)))
remaining = timepos
return { 'state':u"play", 'artist':artist, 'title':title, 'album':album, 'remaining':remaining, 'current':current, 'duration':duration, 'position':timepos, 'volume':volume, 'playlist_display':playlist_display,'playlist_position':playlist_position, 'playlist_count':playlist_count, 'bitrate':bitrate, 'type':tracktype }
else:
return { 'state':u"stop", 'artist':u"", 'title':u"", 'album':u"", 'remaining':u"", 'current':0, 'duration':0, 'position':u"", 'volume':0, 'playlist_display':u"", 'playlist_position':0, 'playlist_count':0, 'bitrate':u"", 'type':u""}
The returned state object is then passed to the rendering part of the script, that uses it to send info to the OLED display
Hope this helps the discussion
Andrea