Add Beats music support

This commit is contained in:
Jonathan Cremin 2014-12-04 15:45:52 +00:00
parent f7a5291e17
commit 1a7896716f
9 changed files with 154 additions and 18 deletions

View file

@ -10,7 +10,6 @@ This is in super early development, has no design and only supports albums right
On the immediate todo list:
* Add support for tracks, and maybe artists
* Use album release year for additional sanity check on matches
* Do some kind of a design, particularly for the share page
* Handle expected and unexpected errors better than the current crash-fest
* Use promises for service searches and do them simultaneously

87
lib/beats.js Normal file
View file

@ -0,0 +1,87 @@
"use strict";
var parse = require('url').parse;
var request = require('superagent');
if (!process.env.BEATS_KEY || !process.env.BEATS_SECRET) {
throw new Error("You need to set BEATS_KEY and BEATS_SECRET environment variables");
}
var credentials = {
key: process.env.BEATS_KEY,
secret: process.env.BEATS_SECRET
};
var apiRoot = "https://partner.api.beatsmusic.com/v1/api";
module.exports.lookupId = function(id, next) {
if (id.substr(0,2) == "al") {
request.get(apiRoot + "/albums/" + id + "/images/default?size=medium&client_id=" + credentials.key).redirects(0).end(function(res) {
var artwork = res.headers.location;
request.get(apiRoot + "/albums/" + id + "?client_id=" + credentials.key, function(res) {
var result = res.body.data;
next({
service: "beats",
type: "album",
id: result.id,
name: result.title,
url: "https://listen.beatsmusic.com/albums/" + result.id,
artwork: artwork,
artist: {
name: result.artist_display_name
}
});
});
});
} else if (id.substr(0,2) == "tr") {
request.get(apiRoot + "/tracks/" + id + "?client_id=" + credentials.key, function(res) {
var result = res.body.data;
request.get(apiRoot + "/albums/" + result.refs.album.id + "/images/default?size=medium&client_id=" + credentials.key).redirects(0).end(function(res) {
var artwork = res.headers.location;
next({
service: "beats",
type: "track",
id: result.id,
name: result.title,
url: "https://listen.beatsmusic.com/albums/" + result.refs.album.id + "/tracks/" + result.id,
artwork: artwork,
artist: {
name: result.artist_display_name
},
album: {
name: result.refs.album.display
}
});
});
});
}
};
module.exports.search = function(data, next) {
var query;
var type = data.type;
if (type == "album") {
query = data.artist.name + " " + data.name;
} else if (type == "track") {
query = data.artist.name + " " + data.album.name + " " + data.name;
}
var path = "/search?q=" + encodeURIComponent(query) + "&type=" + type + "&client_id=" + credentials.key;
request.get(apiRoot + path, function(res) {
if (!res.body.data[0]) {
next({service: "beats"});
} else {
module.exports.lookupId(res.body.data[0].id, next);
}
});
};
module.exports.parseUrl = function(url, next) {
var matches = parse(url).path.match(/\/albums[\/]+([^\/]+)(\/tracks\/)?([^\/]+)?/);
if (matches && matches[3]) {
module.exports.lookupId(matches[3], next);
} else if (matches && matches[1]) {
module.exports.lookupId(matches[1], next);
}
}

View file

