Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement: show spotify code of current radio track #61

Open
marco79cgn opened this issue Dec 18, 2022 · 9 comments
Open

Improvement: show spotify code of current radio track #61

marco79cgn opened this issue Dec 18, 2022 · 9 comments

Comments

@marco79cgn
Copy link

marco79cgn commented Dec 18, 2022

Not really an issue, but I kindly ask if someone would help me to show an additional image (Spotify code) as overlay of the cover (see preview below).

Background:
I never wrote any python code before but managed to integrate Spotipy (in sonos_user_data.py). I'm using it to show cover art of the current track on (mostly german) radio stations. I'm reading both artist and track name from the radio text, query Spotify, get the cover url and set it as background image (image_uri). This already works pretty good.
As a bonus, since I already have the spotify metadata, I'd like to show the spotify code as well. It's kind of a qr code that can be scanned in the Spotify app to play the track. I already know how to get the code, I just have to put the spotify track uri at the end of this url to download the image with the code:
https://scannables.scdn.co/uri/plain/jpeg/368A7D/white/640/spotify:track:5wEoNauEpwOc2rlU0274oT

ToDo:
So all there's left to do is to display this image for example in the upper right of the cover art (as overlay). I'm struggling how to do that as I don't really understand this tk mechanism (and how to extend label_albumart_detail). Any help is really appreciated.

This is how it shuold look like:
Cover-SpotifyCode

@ashenshugarRET
Copy link
Contributor

ashenshugarRET commented Dec 19, 2022

Hello,

Yes, I find tk.inter a bit confusing as well, essentially you'll want to declare a new label and anchor it to the top right (or NE) as follows in display_controller.py:

self.label_spotify_code = tk.Label(
self.album_frame,
image=None,
borderwidth=0,
highlightthickness=0,
fg="white",
bg="black",
)
self.label_spotify_code.place(x=10, y=10, anchor='ne')

For ease of reference I would insert this in the same place where all the other tk.inter frames and labels are assigned...

The x= and y= values will indent the graphic a little from the edge. This label is attached to album_frame so it should show whenever album art is displayed.

The final thing you need to do is to populate the label with your Spotify Code graphic, adding the following towards the end of display_controller.py where the similar code to populate the album art should do it:

self.label_spotify_code.configure(image=link to spotify code or variable which contains the graphic), this is the bit I'm struggling with but you mention that you are able to construct the hyperlink so hopefully that will work.

I hope this points in enough of the right direction to get your idea working.

@marco79cgn
Copy link
Author

marco79cgn commented Dec 19, 2022

Thank you so much, you really helped me a lot to understand this thing. I managed to implement it now and it works! It took the most time to place this image on the screen so that you can actually see it at all. lol For some reason, West and East are switched and work exactly the other way round on my screen. But at the end I moved it more to the bottom right (relx=0.54, y=600, anchor=tk.SW) with trial & error. ;)

Now I just have to find a solution that looks pleasing. Probably I put the cover back to fullscreen and use the new overlay track information. The thing is, I'm still on a very old version of the music-screen-api since I made quite some changes and didn't want to break anything when trying to merge the latest master. In Java and IntelliJ I'm quite familiar with merging code and there is a really good integrated merge tool to solve conflicts. But in Python, I use vim in the Console and VS Code on the Mac. Didn't manage to connect VS Code remotely via SSH as it always tells me the SSH server on my Pi would be incompatible for reasons unknown.

This is how it looks right now:
Bildschirm­foto 2022-12-19 um 23 24 39 Kopie
So the next thing would be to merge all my stuff with the latest master. Thanks again!

@ashenshugarRET
Copy link
Contributor

ashenshugarRET commented Dec 20, 2022

Hello,

Great to hear you've been successful, label placement is definitely tricky in tkinter.

I've been trying to get this working as well, and have managed to implement code that allows the user to select whether they want to display the Spotify Code or not through a setting in the sonos_settings.py file.

