diff --git a/lib/services/beats.js b/lib/services/beats/index.js similarity index 82% rename from lib/services/beats.js rename to lib/services/beats/index.js index aec6509..cf9ba5a 100644 --- a/lib/services/beats.js +++ b/lib/services/beats/index.js @@ -17,18 +17,33 @@ var credentials = { var apiRoot = "https://partner.api.beatsmusic.com/v1/api"; -module.exports.match = function(url, type) { - var parsed = parse(url); - return parsed.host.match(/beatsmusic\.com$/); -}; +module.exports.match = require("./url").match; -module.exports.lookupId = function(id) { +module.exports.parseUrl = function(url) { + var deferred = Q.defer(); + var matches = parse(url).path.match(/\/albums[\/]+([^\/]+)(\/tracks\/)?([^\/]+)?/); + if (matches && matches[3]) { + module.exports.lookupId(matches[3], "track").then(deferred.resolve); + } else if (matches && matches[1]) { + module.exports.lookupId(matches[1], "album").then(deferred.resolve); + } else { + deferred.reject(); + } + return deferred.promise; +} + +module.exports.lookupId = function(id, type) { var deferred = Q.defer(); - if (id.substr(0,2) == "al") { + if (type == "album") { 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) { + if (!res.body.data) { + var error = new Error("Not Found"); + error.status = 404; + return deferred.reject(error); + } var result = res.body.data; deferred.resolve({ service: "beats", @@ -44,8 +59,13 @@ module.exports.lookupId = function(id) { }); }); }); - } else if (id.substr(0,2) == "tr") { + } else if (type == "track") { request.get(apiRoot + "/tracks/" + id + "?client_id=" + credentials.key, function(res) { + if (!res.body.data) { + var error = new Error("Not Found"); + error.status = 404; + return deferred.reject(error); + } 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; @@ -103,20 +123,8 @@ module.exports.search = function(data) { deferred.resolve({service: "beats"}); } } else { - module.exports.lookupId(res.body.data[0].id).then(deferred.resolve); + module.exports.lookupId(res.body.data[0].id, type).then(deferred.resolve); } }); return deferred.promise; }; - -module.exports.parseUrl = function(url) { - var deferred = Q.defer(); - var matches = parse(url).path.match(/\/albums[\/]+([^\/]+)(\/tracks\/)?([^\/]+)?/); - - if (matches && matches[3]) { - module.exports.lookupId(matches[3]).then(deferred.resolve); - } else if (matches && matches[1]) { - module.exports.lookupId(matches[1]).then(deferred.resolve); - } - return deferred.promise; -} diff --git a/lib/services/beats/url.js b/lib/services/beats/url.js new file mode 100644 index 0000000..026f540 --- /dev/null +++ b/lib/services/beats/url.js @@ -0,0 +1,11 @@ +"use strict"; +var parse = require('url').parse; + +module.exports.match = function(url) { + var parsed = parse(url); + if (!parsed.host.match(/beatsmusic\.com$/)) { + return false; + } + var matches = parsed.path.match(/\/albums[\/]+([^\/]+)(\/tracks\/)?([^\/]+)?/); + return matches.length > 1; +}; diff --git a/lib/services/deezer.js b/lib/services/deezer/index.js similarity index 96% rename from lib/services/deezer.js rename to lib/services/deezer/index.js index ef08760..7d85506 100644 --- a/lib/services/deezer.js +++ b/lib/services/deezer/index.js @@ -7,10 +7,19 @@ module.exports.id = "deezer"; var apiRoot = "https://api.deezer.com"; -module.exports.match = function(url, type) { - var parsed = parse(url); - return parsed.host.match(/deezer\.com$/); -}; +module.exports.match = require('./url').match; + +module.exports.parseUrl = function(url, next) { + var deferred = Q.defer(); + var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); + + if (matches && matches[2]) { + module.exports.lookupId(matches[2], matches[1]).then(deferred.resolve); + } else { + deferred.reject(); + } + return deferred.promise; +} module.exports.lookupId = function(id, type) { var deferred = Q.defer(); @@ -96,13 +105,3 @@ module.exports.search = function(data, next) { }); return deferred.promise; }; - -module.exports.parseUrl = function(url, next) { - var deferred = Q.defer(); - var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); - - if (matches && matches[2]) { - module.exports.lookupId(matches[2], matches[1]).then(deferred.resolve); - } - return deferred.promise; -} diff --git a/lib/services/deezer/url.js b/lib/services/deezer/url.js new file mode 100644 index 0000000..daea1b7 --- /dev/null +++ b/lib/services/deezer/url.js @@ -0,0 +1,11 @@ +"use strict"; +var parse = require('url').parse; + +module.exports.match = function(url) { + var parsed = parse(url); + if (!parsed.host.match(/deezer\.com$/)) { + return false; + } + var matches = parsed.path.match(/\/(album|track)[\/]+([^\/]+)/); + return matches.length > 1; +}; diff --git a/lib/services/googleplaymusic.js b/lib/services/google/index.js similarity index 93% rename from lib/services/googleplaymusic.js rename to lib/services/google/index.js index 87689a3..a906bf9 100644 --- a/lib/services/googleplaymusic.js +++ b/lib/services/google/index.js @@ -17,10 +17,34 @@ pm.init({email: process.env.GOOGLE_EMAIL, password: process.env.GOOGLE_PASSWORD} ready.resolve(); }); -module.exports.match = function(url, type) { - var parsed = parse(url); - return parsed.host.match(/play\.google\.com$/); -}; +module.exports.match = require('./url').match; + +module.exports.parseUrl = function(url) { + var deferred = Q.defer(); + ready.promise.then(function() { + var parsed = parse(url.replace(/\+/g, "%20")); + var path = parsed.path; + var hash = parsed.hash; + if (hash) { + var parts = hash.split("/"); + var type = parts[1]; + var id = parts[2]; + var artist = decodeURIComponent(parts[3]); + var album = decodeURIComponent(parts[4]); + + if (id.length > 0) { + deferred.resolve({id: id, type: type}); + } else { + module.exports.search({type: type, name:album, artist: {name: artist}}).then(deferred.resolve); + } + } else if(path) { + var matches = path.match(/\/music\/m\/([\w]+)/); + var type = matches[1][0] == "T" ? "track" : "album"; + module.exports.lookupId(matches[1], type).then(deferred.resolve); + } + }); + return deferred.promise; +} module.exports.lookupId = function(id, type, next) { var deferred = Q.defer(); @@ -28,7 +52,7 @@ module.exports.lookupId = function(id, type, next) { if (type == "album") { pm.getAlbum(id, false, function(album) { deferred.resolve({ - service: "googleplaymusic", + service: "google", type: "album", id: album.albumId, name: album.name, @@ -45,7 +69,7 @@ module.exports.lookupId = function(id, type, next) { } else if (type == "track") { pm.getAllAccessTrack(id, function(track) { deferred.resolve({ - service: "googleplaymusic", + service: "google", type: "track", id: track.nid, name: track.title, @@ -104,7 +128,7 @@ module.exports.search = function(data) { }).shift(); if (!result) { - deferred.resolve({service: "googleplaymusic"}); + deferred.resolve({service: "google"}); } else { var id; if (type == "album") { @@ -119,30 +143,3 @@ module.exports.search = function(data) { }); return deferred.promise; } - -module.exports.parseUrl = function(url, next) { - var deferred = Q.defer(); - ready.promise.then(function() { - var parsed = parse(url.replace(/\+/g, "%20")); - var path = parsed.path; - var hash = parsed.hash; - if (hash) { - var parts = hash.split("/"); - var type = parts[1]; - var id = parts[2]; - var artist = decodeURIComponent(parts[3]); - var album = decodeURIComponent(parts[4]); - - if (id.length > 0) { - deferred.resolve({id: id, type: type}); - } else { - module.exports.search({type: type, name:album, artist: {name: artist}}).then(deferred.resolve); - } - } else if(path) { - var matches = path.match(/\/music\/m\/([\w]+)/); - var type = matches[1][0] == "T" ? "track" : "album"; - module.exports.lookupId(matches[1], type).then(deferred.resolve); - } - }); - return deferred.promise; -} diff --git a/lib/services/google/url.js b/lib/services/google/url.js new file mode 100644 index 0000000..8fac38e --- /dev/null +++ b/lib/services/google/url.js @@ -0,0 +1,30 @@ +"use strict"; +var parse = require('url').parse; + +module.exports.match = function(url) { + var parsed = parse(url.replace(/\+/g, "%20")); + if (!parsed.host.match(/play\.google\.com$/)) { + return false; + } + + var path = parsed.path; + var hash = parsed.hash; + + if (hash) { + var parts = hash.split("/"); + var id = parts[2]; + var artist = parts[3]; + + if (id.length > 0) { + return true; + } else if (artist.length > 0) { + return true; + } + } else if(path) { + var matches = path.match(/\/music\/m\/([\w]+)/); + if (matches[1]) { + return true + } + } + return false +}; diff --git a/lib/services/itunes.js b/lib/services/itunes/index.js similarity index 85% rename from lib/services/itunes.js rename to lib/services/itunes/index.js index 0a47290..8f91104 100644 --- a/lib/services/itunes.js +++ b/lib/services/itunes/index.js @@ -1,5 +1,6 @@ "use strict"; var parse = require('url').parse; +var querystring = require('querystring'); var request = require('superagent'); var Q = require('q'); @@ -7,9 +8,26 @@ module.exports.id = "itunes"; var apiRoot = "https://itunes.apple.com"; -module.exports.match = function(url, type) { +module.exports.match = require('./url').match; + +module.exports.parseUrl = function(url) { + var deferred = Q.defer(); var parsed = parse(url); - return parsed.host.match(/itunes.apple\.com$/); + var matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); + var query = querystring.parse(parsed.query); + + if (matches) { + var type = "album"; + var id = matches[3].substr(2); + if (query.i) { + type = "track"; + id = query.i; + } + module.exports.lookupId(id, type, matches[1] || "us").then(deferred.resolve, deferred.reject); + } else { + deferred.reject(); + } + return deferred.promise; }; module.exports.lookupId = function(id, type, cc) { @@ -38,7 +56,7 @@ module.exports.lookupId = function(id, type, cc) { var item = { service: "itunes", type: type, - id: cc + result.collectionId, + id: cc + id, name: result.trackName ? result.trackName : result.collectionName, streamUrl: null, purchaseUrl: result.collectionViewUrl, @@ -58,7 +76,7 @@ module.exports.lookupId = function(id, type, cc) { } }); return deferred.promise; -} +}; module.exports.search = function(data) { var deferred = Q.defer(); @@ -118,16 +136,4 @@ module.exports.search = function(data) { }); return deferred.promise; -} - -module.exports.parseUrl = function(url) { - var deferred = Q.defer(); - var matches = parse(url).path.match(/[\/]?([\/]?[a-z]{2}?)?\/(album|track)[\/]+([^\/]+)[\/]+([^\?]+)/); - - if (matches && matches[4]) { - module.exports.lookupId(matches[4].substr(2), matches[2], matches[1]).then(deferred.resolve, deferred.reject); - } else if (matches[3]) { - module.exports.lookupId(matches[3].substr(2), matches[1], "").then(deferred.resolve, deferred.reject); - } - return deferred.promise; -} +}; diff --git a/lib/services/itunes/url.js b/lib/services/itunes/url.js new file mode 100644 index 0000000..72172da --- /dev/null +++ b/lib/services/itunes/url.js @@ -0,0 +1,15 @@ +"use strict"; +var parse = require('url').parse; + +module.exports.match = function(url, type) { + var parsed = parse(url); + + if (!parsed.host.match(/itunes.apple\.com$/)) { + return false; + } + + var matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); + var query = querystring.parse(parsed.query); + + return !!matches[3]; +}; diff --git a/lib/services/rdio.js b/lib/services/rdio/index.js similarity index 95% rename from lib/services/rdio.js rename to lib/services/rdio/index.js index 494bb91..a99eed6 100644 --- a/lib/services/rdio.js +++ b/lib/services/rdio/index.js @@ -14,10 +14,7 @@ var rdio = require('rdio')({ rdio_api_shared: process.env.RDIO_API_SHARED, }); -module.exports.match = function(url, type) { - var parsed = parse(url); - return parsed.host.match(/rd\.io$/) || parsed.host.match(/rdio\.com$/); -}; +module.exports.match = require('./url').match; module.exports.lookupId = function(id) { var deferred = Q.defer(); @@ -76,7 +73,7 @@ module.exports.parseUrl = function(url) { } else { var error = new Error("Not Found"); error.status = 404; - deferred.reject(error); + return deferred.reject(error); } rdio.api("", "", data, function(err, results) { @@ -85,7 +82,7 @@ module.exports.parseUrl = function(url) { if (!result || results.status != "ok") { var error = new Error("Not Found"); error.status = 404; - deferred.reject(error); + return deferred.reject(error); } else { var parsed = parse(result.shortUrl) var id = parsed.path.replace("/x/", "").replace("/", ""); diff --git a/lib/services/rdio/url.js b/lib/services/rdio/url.js new file mode 100644 index 0000000..173fecf --- /dev/null +++ b/lib/services/rdio/url.js @@ -0,0 +1,11 @@ +"use strict"; +var parse = require('url').parse; + +module.exports.match = function(url) { + var parsed = parse(url); + if (!parsed.host.match(/rd\.io$/) && !parsed.host.match(/rdio\.com$/)) { + return false; + } + var matches = parsed.path.match(/[\/]*artist[\/]*([^\/]*)[\/]*album[\/]*([^\/]*)[\/]*([track]*)?[\/]*([^\/]*)/); + return !!matches[2]; +}; diff --git a/lib/services/spotify.js b/lib/services/spotify/index.js similarity index 96% rename from lib/services/spotify.js rename to lib/services/spotify/index.js index 39625b4..f253f62 100644 --- a/lib/services/spotify.js +++ b/lib/services/spotify/index.js @@ -5,10 +5,17 @@ var Q = require('q'); module.exports.id = "spotify"; -module.exports.match = function(url, type) { - var parsed = parse(url); - return parsed.host.match(/spotify\.com$/); -}; +module.exports.match = require('./url').match; + +module.exports.parseUrl = function(url) { + var deferred = Q.defer(); + var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); + + if (matches && matches[2]) { + module.exports.lookupId(matches[2], matches[1]).then(deferred.resolve); + } + return deferred.promise; +} module.exports.lookupId = function(id, type) { var deferred = Q.defer(); @@ -95,13 +102,3 @@ module.exports.search = function(data) { }); return deferred.promise; } - -module.exports.parseUrl = function(url) { - var deferred = Q.defer(); - var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); - - if (matches && matches[2]) { - module.exports.lookupId(matches[2], matches[1]).then(deferred.resolve); - } - return deferred.promise; -} diff --git a/lib/services/spotify/url.js b/lib/services/spotify/url.js new file mode 100644 index 0000000..130bfff --- /dev/null +++ b/lib/services/spotify/url.js @@ -0,0 +1,12 @@ +"use strict"; +var parse = require('url').parse; + +module.exports.match = function(url, type) { + var parsed = parse(url); + if (!parsed.host.match(/spotify\.com$/)) { + return false; + } + + var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); + return !!matches[2]; +}; diff --git a/lib/services/youtube.js b/lib/services/youtube/index.js similarity index 91% rename from lib/services/youtube.js rename to lib/services/youtube/index.js index f1b0f39..76a40f5 100644 --- a/lib/services/youtube.js +++ b/lib/services/youtube/index.js @@ -16,9 +16,7 @@ var credentials = { var apiRoot = "https://www.googleapis.com/youtube/v3"; -module.exports.match = function(url, type) { - return false; -}; +module.exports.match = require('./url').match; module.exports.search = function(data) { var deferred = Q.defer(); @@ -33,7 +31,7 @@ module.exports.search = function(data) { album = data.album.name } - var path = "/search?part=snippet&q=" + encodeURIComponent(query) + "&type=video&videoCaption=any&key=" + credentials.key; + var path = "/search?part=snippet&q=" + encodeURIComponent(query) + "&type=video&videoCaption=any&videoCategoryId=10&key=" + credentials.key; request.get(apiRoot + path, function(res) { var result = res.body.items[0]; diff --git a/lib/services/youtube/url.js b/lib/services/youtube/url.js new file mode 100644 index 0000000..4a82c7e --- /dev/null +++ b/lib/services/youtube/url.js @@ -0,0 +1,5 @@ +"use strict"; + +module.exports.match = function(url, type) { + return false; +}; diff --git a/routes/search.js b/routes/search.js index 8e45776..dd3621e 100644 --- a/routes/search.js +++ b/routes/search.js @@ -36,7 +36,7 @@ module.exports = function(req, res, next) { error = new Error("Error talking to music service"); error.status = "502"; next(error); - } else if (!error.status) { + } else if (!error || !error.status) { error = new Error("An unexpected error happenend"); error.status = 500; next(error); diff --git a/test/services/beats.js b/test/services/beats.js index c5776ba..af990d9 100644 --- a/test/services/beats.js +++ b/test/services/beats.js @@ -7,14 +7,14 @@ var beats = require("../../lib/services/beats"); describe('Beats Music', function(){ describe('lookupId', function(){ it('should find album by ID', function(done){ - beats.lookupId("al920431").then(function(result) { + beats.lookupId("al920431", "album").then(function(result) { result.name.should.equal("Deftones"); done(); }); }); it('should find track by ID', function(done){ - beats.lookupId("tr6910289").then(function(result) { + beats.lookupId("tr6910289", "track").then(function(result) { result.name.should.equal("Californication"); done(); }); diff --git a/test/services/googleplaymusic.js b/test/services/google.js similarity index 59% rename from test/services/googleplaymusic.js rename to test/services/google.js index a16f1fb..ce44397 100644 --- a/test/services/googleplaymusic.js +++ b/test/services/google.js @@ -2,12 +2,12 @@ var assert = require("assert"); var should = require('should'); -var googleplaymusic = require("../../lib/services/googleplaymusic"); +var google = require("../../lib/services/google"); describe('Google Play Music', function(){ describe('lookupId', function(){ it('should find album by ID', function(done){ - googleplaymusic.lookupId("Byp6lvzimyf74wxi5634ul4tgam", "album").then(function(result) { + google.lookupId("Byp6lvzimyf74wxi5634ul4tgam", "album").then(function(result) { result.name.should.equal("Listen (Deluxe)"); done(); }); @@ -16,7 +16,7 @@ describe('Google Play Music', function(){ describe('search', function(){ it('should find album by search', function(done){ - googleplaymusic.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}).then(function(result) { + google.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}).then(function(result) { result.name.should.equal("Listen (Deluxe)"); done(); }); @@ -25,21 +25,21 @@ describe('Google Play Music', function(){ describe('lookupUrl', function(){ it('should parse regular url into album ID', function(done){ - googleplaymusic.parseUrl("https://play.google.com/music/listen#/album/Byp6lvzimyf74wxi5634ul4tgam/David+Guetta/Listen+(Deluxe)").then(function(result) { + google.parseUrl("https://play.google.com/music/listen#/album/Byp6lvzimyf74wxi5634ul4tgam/David+Guetta/Listen+(Deluxe)").then(function(result) { result.id.should.equal("Byp6lvzimyf74wxi5634ul4tgam"); done(); }); }); it('should parse url without ID into album ID', function(done){ - googleplaymusic.parseUrl("https://play.google.com/music/listen#/album//David+Guetta/Listen+(Deluxe)").then(function(result) { + google.parseUrl("https://play.google.com/music/listen#/album//David+Guetta/Listen+(Deluxe)").then(function(result) { result.id.should.equal("Byp6lvzimyf74wxi5634ul4tgam"); done(); }); }); it('should parse share url into album ID', function(done){ - googleplaymusic.parseUrl("https://play.google.com/music/m/Byp6lvzimyf74wxi5634ul4tgam").then(function(result) { + google.parseUrl("https://play.google.com/music/m/Byp6lvzimyf74wxi5634ul4tgam").then(function(result) { result.id.should.equal("Byp6lvzimyf74wxi5634ul4tgam"); done(); }); diff --git a/test/services/itunes.js b/test/services/itunes.js new file mode 100644 index 0000000..de119a5 --- /dev/null +++ b/test/services/itunes.js @@ -0,0 +1,57 @@ +"use strict"; +var assert = require("assert"); +var should = require('should'); + +var itunes = require("../../lib/services/itunes"); + +describe('iTunes Music', function(){ + describe('lookupId', function(){ + it('should find album by ID', function(done){ + itunes.lookupId("id215206912").then(function(result) { + result.name.should.equal("Peace Orchestra"); + done(); + }); + }); + + it('should find track by ID', function(done){ + itunes.lookupId("id215206958").then(function(result) { + result.name.should.equal("Double Drums"); + done(); + }); + }); + }); + + describe('search', function(){ + it('should find album by search', function(done){ + itunes.search({type: "album", artist: {name: "Deftones"}, name: "Deftones"}).then(function(result) { + result.name.should.equal("Deftones"); + done(); + }); + }); + + it('should find track by search', function(done){ + itunes.search({type: "track", artist: {name: "Deftones"}, album: {name: "Deftones"}, name: "Hexagram"}).then(function(result) { + result.name.should.equal("Hexagram"); + done(); + }); + }); + }); + + describe('lookupUrl', function(){ + describe('parseUrl', function(){ + it('should parse album url into ID', function(done){ + itunes.parseUrl("https://itunes.apple.com/us/album/double-drums/id215206912").then(function(result) { + result.id.should.equal("us215206912"); + done(); + }); + }); + + it('should parse track url into ID', function(done){ + itunes.parseUrl("https://itunes.apple.com/us/album/double-drums/id215206912?i=215206958&uo=4").then(function(result) { + result.id.should.equal("us215206958"); + done(); + }); + }); + }); + }); +}); diff --git a/test/services/youtube.js b/test/services/youtube.js new file mode 100644 index 0000000..843e504 --- /dev/null +++ b/test/services/youtube.js @@ -0,0 +1,16 @@ +"use strict"; +var assert = require("assert"); +var should = require('should'); + +var youtube = require("../../lib/services/youtube"); + +describe('Youtube', function(){ + describe('search', function(){ + it('should find album by search', function(done){ + youtube.search({type: "track", artist: {name: "Aesop Rock"}, album: {name: "Skelethon"}, name: "Zero Dark Thirty"}).then(function(result) { + result.name.should.equal("Aesop Rock - Zero Dark Thirty"); + done(); + }); + }); + }); +});