@ -92,13 +92,13 @@ module.exports.search = function(data, next) {
var result = results.filter(function(result) {
if (type == "album" && result.name == data.name) {
return result;
} else if (type == "track" && result.album.name == data.album) {
} else if (type == "track" && result.album == data.album.name) {
return result;
}
}).shift();
console.log(result)
if (!result) {
return next({});
return next({service: "rdio"});
}
var parsed = parse(result.shortUrl)
var id = parsed.path.replace("/x/", "").replace("/", "");

View file

@ -22,7 +22,8 @@
"playmusic": "^1.1.0",
"rdio": "^1.5.2",
"serve-favicon": "~2.1.3",
"spotify": "^0.3.0"
"spotify": "^0.3.0",
"superagent": "^0.21.0"
},
"devDependencies": {
"should": "^4.3.0",

BIN
public/images/beats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -65,6 +65,9 @@ h2 {
.album-artwork {
margin-bottom: 10px;
}
.not-found {
opacity: 0.3;
}
.service-link {
text-align: center;
}

View file

@ -6,6 +6,7 @@ var router = express.Router();
var googleplaymusic = require('../lib/googleplaymusic');
var spotify = require('../lib/spotify');
var rdio = require('../lib/rdio');
var beats = require('../lib/beats');
var cache = {googleplaymusic:{}, spotify:{},rdio:{}};
@ -23,7 +24,10 @@ router.get('/:service/:type/:id', function(req, res) {
items.push(item);
rdio.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
beats.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
});
});
});
});
@ -35,7 +39,10 @@ router.get('/:service/:type/:id', function(req, res) {
items.push(item);
rdio.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
beats.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
});
});
});
});
@ -47,7 +54,25 @@ router.get('/:service/:type/:id', function(req, res) {
items.push(item);
spotify.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
beats.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
});
});
});
});
break;
case "beats":
beats.lookupId(id, function(result) {
items.push(result);
googleplaymusic.search(result, function(item) {
items.push(item);
spotify.search(result, function(item) {
items.push(item);
rdio.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
});
});
});
});
@ -56,7 +81,6 @@ router.get('/:service/:type/:id', function(req, res) {
});
router.post('/search', function(req, res) {
// determine spotify or google music
var url = parse(req.body.url);
if (!url.host) {
@ -90,6 +114,14 @@ router.post('/search', function(req, res) {
res.redirect("/google/" + result.type + "/" + result.id);
}
});
} else if (url.host.match(/beatsmusic\.com$/)) {
beats.parseUrl(url.href, function(result) {
if (!result.id) {
req.flash('search-error', 'No match found for this link');
res.redirect('/');
}
res.redirect("/beats/" + result.type + "/" + result.id);
});
} else {
req.flash('search-error', 'No match found for this link');
res.redirect('/');

View file

@ -32,10 +32,17 @@
<div class="col-md-3 col-xs-6">
<div class="service <%= i==0 ? "source-service" : "" %>">
<div class="matching-from"><%= i==0 ? "Found matches for this link" : "" %></div>
<a href="<%= album.url %>"><img src="<%= album.artwork %>" class="img-rounded album-artwork" width="100%"></a>
<div class="service-link">
<a href="<%= album.url %>">Listen on <img src="/images/<%= album.service %>.png" class="img-rounded"></a>
</div>
<% if (album.url) { %>
<a href="<%= album.url %>"><img src="<%= album.artwork %>" class="img-rounded album-artwork" width="100%"></a>
<div class="service-link">
<a href="<%= album.url %>"><img src="/images/<%= album.service %>.png" class="img-rounded"></a>
</div>
<% } else { %>
<img src="<%= items[0].artwork %>" class="img-rounded album-artwork not-found" width="100%"></a>
<div class="service-link">
<img src="/images/<%= album.service %>.png" class="img-rounded not-found">
</div>
<% } %>
</div>
</div>
<% } %>

View file

@ -32,10 +32,17 @@
<div class="col-md-3 col-xs-6">
<div class="service <%= i==0 ? "source-service" : "" %>">
<div class="matching-from"><%= i==0 ? "Found matches for this link" : "" %></div>
<a href="<%= track.url %>"><img src="<%= track.artwork %>" class="img-rounded album-artwork" width="100%"></a>
<div class="service-link">
<a href="<%= track.url %>">Listen on <img src="/images/<%= track.service %>.png" class="img-rounded"></a>
</div>
<% if (track.url) { %>
<a href="<%= track.url %>"><img src="<%= track.artwork %>" class="img-rounded album-artwork" width="100%"></a>
<div class="service-link">
<a href="<%= track.url %>"><img src="/images/<%= track.service %>.png" class="img-rounded"></a>
</div>
<% } else { %>
<img src="<%= items[0].artwork %>" class="img-rounded album-artwork not-found" width="100%"></a>
<div class="service-link">
<img src="/images/<%= track.service %>.png" class="img-rounded not-found">
</div>
<% } %>
</div>
</div>
<% } %>