What I can't do is the Spotipy artist and song name search to return the Spotify URI for songs...

spotify code example

I've uploaded these changes to my upto date repository ashenshugarRET/music-screen-api for this program, perhaps if we can collaborate we can contribute the results back to the Hankhank10's parent project

@marco79cgn
Copy link
Author

marco79cgn commented Dec 21, 2022

Sure, let's do it. 👍
At the moment I don't have that much spare time because of christmas. But I'll try it as soon as possible.

The Spotify integration isn't that complicated with the Spotipy library.

In sonos_settings.py I added two additional attributes which are needed for the spotify authentication. You have to create a new client in the Dashboard to obtain them:

spotify_client_id = "33************"
spotify_client_secret = "97df**********"

I imported Spotipy in sonos_user_data.py at the top:

import spotipy
from spotipy.oauth2 import SpotifyOAuth 
import spotipy.util as util

With these 3 additional functions I'm retrieving the Spotify metadata:

def get_album_cover_from_spotify(self, trackname, artist, spotify_client_id, spotify_client_secret, station):
    current_artist_title = get_artist_title_separated(trackname)
    search_title = ""
    search_artist = ""
    image_uri = ""
    try:
        if len(current_artist_title) > 1:
            search_title = clean_title(current_artist_title[1]) 
        search_artist = current_artist_title[0]
    except:
        search_title = trackname
        search_artist = artist

    try:
        _LOGGER.info("Attempting to get cover url for %s %s..." % (search_title, search_artist))
        spotify = spotipy.Spotify(auth_manager=SpotifyOAuth(username='YOUR_SPOTIFY_USERNAME', client_id=spotify_client_id, client_secret=spotify_client_secret, redirect_uri="https://example.com/callback/", scope="playlist-modify-public"))
        results = spotify.search(q='artist:' + search_artist + ' track:' + search_title, type='album,track', limit=2, market='DE')
        _LOGGER.debug("albums result: %s" % results)
        if len(results) == 0:
            print("Couldn't find entry for track %s from artist %s" % (search_title, search_artist))
        image_uri = results['tracks']['items'][0]['album']['images'][0]['url']
        _LOGGER.info("cover: %s" % image_uri)
        self.spotify_code_uri = "https://scannables.scdn.co/uri/plain/jpeg/368A7D/white/640/" + results['tracks']['items'][0]['uri']
        _LOGGER.info("Spotify Code Uri: %s", self.spotify_code_uri)
    except:
        _LOGGER.info("Couldn't find artwork for %s %s" % (search_title, search_artist))
        _LOGGER.info("Trying to query Spotify again with one search string")
        results = spotify.search(q=search_artist + ' ' + search_title, type='album,track', limit=2, market='DE')
        try:
            image_uri = results['tracks']['items'][0]['album']['images'][0]['url']
            _LOGGER.info("cover: %s" % self.image_uri)
            self.spotify_code_uri = "https://scannables.scdn.co/uri/plain/jpeg/368A7D/white/640/" + results['tracks']['items'][0]['uri']
            _LOGGER.info("Spotify Code Uri: %s", self.spotify_code_uri)
        except:
            _LOGGER.info("Couldn't get a cover, giving up..")
    return image_uri

def get_artist_title_separated(trackname):
    _LOGGER.info("Trying to split trackname: %s", trackname)
    if " von " in trackname: 
        trackname = trackname.replace('"', '')
        return trackname.split(" von ")
    if " - " in trackname: return trackname.split(' - ')
    if " mit " in trackname: return trackname.split(" mit ")
    if " / " in trackname: return trackname.split(" / ")
    if ": " in trackname: return trackname.split(": ")
    return trackname

def clean_title(title):
    cleaned_title = title
    try:
        cleaned_title_array = title.split(' (')
        cleaned_title = cleaned_title_array[0]
        cleaned_title_array = cleaned_title.split(' --')
        cleaned_title = cleaned_title_array[0]
        cleaned_title_array = cleaned_title.split(' |')
        cleaned_title = cleaned_title_array[0]
    except:
        return cleaned_title
    return cleaned_title

