Add suppport for tracks, basic design, minor refactor

This commit is contained in:
Jonathan Cremin 2014-12-03 23:03:34 +00:00
parent e20b3b86c4
commit acb129899d
13 changed files with 267 additions and 77 deletions

View file

@ -13,3 +13,4 @@ On the immediate todo list:
* Add support for tracks, and maybe artists * Add support for tracks, and maybe artists
* Use album release year for additional sanity check on matches * Use album release year for additional sanity check on matches
* Do some kind of a design, particularly for the share page * Do some kind of a design, particularly for the share page
* Handle expected and unexpected errors better than the current crash-fest

View file

@ -14,33 +14,47 @@ module.exports.lookupId = function(id, type, next) {
if (type == "album") { if (type == "album") {
pm.getAlbum(id, true, function(album) { pm.getAlbum(id, true, function(album) {
next({ next({
service: "googleplaymusic",
type: "album",
id: album.albumId, id: album.albumId,
name: album.name, name: album.name,
url: "https://play.google.com/music/listen#/album/" + album.albumId, url: "https://play.google.com/music/listen#/album/" + album.albumId,
artwork: album.albumArtRef.replace("http:", ""), artwork: album.albumArtRef.replace("http:", ""),
artist: { artist: {
name: album.artist name: album.artist
}, }
type: type
}); });
}); });
} else if (type == "track") { } else if (type == "track") {
pm.getAllAccessTrack(id, function(track) { pm.getAllAccessTrack(id, function(track) {
next({ next({
service: "googleplaymusic",
type: "track",
id: track.nid, id: track.nid,
name: track.title, name: track.title,
url: "https://play.google.com/music/listen#/track/" + track.nid + "/" + track.albumId, url: "https://play.google.com/music/listen#/track/" + track.nid + "/" + track.albumId,
artwork: track.albumArtRef[0].url.replace("http:", ""), artwork: track.albumArtRef[0].url.replace("http:", ""),
album: {
name: track.album
},
artist: { artist: {
name: track.artist name: track.artist
}, }
type: type
}); });
}); });
} }
} }
module.exports.search = function(query, type, next) { 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;
}
pm.search(query, 5, function(data) { // max 5 results pm.search(query, 5, function(data) { // max 5 results
var result = data.entries.filter(function(result) { var result = data.entries.filter(function(result) {
return result[type]; return result[type];
@ -49,9 +63,9 @@ module.exports.search = function(query, type, next) {
}).shift(); }).shift();
var id; var id;
if (result.album) { if (type == "album") {
id = result.album.albumId; id = result.album.albumId;
} else if (result.track) { } else if (type == "track") {
id = result.track.nid; id = result.track.nid;
} }
@ -73,9 +87,7 @@ module.exports.parseUrl = function(url, next) {
if (id.length > 0) { if (id.length > 0) {
return next({id: id, type: type}); return next({id: id, type: type});
} else { } else {
module.exports.search(artist + " " + album, "album", function(googleAlbum) { module.exports.search({type: type, name:album, artist: {name: artist}}, next);
next(googleAlbum);
});
} }
} else if(path) { } else if(path) {
var matches = path.match(/\/music\/m\/([\w]+)/); var matches = path.match(/\/music\/m\/([\w]+)/);

View file

@ -21,14 +21,15 @@ module.exports.lookupId = function(id, next) {
var id = parsed.path.replace("/x/", "").replace("/", ""); var id = parsed.path.replace("/x/", "").replace("/", "");
var type = result.album ? "track" : "album"; var type = result.album ? "track" : "album";
next({ next({
service: "rdio",
type: type,
id: id, id: id,
name: result.name, name: result.name,
url: result.shortUrl, url: result.shortUrl,
artwork: result.icon.replace("http:", ""), artwork: result.icon.replace("http:", "").replace("square-200", "square-500"),
artist: { artist: {
name: result.artist name: result.artist
}, }
type: type
}); });
}); });
}; };
@ -58,19 +59,29 @@ module.exports.lookupUrl = function(url, next) {
var id = parsed.path.replace("/x/", "").replace("/", ""); var id = parsed.path.replace("/x/", "").replace("/", "");
var type = result.album ? "track" : "album"; var type = result.album ? "track" : "album";
next({ next({
service: "rdio",
type: type,
id: id, id: id,
name: result.name, name: result.name,
url: result.shortUrl, url: result.shortUrl,
artwork: result.icon.replace("http:", ""), artwork: result.icon.replace("http:", "").replace("square-200", "square-500"),
artist: { artist: {
name: result.artist name: result.artist
}, }
type: type
}); });
}); });
}; };
module.exports.search = function(query, type, next) { 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;
}
console.log(query)
rdio.api("", "", { rdio.api("", "", {
query: query, query: query,
method: 'search', method: 'search',
@ -83,14 +94,15 @@ module.exports.search = function(query, type, next) {
var parsed = parse(result.shortUrl) var parsed = parse(result.shortUrl)
var id = parsed.path.replace("/x/", "").replace("/", ""); var id = parsed.path.replace("/x/", "").replace("/", "");
next({ next({
service: "rdio",
type: type,
id: id, id: id,
name: result.name, name: result.name,
url: result.shortUrl, url: result.shortUrl,
artwork: result.icon.replace("http:", ""), artwork: result.icon.replace("http:", "").replace("square-200", "square-500"),
artist: { artist: {
name: result.artist name: result.artist
}, }
type: type
}); });
}); });
}; };

