Album Art - Part 3

Downloading images

The next challenge encountered was downloading images to be used later in the notification for the Album Art.

Again, an existing Rust Crate allowed to me make HTTP Get requests and process the necessary response.

isahc is a HTTP client library which serves the purpose of making HTTP requests in Rust seamlessly.

Example.

First, create a new HTTP client instance.

Here you can customize a wide range of properties .e.g. the duration of how long a request should timeout, the redirection policy and so on.

Further information of this exhaustive list can be found in the docs HttpClientBuilder

I purposely kept the configuration as simple as possible so not to over customize the HttpClient.

// create a new http client instance
    pub fn build_http_client() -> Result<HttpClient, isahc::Error> {
        let http_client = HttpClient::builder()
            .timeout(TIMEOUT)
            .redirect_policy(RedirectPolicy::Follow)
            .build()?;

        Ok(http_client)
    }

After a HttpClient is created. The next step is to get the album art image.

Note.

  1. The asynchronous call to make the HTTP request

  2. The data received from the server is stored in a buffer which is a vector in this example

    • this is purposely chosen as the data received back as a response is in bytes (which makes up an image)
    pub async fn get_album_art(&self, http_client: &HttpClient) -> Result<Vec<u8>, isahc::Error> {
        let mut img_buffer = vec![];
        http_client
            .get_async(&self.url)
            .await?
            .copy_to(&mut img_buffer)
            .await?;

        Ok(img_buffer)
    }

Saving the image to disk

Once the bytes of the image have been downloaded and stored in our buffer vector.

The process of saving the image to disk starts.

A popular Rust Crate called image serves the purpose of:

  1. Creating a new image from a byte slice

  2. Resizing the image to specific dimensions (height & width) - this solely acts as a guard to ensure that all images saved to disk are small

  3. Finally saving the image to disk; the image Crate can guess the format to save the image as (in this case it is in jpeg format).


	const THUMBNAIL_SIZE: u32 = 256;

 // save album art to disk
    pub fn save_album_art(&self, bytes: &Vec<u8>) -> Result<ImageResult<()>, ImageError> {
        let image = image::load_from_memory(&bytes);

        match image {
            Ok(_) => &image,
            Err(e) => panic!("{}", e),
        };

        let reader = ImageReader::new(Cursor::new(bytes))
            .with_guessed_format()
            .unwrap();

        let img_thumbnail = &image.unwrap().resize(
            THUMBNAIL_SIZE,
            THUMBNAIL_SIZE,
            image::imageops::FilterType::Nearest,
        );

        let img_save = img_thumbnail.save_with_format(&self.filename, reader.format().unwrap());

        match img_save {
            Ok(_) => Ok(img_save),
            Err(e) => Err(e),
        }
    }

The Album Art image filesize is not particularly that big with a file being on average 30kb.

Remember the Album Art is not that very big when you see it displayed inside the pop-up notification.

Further observations

Each track has a corresponding mpris:artUrl property .e.g.

dict entry(
    string "mpris:artUrl"
    variant string "https://open.spotify.com/image/ab67616d00001e024a7dcb87b8ec33f6c98ec5ff"
)

Notice the URL here returns a Page not found from Spotify.

The URL needs to be slightly ameded to ensure it points to a valid Album Art URL.

Original Incorrect URL = https://open.spotify.com/image/ab67616d00001e024a7dcb87b8ec33f6c98ec5ff

Correct URL = https://i.scdn.co/image/ab67616d00001e024a7dcb87b8ec33f6c98ec5ff

The Rust program deals with this by invoking a replace of string “open.spotify.com” with “i.scdn.co”

Last updated on 5 Sep 2022
Published on 5 Sep 2022