First of all, the code needs refactoring or cleanup, I know. The thing is that all german radio stations transmit both artist and title combined as title. So I have to parse both values from the title because you get the best search results from Spotify when asking for both artist and title separated. I also clean the title and remove things like "(Radio Edit) etc. The problem here is, that every radio station shows this information differently, for example
"title": "Holiday von Madonna"
"title": "Holiday - Madonna"
"title": "Madonna / Holiday"
"title": "Holiday, Madonna"
etc.

So I need a lot of if/else statements to extract the information correctly. On my iPhone in Shorcuts, I'm using a dictionary where I store the radio station as key and an additional dictionary as value where I configure which separator each station is using and whether artist is on the first or second position after splitting.
Bildschirm­foto 2022-12-21 um 17 52 22

Long story short, in sonos_user_data.py I'm calling the function above in the refresh() function like this:

cover = get_album_cover_from_spotify(self, self.trackname, self.artist, self.spotify_client_id, self.spotify_client_secret, self.station)
if "http" in cover: 
    self.image_uri = cover

Please excuse my python skills. I'm "debugging" it with logging. 🙈

@ashenshugarRET
Copy link
Contributor

ashenshugarRET commented Dec 21, 2022

Thanks,

I have an issue importing spotipy, everytime I insert 'import spotipy' into the top of sonos_user_data.py the program crashes on booting producing the following errors

Traceback (most recent call last):
File "go_sonos_highres.py", line 14, in
from aiohttp import ClientError, ClientSession
File "/usr/local/lib/python3.7/dist-packages/aiohttp/init.py", line 6, in
from .client import (
File "/usr/local/lib/python3.7/dist-packages/aiohttp/client.py", line 35, in
from . import hdrs, http, payload
File "/usr/local/lib/python3.7/dist-packages/aiohttp/http.py", line 7, in
from .http_parser import (
File "/usr/local/lib/python3.7/dist-packages/aiohttp/http_parser.py", line 15, in
from .helpers import NO_EXTENSIONS, BaseTimerContext
File "/usr/local/lib/python3.7/dist-packages/aiohttp/helpers.py", line 667, in
class CeilTimeout(async_timeout.timeout):
TypeError: function() argument 1 must be code, not str

It's clearly a local issue form me as you are successfully using Spotipy with your installation

@marco79cgn
Copy link
Author

marco79cgn commented Dec 21, 2022

Did you install the lib successfully with

pip install spotipy

?
Maybe my version is already a little outdated but it should still work according to the documentation.

@ashenshugarRET
Copy link
Contributor

ashenshugarRET commented Dec 21, 2022

Yes, I think I installed spotipy successfully, I used the exact instruction you mention. I can import it into standalone scripts and access and search Spotify successfully. The problem only appears when I try importing into sonos_user_data.py, go_sonos_highres.py or display_controller.py...

I will investigate by installing from a fresh Raspberry Pi image, like you I don't have much time available this side of Christmas, so this will be a New Year Project

UPDATE: Fixed it was a permissions issue as I had set go_sonos_highres.py to start as sudo which must have been causing conflicts.

@ashenshugarRET
Copy link
Contributor

ashenshugarRET commented Jan 4, 2023

To provide an update, I now have the Spotify Code showing reliably when the spotipy search returns a result, when no result is returned no code is displayed.
I have just implemented code to override displaying the albumart from the Sonos system with that on Spotify (again if a valid result is returned), I'm going to test this for a few days before commiting the changes to my respository which will then be rolled up to the pull request I requested to this parent respository.
Both the displaying of the Spotify Code and Albumart are controlled by variables in sonos_settings.py.

@ashenshugarRET
Copy link
Contributor

Hello,

I've completed my implementation of showing Spotify Codes as well as using Spotify's album art, these should be getting merged into the main repository soon.

I woujld imagine @hankhank10 will close this issue once that is done.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants