In the previous post, I was able to show song title, artist name, and album name of Bluetooth audio. Then, I thought it would be nice if the album cover art images could be displayed too.
Specification wise, transferring cover art over Bluetooth is supported since AVRCP 1.6 [1], but unfortunately it’s not widely implemented yet. So, my idea is to get cover art from Google Images [2] by web scraping the search results.
Assumptions
Bluetooth audio streaming (A2DP) and song info display (AVRCP) should be enabled by following the previous posts:
- Streaming Bluetooth Audio from Phone to Raspberry Pi using ALSA
- Controlling Bluetooth Audio on Raspberry Pi
- Internet connection for web scraping
- Raspberry Pi (e.g. Raspberry Pi 3 B+) *tested with Raspbian Stretch with Desktop (2019-04-08)
- Display (e.g. Raspberry Pi Official 7″ display)
- 3.5 mm plug Speaker (e.g. Rumfo Mini Speaker)
- Smartphone (Samsung Galaxy S7)
Steps
1. Installation of Required Software
1-1. Install “feh”, which will be used to display images.
sudo apt-get install feh -y
1-2. Install “beautifulsoup4” python package, which will be used for web scraping.
pip install beautifulsoup4
2. Updating Code
2-1. Open “media_control.py” from the previous post.
2-2. Add the following code which implements the cover art feature. [3] [4] [5]
import argparse import requests import bs4 import json import subprocess URL = 'https://www.google.com/search?tbm=isch&q=' HEADER = {'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36"} viewer = None def show_album_art(artist, album): if artist == '' or album == '': return global viewer if viewer: viewer.terminate() viewer.kill() artist = '"' + '+'.join(artist.split()) + '"' album = '"' + '+'.join(album.split()) + '"' res = requests.get(URL+'+'.join((artist, album)), headers=HEADER) soup = bs4.BeautifulSoup(res.text, 'html.parser') meta_elements = soup.find_all("div", {"class": "rg_meta"}) meta_dicts = (json.loads(e.text) for e in meta_elements) for d in meta_dicts: if d['oh'] == d['ow']: image_url = d['ou'] break res = requests.get(image_url) if res.status_code != 200: return with open('album_art', 'wb') as f: f.write(res.content) viewer = subprocess.Popen(['feh', '-g', '220x220', 'album_art'])
2-3. Add the line below at the end of elif block in “on_property_changed()”.
show_album_art(value.get('Artist', ''), value.get('Album', ''))
After the modification, “on_property_changed()” should look like this.
def on_property_changed(interface, changed, invalidated): if interface != 'org.bluez.MediaPlayer1': return for prop, value in changed.iteritems(): if prop == 'Status': print('Playback Status: {}'.format(value)) elif prop == 'Track': print('Music Info:') for key in ('Title', 'Artist', 'Album'): print(' {}: {}'.format(key, value.get(key, ''))) show_album_art(value.get('Artist', ''), value.get('Album', ''))
3. Test
3-1. Make sure Bluetooth audio connection is already established and bluealsa audio forwarding is enabled.
3-2. Run the updated program. Following images are examples of the results. The right side is the smartphone screen running Pandora streaming music. The left side (background) is the Raspberry Pi’s desktop showing the cover art image from Google Images.
When song changes, it closes feh window for the previous song, then re-open it with new cover art image for the current song.
Summary
There is still room for improvement (e.g. logic for image selection, error handling, etc), but so far I’m satisfied with the result as a prototype.
References
[1] Traditional Profile Specifications – Bluetooth SIG
[2] Google Images
[3] Python – Download Images from google Image search? – Stack Overflow
[4] scraping full size images from Google Images – GitHub Gist
[5] How can I close an image shown to the user with the Python Imaging Library? – Stack Overflow
This is something I was hoping to figure out for use at home. I would like to mount an old flat screen monitor on the wall and have the album art currently playing on my phone shown.
This appears to be similar to what you are working on, although I’m unclear as to where you are displaying.
Hi Michael, thank you for your comment! Flat screen on the wall sounds nice! For me, it’s just a prototype at the moment and I’m displaying it on my small raspberry pi display… 🙂
i get this error after editing property changed block “NameError: name ‘show_album_art’ is not defined”
Hi aaron, is the indentation correct?
Hi max,
I really like your content. You helped me a lot with my raspberry pi projects.
I am not a python guy, so I really have no clue why I get this error, but it seems like image_url is used before it is assigned. A call “res = requests.get(image_url)” reports “local variable ‘image_url’ referenced before assignment”.
Hi Mike, thanks for your comment!
Hmm, I’m getting the same error and it looks Google has changed the search result and the script needs to be updated… Thankfully, a solution is available here:
https://gist.github.com/genekogan/ebd77196e4bf0705db51f86431099e57#gistcomment-3170109
Hey great work!! I’m looking to do the reverse, that is hook up the pi to a cars head unit over Bluetooth and have it display track and title info from MPD.
Amy pointers or tips for going in that direction?