Skip to content

Commit

Permalink
Spotify cover images (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonPuglisi authored May 2, 2019
1 parent 49514c9 commit df8d810
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 84 deletions.
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

Elegant now playing display for Last.fm showing song metadata and local weather.

## Overview

Fetches now playing song information from Last.fm and displays album artwork
along with local weather, time, and user info. Automatically hides the cursor
after a few seconds of inactivity if the window is in focus.

Able to control colored Philips Hue lights based on prominent album art colors.

## Usage

Ensure you have recent versions of [Node.js](https://nodejs.org/en/) and
Expand All @@ -20,6 +28,8 @@ proxy, such as [NGINX](https://www.nginx.com/), in front of Descent's server.

Navigate to [`/now`](https://descent.live/now) to use Descent.

## API Requirements

### Weather

Weather is powered by the [Dark Sky API](https://darksky.net/dev/), or the
Expand All @@ -39,24 +49,23 @@ work in some modern browsers.

### Spotify Images

The
[Spotify Web API](https://beta.developer.spotify.com/documentation/web-api/) is
required for all artist images. Additionally, if Last.fm is unable to find an
album image, Spotify may be used as a backup.

Album cover and artist images and provided by the
[Spotify Web API](https://beta.developer.spotify.com/documentation/web-api/).
You will need to provide API authorization through a client ID and client
secret assigned by Spotify. Set your client ID as the `SPOTIFY_CLIENT`
environment variable, and your client secret as the `SPOTIFY_SECRET`
environment variable.

## User Preferences

### Descent Configuration

To configure the background, weather, and time displays, visit
[`/now/app/config`](https://descent.live/now/app/config). Dark Sky can
automatically determine weather units, but OpenWeatherMap cannot, so Descent
defaults to imperial units unless otherwise specified.

### Descent Configuration Import
#### Descent Configuration Import

You can import settings through a POST request to
[`/now/app/config/set`](https://descent.live/now/app/config/set). Each post
Expand Down Expand Up @@ -107,11 +116,3 @@ If Descent is hosted using HTTPS, users must instruct their browsers to allow
loading insecure (HTTP) content for the Descent website. This is because the
Hue API can only be accessed via HTTP. Users are informed of this on the
configuration page.

## Overview

Fetches now playing song information from Last.fm and displays album artwork
along with local weather, time, and user info. Automatically hides the cursor
after a few seconds of inactivity if the window is in focus.

Able to control colored Philips Hue lights based on prominent album art colors.
42 changes: 5 additions & 37 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,32 +122,6 @@ app.get('/now/app/hue', (req, res) => {
res.render('hue', { title });
});

app.get('/now/app/cover', (req, res) => {
let url = encodeURI(decodeURIComponent(req.query.url));
if (!url) {
console.warn('Error getting cover: No URL specified');
res.send();
return;
}

let pattern = /^https:\/\/lastfm-img[0-9]+\.akamaized\.net\//;
if (!url.match(pattern)) {
console.warn(`Error getting cover: Invalid URL - ${url}`);
res.send();
return;
}

request({ url, encoding: null }, (err, res2, body) => {
if (err || res2.statusCode != 200) {
console.warn(`Error getting cover: Invalid response - ${err}`);
res.send();
return;
}

res.send(body);
});
});

app.post('/now/app/weather', (req, res) => {
let lat = req.body.latitude;
let lon = req.body.longitude;
Expand Down Expand Up @@ -216,35 +190,29 @@ app.post('/now/app/spotify/track', (req, res) => {

app.post('/now/app/spotify/artist', (req, res) => {
if (!spotifyKey) {
console.warn('Error getting Spotify track: No API key');
res.json(new Track());
console.warn('Error getting Spotify artist: No API key');
res.json(new Artist());
return;
}

let artist = encodeURIComponent(decodeURIComponent(req.body.artist));
let query = artist;

let options = {
url: `https://api.spotify.com/v1/search?q=${query}&type=artist&limit=1`,
url: `https://api.spotify.com/v1/artists/${query}`,
headers: {
'Authorization': `Bearer ${spotifyKey}`
}
};
request(options, (err, res2, body) => {
if (err || res.statusCode != 200) {
if (err || res2.statusCode != 200) {
console.warn(`Error getting Spotify artist: Invalid response: ${err}`);
res.json(new Artist());
return;
}

let data = JSON.parse(body);
if (data.artists.total < 1) {
console.warn('Error getting Spotify artist: No results');
res.json(new Artist());
return;
}

let artist = data.artists.items[0];
let artist = data;
artist.success = true;

res.json(artist);
Expand Down
68 changes: 36 additions & 32 deletions source/js/utility/images.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,44 @@ function fetchImages() {
if (!newTrack())
return;

// Check for cover from Last.fm, and fallback to Spotify
if (resources.track.current.cover) {
let urlCover = encodeURIComponent(resources.track.current.cover);
setCover(`/now/app/cover?url=${urlCover}`);
}
else {
let url = '/now/app/spotify/track';
let urlArtist = encodeURIComponent(resources.track.current.artist);
let urlTitle = encodeURIComponent(resources.track.current.title);
let body = `artist=${urlArtist}&title=${urlTitle}`;

$.post(url, body, data => {
// Perform no action if unsuccessful
if (!data.success) {
resetCover();
return;
}

// Set cover image if one is found
setCover(data.album.images[0].url);
}).fail(resetCover);
}
// Update cover and artist images
fetchTrackInfo((coverImageUrl, artistId) => {
setCover(coverImageUrl);

// Stop if track has changed and background type is not artist
if (getBackgroundType() !== 'artist')
return;
// Update background if type is artist
if (getBackgroundType() === 'artist')
fetchArtistImage(artistId, setBackground);
});
}

function fetchTrackInfo(callback) {
// Query Spotify for track info
let url = '/now/app/spotify/track';
let urlArtist = encodeURIComponent(resources.track.current.artist);
let urlTitle = encodeURIComponent(resources.track.current.title);
let body = `artist=${urlArtist}&title=${urlTitle}`;

$.post(url, body, data => {
// Return track info if found
if (data && data.success && data.album.images.length > 0)
return callback(data.album.images[0].url, data.artists[0].id);

resetCover();
}).fail(resetCover);
}

function fetchArtistImage(artistId, callback) {
// Query Spotify for artist image
let url = '/now/app/spotify/artist';
let urlArtist = encodeURIComponent(resources.track.current.artist);
let body = `artist=${urlArtist}`;
let urlArtistId = encodeURIComponent(artistId);
let body = `artist=${urlArtistId}`;

$.post(url, body, data => {
// Set background image if one is found
if (data && data.success && data.images.length > 0) {
let img = data.images[0].url;
$('.background').css('background-image', `url(${img})`);
} else
resetBackground();
if (data && data.success && data.images.length > 0)
return callback(data.images[0].url);

resetBackground();
}).fail(resetBackground);
}

Expand Down Expand Up @@ -92,6 +91,11 @@ function updateCover(cover) {
resources.cover.src = url;
}

function setBackground(background) {
// Set background image
$('.background').css('background-image', `url(${background})`);
}

function resetBackground() {
let url;
if (nowPlaying() && resources.cover.src !== getBlankImageData())
Expand Down
2 changes: 1 addition & 1 deletion source/js/utility/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function initMetadata() {

// Start metadata fetch loop
fetchMetadata();
setInterval(fetchMetadata, 3000);
setInterval(fetchMetadata, 2000);
}

function fetchMetadata() {
Expand Down

0 comments on commit df8d810

Please sign in to comment.