View file

@ -11,19 +11,49 @@ module.exports.lookupId = function(id, type, next) {
var artist = data.artists[0]; var artist = data.artists[0];
next({ if (type == "album") {
id: data.id, next({
name: data.name, service: "spotify",
url: "https://play.spotify.com/" + type + "/" + data.id, type: type,
artwork: data.images ? data.images[0].url.replace("http:", "") : data.album.images[0].url.replace("http:", ""), id: data.id,
artist: { name: data.name,
name: artist.name url: "https://play.spotify.com/" + type + "/" + data.id,
} artwork: data.images ? data.images[0].url.replace("http:", "") : data.album.images[0].url.replace("http:", ""),
}) artist: {
name: artist.name
}
});
} else if (type == "track") {
next({
service: "spotify",
type: type,
id: data.id,
name: data.name,
url: "https://play.spotify.com/" + type + "/" + data.id,
artwork: data.images ? data.images[0].url.replace("http:", "") : data.album.images[0].url.replace("http:", ""),
artist: {
name: artist.name
},
album: {
name: data.album.name
}
})
}
}); });
} }
module.exports.search = function(query, type, next) { 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;
}
query = query.replace(":", "");
spotify.search({query: query, type: type}, function(err, data) { spotify.search({query: query, type: type}, function(err, data) {
if ( err ) { if ( err ) {
console.log('Error occurred: ' + err); console.log('Error occurred: ' + err);
@ -40,6 +70,6 @@ module.exports.parseUrl = function(url, next) {
var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/);
if (matches && matches[2]) { if (matches && matches[2]) {
next({id:matches[2], type: matches[1]}) module.exports.lookupId(matches[2], matches[1], next);
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
public/images/rdio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

BIN
public/images/spotify.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -1,8 +1,73 @@
body { body {
padding: 50px; font-family: "Open Sans";
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
} }
a { header {
color: #00B7FF; background: #e6832e;
} }
header h1 {
margin: 0;
text-align: center;
font-weight: 300;
font-size: 2em;
padding: 20px 0;
}
header h1 a {
color: #fff;
}
header h1 a:hover {
color: #f2c4ad;
text-decoration: none;
}
.share header h1 {
text-align: left;
font-size: 1.5em;
padding: 10px 0;
}
.audio-lighten {
color:#f2c4ad;
}
h2 {
font-size: 1.5em;
font-weight: 700;
margin: 40px 0;
}
.share-form {
margin: 50px 0;
text-align: center;
}
.service {
padding: 40px 10px 10px 10px;
}
.matching-from {
position: absolute;
top: 10px;
left: 25px;
}
.source-service {
background: #eee;
}
.album-artwork {
margin-bottom: 10px;
}
.service-link {
text-align: center;
}
.service-link a {
font-size: 1.8em;
color: #444;
}
.service-link a:hover {
text-decoration: none;
}
.service-link img {
margin-bottom: 7px;
}

View file

@ -7,35 +7,47 @@ var googleplaymusic = require('../lib/googleplaymusic');
var spotify = require('../lib/spotify'); var spotify = require('../lib/spotify');
var rdio = require('../lib/rdio'); var rdio = require('../lib/rdio');
var cache = {googleplaymusic:{}, spotify:{},rdio:{}};
router.get('/:service/:type/:id', function(req, res) { router.get('/:service/:type/:id', function(req, res) {
var service = req.params.service; var service = req.params.service;
var type = req.params.type; var type = req.params.type;
var id = req.params.id; var id = req.params.id;
var items = [];
switch(service) { switch(service) {
case "spotify": case "spotify":
spotify.lookupId(id, type, function(spotifyAlbum) { spotify.lookupId(id, type, function(result) {
googleplaymusic.search(spotifyAlbum.artist.name + " " + spotifyAlbum.name, type, function(googleAlbum) { items.push(result);
rdio.search(googleAlbum.artist.name + " " + googleAlbum.name, type, function(rdioAlbum) { googleplaymusic.search(result, function(item) {
res.render('album', {rdioAlbum: rdioAlbum, googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum}); items.push(item);
rdio.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
}); });
}); });
}); });
break; break;
case "google": case "google":
googleplaymusic.lookupId(id, type, function(googleAlbum) { googleplaymusic.lookupId(id, type, function(result) {
spotify.search(googleAlbum.artist.name + " " + googleAlbum.name, type, function(spotifyAlbum) { items.push(result);
rdio.search(googleAlbum.artist.name + " " + googleAlbum.name, type, function(rdioAlbum) { spotify.search(result, function(item) {
res.render('album', {rdioAlbum: rdioAlbum, googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum}); items.push(item);
rdio.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
}); });
}); });
}); });
break; break;
case "rdio": case "rdio":
rdio.lookupId(id, function(rdioAlbum) { rdio.lookupId(id, type, function(result) {
googleplaymusic.search(rdioAlbum.artist.name + " " + rdioAlbum.name, type, function(googleAlbum) { items.push(result);
spotify.search(rdioAlbum.artist.name + " " + rdioAlbum.name, type, function(spotifyAlbum) { googleplaymusic.search(result, function(item) {
res.render('album', {rdioAlbum: rdioAlbum, googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum}); items.push(item);
spotify.search(result, function(item) {
items.push(item);
res.render(result.type, {items: items});
}); });
}); });
}); });
@ -65,11 +77,12 @@ router.post('/search', function(req, res) {
}); });
} else if (url.host.match(/play\.google\.com$/)) { } else if (url.host.match(/play\.google\.com$/)) {
googleplaymusic.parseUrl(url.href, function(result) { googleplaymusic.parseUrl(url.href, function(result) {
if (!result.id) { if (!result) {
req.flash('search-error', 'No match found for this link'); req.flash('search-error', 'No match found for this link');
res.redirect('/'); res.redirect('/');
} else {
res.redirect("/google/" + result.type + "/" + result.id);
} }
res.redirect("/google/" + result.type + "/" + result.id);
}); });
} else { } else {
req.flash('search-error', 'No match found for this link'); req.flash('search-error', 'No match found for this link');

View file

@ -17,7 +17,7 @@ describe('Spotify', function(){
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function(done){
spotify.search("David Guetta Listen (Deluxe)", "album", function(result) { spotify.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}, function(result) {
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal("Listen (Deluxe)");
done(); done();
}); });
@ -46,7 +46,7 @@ describe('Rdio', function(){
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function(done){
rdio.search("David Guetta Listen (Deluxe)", "album", function(result) { rdio.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}, function(result) {
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal("Listen (Deluxe)");
done(); done();
}); });
@ -82,7 +82,7 @@ describe('Google Play Music', function(){
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function(done){
googleplaymusic.search("David Guetta Listen (Deluxe)", "album", function(result) { googleplaymusic.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}, function(result) {
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal("Listen (Deluxe)");
done(); done();
}); });

View file

@ -7,23 +7,41 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<style> <link rel="stylesheet" href="/stylesheets/style.css">
.container {
padding-top: 100px;
}
</style>
</head> </head>
<body> <body class="album share">
<header>
<div class="container">
<div class="row">
<div class="col-md-12">
<h1><a href="/">match<span class="audio-lighten">.audio</span></a></h1>
</div>
</div>
</div>
</header>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<img src="<%= spotifyAlbum.artwork %>" height=200 class="img-rounded" /> <%= spotifyAlbum.id %> <a href="<%= spotifyAlbum.url %>">Play on Spotify</a> <div class="col-md-12">
<h2><%= items[0].artist.name %> - <%= items[0].name %></h2>
</div>
</div> </div>
<div class="row"> <div class="row">
<img src="<%= googleAlbum.artwork %>" height=200 class="img-rounded" /> <%= googleAlbum.id %> <a href="<%= googleAlbum.url %>">Play on Google Music</a> <% for (var i=0;i < items.length;i++) { var album = items[i]; %>
<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 album" : "" %></div>
<img src="<%= album.artwork %>" class="img-rounded album-artwork" width="100%">
<div class="service-link">
<a href="<%= album.url %>">Listen on <img src="/images/<%= album.service %>.png" class="img-rounded"></a>
</div>
</div>
</div>
<% } %>
</div> </div>
<div class="row"> <div class="row share">
<img src="<%= rdioAlbum.artwork %>" height=200 class="img-rounded" /> <%= rdioAlbum.id %> <a href="<%= rdioAlbum.url %>">Play on Rdio</a>
</div> </div>
</div> </div>
</body> </body>

View file

@ -7,32 +7,31 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<style> <link rel="stylesheet" href="/stylesheets/style.css">
.container {
padding-top: 100px;
}
.form-inline {
padding-bottom: 100px;
}
</style>
</head> </head>
<body> <body class="home">
<header>
<h1><a href="/">match<span class="audio-lighten">.audio</span></a></h1>
</header>
<div class="container"> <div class="container">
<div class="row"> <div class="row share-form">
<div class="col-sm-6 col-sm-offset-3"> <div class="col-md-6 col-md-offset-3">
<%= error %> <%= error %>
<form role="form" method="post" action="/search" class="form-inline"> <form role="form" method="post" action="/search" class="form-inline">
<div class="form-group form-group-lg"> <div class="form-group form-group-lg">
<input type="text" name="url" placeholder="Paste link here" class="form-control"> <input type="text" name="url" placeholder="Paste link here" class="form-control">
<input type="submit" class="btn btn-default btn-lg" value="Share Music">
</div> </div>
<input type="submit" class="btn btn-default btn-lg" value="Share Music">
</form> </form>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-6 col-sm-offset-3"> <div class="col-md-6 col-md-offset-3">
<p>Make sharing music from subscription services better. Give us one link (Rdio, Spotify or Google Music) and we'll match it with other services and give you back a link with all of them. <a href="/google/album/B3qrtsvk5s3piwyla76sk6qyxny">Here's an example</a>.</p> <p>Make sharing music from subscription services better.
We match links from Rdio, Spotify and Google Music and give you back a link with all of them.
<a href="/google/album/B3qrtsvk5s3piwyla76sk6qyxny">Here's an example</a>.</p>
</div> </div>
</div> </div>
</div> </div>

40
views/track.ejs Normal file
View file

@ -0,0 +1,40 @@
<!doctype html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Match Audio</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body class="track share">
<div class="container">
<div class="row">
<div class="col-md-12">
<h2><a href="/">match.audio</a></h2>
<h1><%= items[0].artist.name %> - <%= items[0].name %></h1>
</div>
</div>
<div class="row">
<% for (var i=0;i < items.length;i++) { var track = items[i]; %>
<div class="col-md-3 col-xs-6">
<div class="service <%= i==0 ? "source-service" : "" %>">
<div class="matching-from"><%= i==0 ? "Matching track from this album" : "" %></div>
<img src="<%= track.artwork %>" class="img-rounded album-artwork" width="100%">
<div class="service-link">
<a href="<%= track.url %>">Listen on <img src="/images/<%= track.service %>.png" class="img-rounded"></a>
</div>
</div>
</div>
<% } %>
</div>
<div class="row share">
</div>
</div>
</body>
</html>