From acb129899dc46ffaadcd75a9b138bc68cc222359 Mon Sep 17 00:00:00 2001 From: Jonathan Cremin Date: Wed, 3 Dec 2014 23:03:34 +0000 Subject: [PATCH] Add suppport for tracks, basic design, minor refactor --- README.md | 1 + lib/googleplaymusic.js | 32 +++++++++---- lib/rdio.js | 32 +++++++++---- lib/spotify.js | 52 ++++++++++++++++----- public/images/googleplaymusic.png | Bin 0 -> 7664 bytes public/images/rdio.png | Bin 0 -> 4066 bytes public/images/spotify.png | Bin 0 -> 7761 bytes public/stylesheets/style.css | 75 ++++++++++++++++++++++++++++-- routes/index.js | 41 ++++++++++------ test/test.js | 6 +-- views/album.ejs | 38 +++++++++++---- views/index.ejs | 27 ++++++----- views/track.ejs | 40 ++++++++++++++++ 13 files changed, 267 insertions(+), 77 deletions(-) create mode 100644 public/images/googleplaymusic.png create mode 100644 public/images/rdio.png create mode 100644 public/images/spotify.png create mode 100644 views/track.ejs diff --git a/README.md b/README.md index 05623be..d884662 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,4 @@ 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 diff --git a/lib/googleplaymusic.js b/lib/googleplaymusic.js index 235f76c..eabda90 100644 --- a/lib/googleplaymusic.js +++ b/lib/googleplaymusic.js @@ -14,33 +14,47 @@ module.exports.lookupId = function(id, type, next) { if (type == "album") { pm.getAlbum(id, true, function(album) { next({ + service: "googleplaymusic", + type: "album", id: album.albumId, name: album.name, url: "https://play.google.com/music/listen#/album/" + album.albumId, artwork: album.albumArtRef.replace("http:", ""), artist: { name: album.artist - }, - type: type + } }); }); } else if (type == "track") { pm.getAllAccessTrack(id, function(track) { next({ + service: "googleplaymusic", + type: "track", id: track.nid, name: track.title, url: "https://play.google.com/music/listen#/track/" + track.nid + "/" + track.albumId, artwork: track.albumArtRef[0].url.replace("http:", ""), + album: { + name: track.album + }, 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 var result = data.entries.filter(function(result) { return result[type]; @@ -49,9 +63,9 @@ module.exports.search = function(query, type, next) { }).shift(); var id; - if (result.album) { + if (type == "album") { id = result.album.albumId; - } else if (result.track) { + } else if (type == "track") { id = result.track.nid; } @@ -73,9 +87,7 @@ module.exports.parseUrl = function(url, next) { if (id.length > 0) { return next({id: id, type: type}); } else { - module.exports.search(artist + " " + album, "album", function(googleAlbum) { - next(googleAlbum); - }); + module.exports.search({type: type, name:album, artist: {name: artist}}, next); } } else if(path) { var matches = path.match(/\/music\/m\/([\w]+)/); diff --git a/lib/rdio.js b/lib/rdio.js index 95a6d0f..cfa62cb 100644 --- a/lib/rdio.js +++ b/lib/rdio.js @@ -21,14 +21,15 @@ module.exports.lookupId = function(id, next) { var id = parsed.path.replace("/x/", "").replace("/", ""); var type = result.album ? "track" : "album"; next({ + service: "rdio", + type: type, id: id, name: result.name, url: result.shortUrl, - artwork: result.icon.replace("http:", ""), + artwork: result.icon.replace("http:", "").replace("square-200", "square-500"), artist: { name: result.artist - }, - type: type + } }); }); }; @@ -58,19 +59,29 @@ module.exports.lookupUrl = function(url, next) { var id = parsed.path.replace("/x/", "").replace("/", ""); var type = result.album ? "track" : "album"; next({ + service: "rdio", + type: type, id: id, name: result.name, url: result.shortUrl, - artwork: result.icon.replace("http:", ""), + artwork: result.icon.replace("http:", "").replace("square-200", "square-500"), 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("", "", { query: query, method: 'search', @@ -83,14 +94,15 @@ module.exports.search = function(query, type, next) { var parsed = parse(result.shortUrl) var id = parsed.path.replace("/x/", "").replace("/", ""); next({ + service: "rdio", + type: type, id: id, name: result.name, url: result.shortUrl, - artwork: result.icon.replace("http:", ""), + artwork: result.icon.replace("http:", "").replace("square-200", "square-500"), artist: { name: result.artist - }, - type: type + } }); }); }; diff --git a/lib/spotify.js b/lib/spotify.js index 7b838a9..8359f76 100644 --- a/lib/spotify.js +++ b/lib/spotify.js @@ -11,19 +11,49 @@ module.exports.lookupId = function(id, type, next) { var artist = data.artists[0]; - next({ - 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 - } - }) + if (type == "album") { + 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 + } + }); + } 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) { if ( err ) { console.log('Error occurred: ' + err); @@ -40,6 +70,6 @@ module.exports.parseUrl = function(url, next) { var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); if (matches && matches[2]) { - next({id:matches[2], type: matches[1]}) + module.exports.lookupId(matches[2], matches[1], next); } } diff --git a/public/images/googleplaymusic.png b/public/images/googleplaymusic.png new file mode 100644 index 0000000000000000000000000000000000000000..9d27f909b696ecfb6c06a75b61724b93784441da GIT binary patch literal 7664 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 literal 0 HcmV?d00001 diff --git a/public/images/spotify.png b/public/images/spotify.png new file mode 100644 index 0000000000000000000000000000000000000000..735c71c9c9f8408ff8f561eac69c200a689a80a7 GIT binary patch literal 7761 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 HF!uR>o1`K^ literal 0 HcmV?d00001 diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 30e047d..3c62a06 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,8 +1,73 @@ body { - padding: 50px; - font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; + font-family: "Open Sans"; } -a { - color: #00B7FF; -} \ No newline at end of file +header { + 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; +} diff --git a/routes/index.js b/routes/index.js index 7d4b5a1..cd3f81c 100644 --- a/routes/index.js +++ b/routes/index.js @@ -7,35 +7,47 @@ var googleplaymusic = require('../lib/googleplaymusic'); var spotify = require('../lib/spotify'); var rdio = require('../lib/rdio'); +var cache = {googleplaymusic:{}, spotify:{},rdio:{}}; + router.get('/:service/:type/:id', function(req, res) { var service = req.params.service; var type = req.params.type; var id = req.params.id; + var items = []; switch(service) { case "spotify": - spotify.lookupId(id, type, function(spotifyAlbum) { - googleplaymusic.search(spotifyAlbum.artist.name + " " + spotifyAlbum.name, type, function(googleAlbum) { - rdio.search(googleAlbum.artist.name + " " + googleAlbum.name, type, function(rdioAlbum) { - res.render('album', {rdioAlbum: rdioAlbum, googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum}); + spotify.lookupId(id, type, function(result) { + items.push(result); + googleplaymusic.search(result, function(item) { + items.push(item); + rdio.search(result, function(item) { + items.push(item); + res.render(result.type, {items: items}); }); }); }); break; case "google": - googleplaymusic.lookupId(id, type, function(googleAlbum) { - spotify.search(googleAlbum.artist.name + " " + googleAlbum.name, type, function(spotifyAlbum) { - rdio.search(googleAlbum.artist.name + " " + googleAlbum.name, type, function(rdioAlbum) { - res.render('album', {rdioAlbum: rdioAlbum, googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum}); + googleplaymusic.lookupId(id, type, function(result) { + items.push(result); + spotify.search(result, function(item) { + items.push(item); + rdio.search(result, function(item) { + items.push(item); + res.render(result.type, {items: items}); }); }); }); break; case "rdio": - rdio.lookupId(id, function(rdioAlbum) { - googleplaymusic.search(rdioAlbum.artist.name + " " + rdioAlbum.name, type, function(googleAlbum) { - spotify.search(rdioAlbum.artist.name + " " + rdioAlbum.name, type, function(spotifyAlbum) { - res.render('album', {rdioAlbum: rdioAlbum, googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum}); + rdio.lookupId(id, type, function(result) { + items.push(result); + googleplaymusic.search(result, function(item) { + 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$/)) { googleplaymusic.parseUrl(url.href, function(result) { - if (!result.id) { + if (!result) { req.flash('search-error', 'No match found for this link'); res.redirect('/'); + } else { + res.redirect("/google/" + result.type + "/" + result.id); } - res.redirect("/google/" + result.type + "/" + result.id); }); } else { req.flash('search-error', 'No match found for this link'); diff --git a/test/test.js b/test/test.js index da3267e..3eebf08 100644 --- a/test/test.js +++ b/test/test.js @@ -17,7 +17,7 @@ describe('Spotify', function(){ describe('search', function(){ 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)"); done(); }); @@ -46,7 +46,7 @@ describe('Rdio', function(){ describe('search', function(){ 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)"); done(); }); @@ -82,7 +82,7 @@ describe('Google Play Music', function(){ describe('search', function(){ 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)"); done(); }); diff --git a/views/album.ejs b/views/album.ejs index e4889eb..1d767b3 100644 --- a/views/album.ejs +++ b/views/album.ejs @@ -7,23 +7,41 @@ + - + - + +
+
+
+ +
+
+
- <%= spotifyAlbum.id %> Play on Spotify +
+

<%= items[0].artist.name %> - <%= items[0].name %>

+
- <%= googleAlbum.id %> Play on Google Music + <% for (var i=0;i < items.length;i++) { var album = items[i]; %> +
+
"> +
<%= i==0 ? "Found matches for this album" : "" %>
+ + +
+
+ <% } %>
-
- <%= rdioAlbum.id %> Play on Rdio +
diff --git a/views/index.ejs b/views/index.ejs index 1b56d2c..46f67b1 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -7,32 +7,31 @@ + - + - + +
+

match.audio

+
-
-
+
-
-

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. Here's an example.

+
+

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. + Here's an example.

diff --git a/views/track.ejs b/views/track.ejs new file mode 100644 index 0000000..33aa3bb --- /dev/null +++ b/views/track.ejs @@ -0,0 +1,40 @@ + + + + + + Match Audio + + + + + + + + +
+
+
+

match.audio

+

<%= items[0].artist.name %> - <%= items[0].name %>

+
+
+
+ <% for (var i=0;i < items.length;i++) { var track = items[i]; %> +
+
"> +
<%= i==0 ? "Matching track from this album" : "" %>
+ + +
+
+ <% } %> +
+ +
+ +