diff --git a/app.js b/app.js index 9bc11cc..cea09cb 100644 --- a/app.js +++ b/app.js @@ -105,7 +105,7 @@ app.use(function(req, res, next) { // will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { - console.log(err) + console.log(err.stack) res.status(err.status || 500); var content = React.renderToString(ErrorView({status: err.status || 500, message: err.message, error: err})); diff --git a/lib/lookup.js b/lib/lookup.js index 7776710..06ed6dc 100644 --- a/lib/lookup.js +++ b/lib/lookup.js @@ -25,5 +25,4 @@ module.exports = function(url) { }); }); } - return false; }; diff --git a/lib/services/rdio/index.js b/lib/services/rdio/index.js index f227620..1714890 100644 --- a/lib/services/rdio/index.js +++ b/lib/services/rdio/index.js @@ -120,7 +120,11 @@ module.exports.search = function(data) { albumClean = data.name.match(/([^\(\[]+)/)[0]; } else if (type == "track") { query = data.artist.name + " " + data.album.name + " " + data.name; - albumClean = data.album.name.match(/([^\(\[]+)/)[0]; + try { + albumClean = data.album.name.match(/([^\(\[]+)/)[0]; + } catch(e) { + albumClean = ""; + } } return rdio.apiAsync("", "", {query: query, method: 'search', types: type}).then(function(results) { @@ -129,14 +133,14 @@ module.exports.search = function(data) { var result = results.filter(function(result) { if (type == "album" && result.name.match(/([^\(\[]+)/)[0] == albumClean) { return result; - } else if (type == "track" && result.album.match(/([^\(\[]+)/)[0] == albumClean) { + } else if (type == "track" && (result.album.match(/([^\(\[]+)/)[0] == albumClean || !albumClean)) { return result; } }).shift(); if (!result) { var matches = albumClean.match(/^[^\(\[]+/); - if (matches[0] && matches[0] != albumClean) { + if (matches && matches[0] && matches[0] != albumClean) { var cleanedData = JSON.parse(JSON.stringify(data)); if (type == "album") { cleanedData.name = matches[0].trim(); diff --git a/lib/services/spotify/index.js b/lib/services/spotify/index.js index b11fa49..3f4abd3 100644 --- a/lib/services/spotify/index.js +++ b/lib/services/spotify/index.js @@ -72,14 +72,14 @@ module.exports.search = function(data) { query = "artist:" + data.artist.name.replace(":", "") + " album:" + data.name.replace(":", ""); album = data.name; } else if (type == "track") { - query = "artist:" + data.artist.name.replace(":", "") + " album:" + data.album.name.replace(":", "") + " track:" + data.name.replace(":", ""); + query = "artist:" + data.artist.name.replace(":", "") + " track:" + data.name.replace(":", "") + ( data.album.name.length > 0 ? " album: " + data.album.name.replace(":", ""): ""); album = data.album.name; } return spotify.searchAsync({query: query, type: type}).then(function(results) { if (!results[type + "s"].items[0]) { var matches = album.match(/^[^\(\[]+/); - if (matches[0] && matches[0] != album) { + if (matches && matches[0] && matches[0] != album) { var cleanedData = JSON.parse(JSON.stringify(data)); if (type == "album") { cleanedData.name = matches[0].trim(); diff --git a/lib/services/youtube/freebase.js b/lib/services/youtube/freebase.js new file mode 100644 index 0000000..fffc02e --- /dev/null +++ b/lib/services/youtube/freebase.js @@ -0,0 +1,20 @@ +"use strict"; +var parse = require('url').parse; +var Promise = require('bluebird'); +var request = require('superagent'); +require('superagent-bluebird-promise'); + +var credentials = { + key: process.env.YOUTUBE_KEY, +}; + +var apiRoot = "https://www.googleapis.com/freebase/v1/topic"; + +module.exports.get = function(topic) { + return request.get(apiRoot + topic + "?key=" + credentials.key).promise().then(function(res) { + return res.body; + }) +} + + +module.exports.get("/m/0dwcrm_"); \ No newline at end of file diff --git a/lib/services/youtube/index.js b/lib/services/youtube/index.js index b664896..63746af 100644 --- a/lib/services/youtube/index.js +++ b/lib/services/youtube/index.js @@ -1,5 +1,7 @@ "use strict"; var parse = require('url').parse; +var freebase = require('./freebase'); +var querystring = require('querystring'); var Promise = require('bluebird'); var request = require('superagent'); require('superagent-bluebird-promise'); @@ -19,6 +21,65 @@ var apiRoot = "https://www.googleapis.com/youtube/v3"; module.exports.match = require('./url').match; +module.exports.parseUrl = function(url) { + var parsed = parse(url); + var query = querystring.parse(parsed.query); + var id = query.v; + + if (!id) { + id = parsed.path.substr(1); + if (!id) { + throw new Error(); + } + } + return module.exports.lookupId(id, "track"); +} + +module.exports.lookupId = function(id, type) { + + var path = "/videos?part=snippet%2Cstatus%2CtopicDetails&id=" + id + "&key=" + credentials.key; + + return request.get(apiRoot + path).promise().then(function(res) { + var item = res.body.items[0]; + if (item.topicDetails.topicIds) { + var promises = []; + item.topicDetails.topicIds.forEach(function(topicId) { + promises.push(freebase.get(topicId).then(function(topic) { + return topic.property["/music/recording/song"] ? topic : false; + }, function(err) { + console.log(err) + })); + }) + return Promise.all(promises).then(function(topics) { + for (var key in topics) { + var topic = topics[key]; + if (topic) { + console.log(topic.property['/music/recording/song']) + return { + id: id, + service: "youtube", + type: "track", + name: topic.property['/music/recording/song'].values[0].text, + artist: {name: topic.property['/music/recording/artist'].values[0].text}, + album: {name: ""}, + streamUrl: "https://youtu.be/" + id, + purchaseUrl: null, + artwork: { + small: item.snippet.thumbnails.medium.url, + large: item.snippet.thumbnails.high.url, + } + } + } + } + }); + } else { + return {service: "youtube"}; + } + }, function(res) { + return {service: "youtube"}; + }); +}; + module.exports.search = function(data) { var query, album; var type = data.type; diff --git a/lib/services/youtube/url.js b/lib/services/youtube/url.js index 4a82c7e..14fc82f 100644 --- a/lib/services/youtube/url.js +++ b/lib/services/youtube/url.js @@ -1,5 +1,15 @@ "use strict"; +var parse = require("url").parse; +var querystring = require('querystring'); module.exports.match = function(url, type) { + var parsed = parse(url); + + if (parsed.host.match(/youtu\.be$/)) { + return true; + } else if (parsed.host.match(/youtube\.com$/)) { + var query = querystring.parse(parsed.query); + return !!query.v; + } return false; }; diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 854b605..e424803 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -105,6 +105,10 @@ h3 { margin-bottom: 30px; } +.share-form .alert { + margin-top: 20px; +} + .btn-custom { background-color: #FE4365; } diff --git a/routes/search.js b/routes/search.js index 3102cb4..8f1a5e5 100644 --- a/routes/search.js +++ b/routes/search.js @@ -10,7 +10,16 @@ module.exports = function(req, res, next) { return res.json({error:{message:"You need to submit a url."}}); } - lookup(req.body.url).then(function(item) { + var promise = lookup(req.body.url); + + if (!promise) { + return res.json({error: {message: "No supported music found at that link :("}}); + } + + promise.then(function(item) { + if (!item) { + return res.json({error: {message: "No supported music found at that link :("}}); + } item.matched_at = new Date(); var matches = {}; matches[item.service] = item; @@ -30,7 +39,7 @@ module.exports = function(req, res, next) { res.json(item); }); }, function(error) { - console.log(error.stack) - res.json({error: "No matches found for url"}); + console.log(error.stack); + res.json({error: {message: "No matches found for this link, sorry :("}}); }); }; diff --git a/views/error.jsx b/views/error.jsx index 7fef054..4e9fa12 100644 --- a/views/error.jsx +++ b/views/error.jsx @@ -9,7 +9,7 @@ module.exports = React.createClass({ render: function() { return ( -
Most music services have a "share" dialog for albums and tracks in their interface. If you have them open in a web browser instead of an app, you can simply copy and paste the address bar and we'll work out the rest.
+Let me stop you there. Match Audio is open source, that means any capable programmer who wants to add other music services can look at our code and submit changes. If you're not a programmer, you can always submit a request and maybe we'll do it for you.
++