Showing Album Cover Art Images for Bluetooth Audio

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:

 

Example Setup

 

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

 

 

Sponsor Link

7 Comments

  1. 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.

    1. 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… 🙂

  2. i get this error after editing property changed block “NameError: name ‘show_album_art’ is not defined”

  3. 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”.

  4. 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?

Comments are closed.